/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.car.internal.property; import android.car.hardware.CarPropertyValue; import android.car.hardware.CarPropertyValue.PropertyStatus; import com.android.internal.util.Preconditions; import java.time.Duration; import java.util.Objects; /** * A {@link CarPropertyEventTracker} implementation for continuous property * * @hide */ public final class ContCarPropertyEventTracker implements CarPropertyEventTracker{ private static final String TAG = "ContCarPropertyEventTracker"; private static final float NANOSECONDS_PER_SECOND = Duration.ofSeconds(1).toNanos(); // Add a margin so that if an event timestamp is within // 5% of the next timestamp, it will not be dropped. private static final float UPDATE_PERIOD_OFFSET = 0.95f; private final Logger mLogger; private final boolean mEnableVur; private final float mUpdateRateHz; private final float mResolution; private final long mUpdatePeriodNanos; private long mNextUpdateTimeNanos; private CarPropertyValue<?> mCurrentCarPropertyValue; private @PropertyStatus int mCurrentStatus; public ContCarPropertyEventTracker(boolean useSystemLogger, float updateRateHz, boolean enableVur, float resolution) { mLogger = new Logger(useSystemLogger, TAG); if (mLogger.dbg()) { mLogger.logD(String.format( "new continuous car property event tracker, updateRateHz: %f, " + ", enableVur: %b, resolution: %f", updateRateHz, enableVur, resolution)); } // updateRateHz should be sanitized before. Preconditions.checkArgument(updateRateHz > 0, "updateRateHz must be a positive number"); mUpdateRateHz = updateRateHz; mUpdatePeriodNanos = (long) ((1.0 / mUpdateRateHz) * NANOSECONDS_PER_SECOND * UPDATE_PERIOD_OFFSET); mEnableVur = enableVur; mResolution = resolution; } @Override public float getUpdateRateHz() { return mUpdateRateHz; } @Override public CarPropertyValue<?> getCurrentCarPropertyValue() { return mCurrentCarPropertyValue; } private int sanitizeValueByResolutionInt(Object value) { return (int) (Math.round((Integer) value / mResolution) * mResolution); } private long sanitizeValueByResolutionLong(Object value) { return (long) (Math.round((Long) value / mResolution) * mResolution); } private float sanitizeValueByResolutionFloat(Object value) { return Math.round((Float) value / mResolution) * mResolution; } private CarPropertyValue<?> sanitizeCarPropertyValueByResolution( CarPropertyValue<?> carPropertyValue) { if (mResolution == 0.0f) { return carPropertyValue; } Object value = carPropertyValue.getValue(); if (value instanceof Integer) { value = sanitizeValueByResolutionInt(value); } else if (value instanceof Integer[]) { Integer[] array = (Integer[]) value; for (int i = 0; i < array.length; i++) { array[i] = sanitizeValueByResolutionInt(array[i]); } value = array; } else if (value instanceof Long) { value = sanitizeValueByResolutionLong(value); } else if (value instanceof Long[]) { Long[] array = (Long[]) value; for (int i = 0; i < array.length; i++) { array[i] = sanitizeValueByResolutionLong(array[i]); } value = array; } else if (value instanceof Float) { value = sanitizeValueByResolutionFloat(value); } else if (value instanceof Float[]) { Float[] array = (Float[]) value; for (int i = 0; i < array.length; i++) { array[i] = sanitizeValueByResolutionFloat(array[i]); } value = array; } return new CarPropertyValue<>(carPropertyValue.getPropertyId(), carPropertyValue.getAreaId(), carPropertyValue.getStatus(), carPropertyValue.getTimestamp(), value); } /** Returns true if the client needs to be updated for this event. */ @Override public boolean hasUpdate(CarPropertyValue<?> carPropertyValue) { if (carPropertyValue.getTimestamp() < mNextUpdateTimeNanos) { if (mLogger.dbg()) { mLogger.logD(String.format("hasUpdate: Dropping carPropertyValue: %s, " + "because getTimestamp()=%d < nextUpdateTimeNanos=%d", carPropertyValue, carPropertyValue.getTimestamp(), mNextUpdateTimeNanos)); } return false; } mNextUpdateTimeNanos = carPropertyValue.getTimestamp() + mUpdatePeriodNanos; CarPropertyValue<?> sanitizedCarPropertyValue = sanitizeCarPropertyValueByResolution(carPropertyValue); int status = sanitizedCarPropertyValue.getStatus(); Object value = sanitizedCarPropertyValue.getValue(); if (mEnableVur && status == mCurrentStatus && mCurrentCarPropertyValue != null && Objects.deepEquals(value, mCurrentCarPropertyValue.getValue())) { if (mLogger.dbg()) { mLogger.logD(String.format("hasUpdate: Dropping carPropertyValue: %s, " + "because VUR is enabled and value is the same", sanitizedCarPropertyValue)); } return false; } mCurrentStatus = status; mCurrentCarPropertyValue = sanitizedCarPropertyValue; return true; } }