1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.internal.property; 18 19 import android.car.hardware.CarPropertyValue; 20 import android.car.hardware.CarPropertyValue.PropertyStatus; 21 22 import com.android.internal.util.Preconditions; 23 24 import java.time.Duration; 25 import java.util.Objects; 26 27 /** 28 * A {@link CarPropertyEventTracker} implementation for continuous property 29 * 30 * @hide 31 */ 32 public final class ContCarPropertyEventTracker implements CarPropertyEventTracker{ 33 private static final String TAG = "ContCarPropertyEventTracker"; 34 private static final float NANOSECONDS_PER_SECOND = Duration.ofSeconds(1).toNanos(); 35 // Add a margin so that if an event timestamp is within 36 // 5% of the next timestamp, it will not be dropped. 37 private static final float UPDATE_PERIOD_OFFSET = 0.95f; 38 39 private final Logger mLogger; 40 private final boolean mEnableVur; 41 private final float mUpdateRateHz; 42 private final float mResolution; 43 private final long mUpdatePeriodNanos; 44 private long mNextUpdateTimeNanos; 45 private CarPropertyValue<?> mCurrentCarPropertyValue; 46 private @PropertyStatus int mCurrentStatus; 47 ContCarPropertyEventTracker(boolean useSystemLogger, float updateRateHz, boolean enableVur, float resolution)48 public ContCarPropertyEventTracker(boolean useSystemLogger, float updateRateHz, 49 boolean enableVur, float resolution) { 50 mLogger = new Logger(useSystemLogger, TAG); 51 if (mLogger.dbg()) { 52 mLogger.logD(String.format( 53 "new continuous car property event tracker, updateRateHz: %f, " 54 + ", enableVur: %b, resolution: %f", updateRateHz, enableVur, resolution)); 55 } 56 // updateRateHz should be sanitized before. 57 Preconditions.checkArgument(updateRateHz > 0, "updateRateHz must be a positive number"); 58 mUpdateRateHz = updateRateHz; 59 mUpdatePeriodNanos = 60 (long) ((1.0 / mUpdateRateHz) * NANOSECONDS_PER_SECOND * UPDATE_PERIOD_OFFSET); 61 mEnableVur = enableVur; 62 mResolution = resolution; 63 } 64 65 @Override getUpdateRateHz()66 public float getUpdateRateHz() { 67 return mUpdateRateHz; 68 } 69 70 @Override getCurrentCarPropertyValue()71 public CarPropertyValue<?> getCurrentCarPropertyValue() { 72 return mCurrentCarPropertyValue; 73 } 74 sanitizeValueByResolutionInt(Object value)75 private int sanitizeValueByResolutionInt(Object value) { 76 return (int) (Math.round((Integer) value / mResolution) * mResolution); 77 } 78 sanitizeValueByResolutionLong(Object value)79 private long sanitizeValueByResolutionLong(Object value) { 80 return (long) (Math.round((Long) value / mResolution) * mResolution); 81 } 82 sanitizeValueByResolutionFloat(Object value)83 private float sanitizeValueByResolutionFloat(Object value) { 84 return Math.round((Float) value / mResolution) * mResolution; 85 } 86 sanitizeCarPropertyValueByResolution( CarPropertyValue<?> carPropertyValue)87 private CarPropertyValue<?> sanitizeCarPropertyValueByResolution( 88 CarPropertyValue<?> carPropertyValue) { 89 if (mResolution == 0.0f) { 90 return carPropertyValue; 91 } 92 93 Object value = carPropertyValue.getValue(); 94 if (value instanceof Integer) { 95 value = sanitizeValueByResolutionInt(value); 96 } else if (value instanceof Integer[]) { 97 Integer[] array = (Integer[]) value; 98 for (int i = 0; i < array.length; i++) { 99 array[i] = sanitizeValueByResolutionInt(array[i]); 100 } 101 value = array; 102 } else if (value instanceof Long) { 103 value = sanitizeValueByResolutionLong(value); 104 } else if (value instanceof Long[]) { 105 Long[] array = (Long[]) value; 106 for (int i = 0; i < array.length; i++) { 107 array[i] = sanitizeValueByResolutionLong(array[i]); 108 } 109 value = array; 110 } else if (value instanceof Float) { 111 value = sanitizeValueByResolutionFloat(value); 112 } else if (value instanceof Float[]) { 113 Float[] array = (Float[]) value; 114 for (int i = 0; i < array.length; i++) { 115 array[i] = sanitizeValueByResolutionFloat(array[i]); 116 } 117 value = array; 118 } 119 return new CarPropertyValue<>(carPropertyValue.getPropertyId(), 120 carPropertyValue.getAreaId(), 121 carPropertyValue.getStatus(), 122 carPropertyValue.getTimestamp(), 123 value); 124 } 125 126 /** Returns true if the client needs to be updated for this event. */ 127 @Override hasUpdate(CarPropertyValue<?> carPropertyValue)128 public boolean hasUpdate(CarPropertyValue<?> carPropertyValue) { 129 if (carPropertyValue.getTimestamp() < mNextUpdateTimeNanos) { 130 if (mLogger.dbg()) { 131 mLogger.logD(String.format("hasUpdate: Dropping carPropertyValue: %s, " 132 + "because getTimestamp()=%d < nextUpdateTimeNanos=%d", 133 carPropertyValue, carPropertyValue.getTimestamp(), mNextUpdateTimeNanos)); 134 } 135 return false; 136 } 137 mNextUpdateTimeNanos = carPropertyValue.getTimestamp() + mUpdatePeriodNanos; 138 CarPropertyValue<?> sanitizedCarPropertyValue = 139 sanitizeCarPropertyValueByResolution(carPropertyValue); 140 int status = sanitizedCarPropertyValue.getStatus(); 141 Object value = sanitizedCarPropertyValue.getValue(); 142 if (mEnableVur && status == mCurrentStatus && mCurrentCarPropertyValue != null 143 && Objects.deepEquals(value, mCurrentCarPropertyValue.getValue())) { 144 if (mLogger.dbg()) { 145 mLogger.logD(String.format("hasUpdate: Dropping carPropertyValue: %s, " 146 + "because VUR is enabled and value is the same", 147 sanitizedCarPropertyValue)); 148 } 149 return false; 150 } 151 mCurrentStatus = status; 152 mCurrentCarPropertyValue = sanitizedCarPropertyValue; 153 return true; 154 } 155 } 156