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 static java.util.Objects.requireNonNull; 20 21 import android.car.VehiclePropertyIds; 22 import android.car.hardware.CarPropertyValue; 23 import android.car.hardware.property.CarPropertyEvent; 24 import android.util.ArraySet; 25 26 import com.android.car.internal.util.PairSparseArray; 27 import com.android.internal.annotations.GuardedBy; 28 import com.android.internal.annotations.VisibleForTesting; 29 30 /** 31 * Manages a group of property IDs and area IDs registered for the same client at possibly 32 * different update rates. The client here might be different callbacks at 33 * {@code CarPropertyManager} or might be different managers at {@code CarPropertyService}. 34 * 35 * This class is used to decide whether a new property update event needs to be filtered out and 36 * not passed to the client. 37 * 38 * This class is thread-safe. 39 * 40 * @hide 41 */ 42 public class CarPropertyEventController { 43 // Abbreviating TAG because class name is longer than the 23 character Log tag limit. 44 private static final String TAG = "CPEController"; 45 private final Logger mLogger; 46 private final boolean mUseSystemLogger; 47 private final Object mLock = new Object(); 48 // For each property ID and area ID, track the property event information. 49 @GuardedBy("mLock") 50 private final PairSparseArray<CarPropertyEventTracker> mPropIdToAreaIdToCpeTracker = 51 new PairSparseArray<>(); 52 CarPropertyEventController(boolean useSystemLogger)53 public CarPropertyEventController(boolean useSystemLogger) { 54 mUseSystemLogger = useSystemLogger; 55 mLogger = new Logger(useSystemLogger, TAG); 56 } 57 58 /** Gets the update rate in Hz for the property ID, area ID. */ 59 @VisibleForTesting getUpdateRateHz(int propertyId, int areaId)60 public float getUpdateRateHz(int propertyId, int areaId) { 61 synchronized (mLock) { 62 CarPropertyEventTracker tracker = mPropIdToAreaIdToCpeTracker.get(propertyId, areaId); 63 if (tracker == null) { 64 return 0f; 65 } 66 return tracker.getUpdateRateHz(); 67 } 68 } 69 70 /** Tracks the continuous property ID and area IDs at the given update rate. */ addContinuousProperty(int propertyId, int[] areaIds, float updateRateHz, boolean enableVur, float resolution)71 public void addContinuousProperty(int propertyId, int[] areaIds, float updateRateHz, 72 boolean enableVur, float resolution) { 73 requireNonNull(areaIds); 74 synchronized (mLock) { 75 for (int areaId : areaIds) { 76 if (mLogger.dbg()) { 77 mLogger.logD(String.format( 78 "Add new continuous property event tracker, property: %s, " 79 + "areaId: %d, updateRate: %f Hz, enableVur: %b, resolution: %f", 80 VehiclePropertyIds.toString(propertyId), areaId, updateRateHz, 81 enableVur, resolution)); 82 } 83 mPropIdToAreaIdToCpeTracker.put(propertyId, areaId, 84 new ContCarPropertyEventTracker(mUseSystemLogger, updateRateHz, enableVur, 85 resolution)); 86 } 87 } 88 } 89 90 /** Tracks a newly subscribed on-change property ID and area IDs. */ addOnChangeProperty(int propertyId, int[] areaIds)91 public void addOnChangeProperty(int propertyId, int[] areaIds) { 92 requireNonNull(areaIds); 93 synchronized (mLock) { 94 for (int areaId : areaIds) { 95 if (mLogger.dbg()) { 96 mLogger.logD(String.format( 97 "Add new on-change property event tracker, property: %s, " 98 + "areaId: %d", VehiclePropertyIds.toString(propertyId), areaId)); 99 } 100 mPropIdToAreaIdToCpeTracker.put(propertyId, areaId, 101 new OnChangeCarPropertyEventTracker(mUseSystemLogger)); 102 } 103 } 104 } 105 106 /** 107 * Returns the areaIds associated with the given propertyId 108 */ getAreaIds(int propertyId)109 public int[] getAreaIds(int propertyId) { 110 synchronized (mLock) { 111 return setToArray(mPropIdToAreaIdToCpeTracker.getSecondKeysForFirstKey(propertyId)); 112 } 113 } 114 115 /** 116 * Stop tracking the given property ID. 117 * 118 * @return {@code true} if there are no remaining properties being tracked. 119 */ remove(int propertyId)120 public boolean remove(int propertyId) { 121 synchronized (mLock) { 122 for (int areaId : mPropIdToAreaIdToCpeTracker.getSecondKeysForFirstKey(propertyId)) { 123 mPropIdToAreaIdToCpeTracker.delete(propertyId, areaId); 124 } 125 return mPropIdToAreaIdToCpeTracker.size() == 0; 126 } 127 } 128 129 /** 130 * Stop tracking the given property IDs. 131 * 132 * @return {@code true} if there are no remaining properties being tracked. 133 */ remove(ArraySet<Integer> propertyIds)134 public boolean remove(ArraySet<Integer> propertyIds) { 135 synchronized (mLock) { 136 for (int i = 0; i < propertyIds.size(); i++) { 137 int propertyId = propertyIds.valueAt(i); 138 for (int areaId : 139 mPropIdToAreaIdToCpeTracker.getSecondKeysForFirstKey(propertyId)) { 140 mPropIdToAreaIdToCpeTracker.delete(propertyId, areaId); 141 } 142 } 143 return mPropIdToAreaIdToCpeTracker.size() == 0; 144 } 145 } 146 147 /** Returns a list of property IDs being tracked for the client. */ getSubscribedProperties()148 public int[] getSubscribedProperties() { 149 synchronized (mLock) { 150 return setToArray(mPropIdToAreaIdToCpeTracker.getFirstKeys()); 151 } 152 } 153 154 /** 155 * Returns a new sanitized CarPropertyValue if the client callback should be invoked for the 156 * event. 157 */ getCarPropertyValueIfCallbackRequired( CarPropertyEvent carPropertyEvent)158 protected CarPropertyValue<?> getCarPropertyValueIfCallbackRequired( 159 CarPropertyEvent carPropertyEvent) { 160 CarPropertyValue<?> carPropertyValue = carPropertyEvent.getCarPropertyValue(); 161 int propertyId = carPropertyValue.getPropertyId(); 162 int areaId = carPropertyValue.getAreaId(); 163 164 synchronized (mLock) { 165 CarPropertyEventTracker tracker = mPropIdToAreaIdToCpeTracker.get(propertyId, areaId); 166 if (tracker == null) { 167 mLogger.logW( 168 "getCarPropertyValueIfCallbackRequired: callback not registered for event: " 169 + carPropertyEvent); 170 return null; 171 } 172 if (carPropertyEvent.getEventType() == CarPropertyEvent.PROPERTY_EVENT_ERROR) { 173 return carPropertyValue; 174 } 175 if (tracker.hasUpdate(carPropertyValue)) { 176 return tracker.getCurrentCarPropertyValue(); 177 } 178 return null; 179 } 180 } 181 setToArray(ArraySet<Integer> ids)182 private static int[] setToArray(ArraySet<Integer> ids) { 183 int[] propertyIds = new int[ids.size()]; 184 for (int i = 0; i < ids.size(); i++) { 185 propertyIds[i] = ids.valueAt(i); 186 } 187 return propertyIds; 188 } 189 } 190