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