• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.car.testapi;
18 
19 import static android.car.hardware.property.CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE;
20 
21 import static java.lang.Integer.toHexString;
22 
23 import android.annotation.Nullable;
24 import android.car.Car;
25 import android.car.VehicleAreaType;
26 import android.car.VehiclePropertyType;
27 import android.car.hardware.CarPropertyConfig;
28 import android.car.hardware.CarPropertyValue;
29 import android.car.hardware.property.CarPropertyEvent;
30 import android.car.hardware.property.ICarProperty;
31 import android.car.hardware.property.ICarPropertyEventListener;
32 import android.os.RemoteException;
33 import android.util.ArraySet;
34 
35 import com.android.car.internal.property.AsyncPropertyServiceRequest;
36 import com.android.car.internal.property.AsyncPropertyServiceRequestList;
37 import com.android.car.internal.property.CarPropertyConfigList;
38 import com.android.car.internal.property.CarSubscription;
39 import com.android.car.internal.property.GetPropertyConfigListResult;
40 import com.android.car.internal.property.GetSetValueResult;
41 import com.android.car.internal.property.GetSetValueResultList;
42 import com.android.car.internal.property.IAsyncPropertyResultCallback;
43 import com.android.car.internal.property.ISupportedValuesChangeCallback;
44 import com.android.car.internal.property.MinMaxSupportedPropertyValue;
45 import com.android.car.internal.property.PropIdAreaId;
46 import com.android.car.internal.property.RawPropertyValue;
47 import com.android.car.internal.util.PairSparseArray;
48 import com.android.internal.annotations.GuardedBy;
49 
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Objects;
55 import java.util.Set;
56 
57 /**
58  * This is fake implementation of the service which is used in
59  * {@link android.car.hardware.property.CarPropertyManager}.
60  *
61  * @hide
62  */
63 class FakeCarPropertyService extends ICarProperty.Stub implements CarPropertyController {
64     private final Map<Integer, CarPropertyConfig> mConfigs = new HashMap<>();
65     private final Map<PropKey, CarPropertyValue> mValues = new HashMap<>();
66 
67     // Contains a list of values that were set from the manager.
68     private final ArrayList<CarPropertyValue<?>> mValuesSet = new ArrayList<>();
69 
70     private final Object mLock = new Object();
71 
72     // Mapping between [propId, areaId] and a set of listeners.
73     @GuardedBy("mLock")
74     private final PairSparseArray<Set<ListenerInfo>> mListenersByPropIdAreaId =
75             new PairSparseArray<>();
76 
77     @Override
registerListener(List<CarSubscription> subscriptions, ICarPropertyEventListener listener)78     public void registerListener(List<CarSubscription> subscriptions,
79             ICarPropertyEventListener listener) {
80         synchronized (mLock) {
81             for (int i = 0; i < subscriptions.size(); i++) {
82                 int propId = subscriptions.get(i).propertyId;
83                 for (int areaId : subscriptions.get(i).areaIds) {
84                     Set<ListenerInfo> propListeners = mListenersByPropIdAreaId.get(propId, areaId);
85                     if (propListeners == null) {
86                         propListeners = new ArraySet<>();
87                         mListenersByPropIdAreaId.put(propId, areaId, propListeners);
88                     }
89 
90                     propListeners.add(new ListenerInfo(listener));
91                 }
92             }
93         }
94     }
95 
96     @Override
unregisterListener(int propId, ICarPropertyEventListener listener)97     public void unregisterListener(int propId, ICarPropertyEventListener listener)
98             throws RemoteException {
99         synchronized (mLock) {
100             for (int areaId : mListenersByPropIdAreaId.getSecondKeysForFirstKey(propId)) {
101                 Set<ListenerInfo> propListeners = mListenersByPropIdAreaId.get(propId, areaId);
102                 if (propListeners.remove(new ListenerInfo(listener)) && propListeners.isEmpty()) {
103                     mListenersByPropIdAreaId.remove(propId, areaId);
104                 }
105             }
106         }
107     }
108 
109     @Override
getPropertyList()110     public CarPropertyConfigList getPropertyList() throws RemoteException {
111         return new CarPropertyConfigList(new ArrayList<>(mConfigs.values()));
112     }
113 
114     @Override
getPropertyConfigList(int[] propIds)115     public GetPropertyConfigListResult getPropertyConfigList(int[] propIds) {
116         List<CarPropertyConfig> configs = new ArrayList<>(propIds.length);
117         for (int prop : propIds) {
118             CarPropertyConfig cfg = mConfigs.get(prop);
119             if (cfg != null) {
120                 configs.add(cfg);
121             }
122         }
123         GetPropertyConfigListResult result = new GetPropertyConfigListResult();
124         result.unsupportedPropIds = new int[0];
125         result.missingPermissionPropIds = new int[0];
126         result.carPropertyConfigList = new CarPropertyConfigList(configs);
127         return result;
128     }
129 
130     @Override
getPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)131     public void getPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
132             IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)
133             throws RemoteException {
134         List<AsyncPropertyServiceRequest> asyncPropertyServiceRequestList =
135                 asyncPropertyServiceRequests.getList();
136         List<GetSetValueResult> getValueResults = new ArrayList<>();
137         for (int i = 0; i < asyncPropertyServiceRequestList.size(); i++) {
138             AsyncPropertyServiceRequest asyncPropertyServiceRequest =
139                     asyncPropertyServiceRequestList.get(i);
140             getValueResults.add(GetSetValueResult.newGetValueResult(
141                     asyncPropertyServiceRequest.getRequestId(),
142                     getProperty(asyncPropertyServiceRequest.getPropertyId(),
143                             asyncPropertyServiceRequest.getAreaId())));
144         }
145         asyncPropertyResultCallback.onGetValueResults(new GetSetValueResultList(getValueResults));
146     }
147 
148     @Override
setPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)149     public void setPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
150             IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)
151             throws RemoteException {
152         List<AsyncPropertyServiceRequest> asyncPropertyServiceRequestList =
153                 asyncPropertyServiceRequests.getList();
154         List<GetSetValueResult> setValueResults = new ArrayList<>();
155         for (int i = 0; i < asyncPropertyServiceRequestList.size(); i++) {
156             AsyncPropertyServiceRequest asyncPropertyServiceRequest =
157                     asyncPropertyServiceRequestList.get(i);
158             setProperty(asyncPropertyServiceRequest.getCarPropertyValue(), /* listener= */ null);
159             setValueResults.add(GetSetValueResult.newSetValueResult(
160                     asyncPropertyServiceRequest.getRequestId(), /* updateTimestampNanos= */ 0));
161         }
162         asyncPropertyResultCallback.onSetValueResults(new GetSetValueResultList(setValueResults));
163     }
164 
165     @Override
getProperty(int prop, int areaId)166     public CarPropertyValue getProperty(int prop, int areaId) throws RemoteException {
167         return mValues.get(PropKey.of(prop, areaId));
168     }
169 
170     @Override
getAndDispatchInitialValue(List<PropIdAreaId> propIdAreaIds, ICarPropertyEventListener carPropertyEventListener)171     public void getAndDispatchInitialValue(List<PropIdAreaId> propIdAreaIds,
172             ICarPropertyEventListener carPropertyEventListener) throws RemoteException {
173         List<CarPropertyEvent> events = new ArrayList<>();
174         for (var propIdAreaId : propIdAreaIds) {
175             CarPropertyEvent event = new CarPropertyEvent(
176                     CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
177                     getProperty(propIdAreaId.propId, propIdAreaId.areaId));
178             events.add(event);
179         }
180 
181         carPropertyEventListener.onEvent(events);
182     }
183 
184     @Override
setProperty(CarPropertyValue prop, ICarPropertyEventListener listener)185     public void setProperty(CarPropertyValue prop, ICarPropertyEventListener listener)
186             throws RemoteException {
187         mValues.put(PropKey.of(prop), prop);
188         mValuesSet.add(prop);
189         sendEvent(prop);
190     }
191 
192     @Override
cancelRequests(int[] serviceRequestIds)193     public void cancelRequests(int[] serviceRequestIds) {
194         // Do nothing.
195     }
196 
197     @Override
registerRecordingListener(ICarPropertyEventListener callback)198     public CarPropertyConfigList registerRecordingListener(ICarPropertyEventListener callback) {
199         return new CarPropertyConfigList(new ArrayList<>());
200     }
201 
202     @Override
isRecordingVehicleProperties()203     public boolean isRecordingVehicleProperties() {
204         return false;
205     }
206 
207     @Override
stopRecordingVehicleProperties(ICarPropertyEventListener callback)208     public void stopRecordingVehicleProperties(ICarPropertyEventListener callback) {
209         // no-op
210     }
211 
212     @Override
enableInjectionMode(int[] propertyIdsFromRealHardware)213     public long enableInjectionMode(int[] propertyIdsFromRealHardware) {
214         return -1;
215     }
216 
217     @Override
disableInjectionMode()218     public void disableInjectionMode() {
219         // no-op
220     }
221 
222     @Override
isVehiclePropertyInjectionModeEnabled()223     public boolean isVehiclePropertyInjectionModeEnabled() {
224         return false;
225     }
226 
227     @Override
getLastInjectedVehicleProperty(int propertyId)228     public CarPropertyValue getLastInjectedVehicleProperty(int propertyId) {
229         return null;
230     }
231 
232     @Override
injectVehicleProperties(List<CarPropertyValue> carPropertyValues)233     public void injectVehicleProperties(List<CarPropertyValue> carPropertyValues) {
234         // no-op
235     }
236 
237     @Override
getReadPermission(int propId)238     public String getReadPermission(int propId) throws RemoteException {
239         // Return an arbitrary permission if the propId is supported.
240         return mConfigs.containsKey(propId) ? Car.PERMISSION_SPEED : null;
241     }
242 
243     @Override
getWritePermission(int propId)244     public String getWritePermission(int propId) throws RemoteException {
245         // Return an arbitrary permission if the propId is supported.
246         return mConfigs.containsKey(propId) ? Car.PERMISSION_SPEED : null;
247     }
248 
249     @Override
getSupportedNoReadPermPropIds(int[] propertyids)250     public int[] getSupportedNoReadPermPropIds(int[] propertyids) {
251         return new int[0];
252     }
253 
254     @Override
isSupportedAndHasWritePermissionOnly(int propertyId)255     public boolean isSupportedAndHasWritePermissionOnly(int propertyId) {
256         return false;
257     }
258 
259     @Override
addProperty(Integer propId, Object value)260     public CarPropertyController addProperty(Integer propId, Object value) {
261         int areaType = getVehicleAreaType(propId);
262         Class<?> type = getPropertyType(propId);
263         CarPropertyConfig.Builder<?> builder = CarPropertyConfig
264                 .newBuilder(type, propId, areaType);
265         mConfigs.put(propId, builder.build());
266         if (value != null) {
267             updateValues(false, new CarPropertyValue<>(propId, 0, value));
268         }
269 
270         return this;
271     }
272 
273     @Override
addProperty(CarPropertyConfig<?> config, @Nullable CarPropertyValue<?> value)274     public CarPropertyController addProperty(CarPropertyConfig<?> config,
275             @Nullable CarPropertyValue<?> value) {
276         mConfigs.put(config.getPropertyId(), config);
277         if (value != null) {
278             updateValues(false, value);
279         }
280         return this;
281     }
282 
283     @Override
updateValues(boolean triggerListeners, CarPropertyValue<?>... propValues)284     public void updateValues(boolean triggerListeners, CarPropertyValue<?>... propValues) {
285         for (CarPropertyValue v : propValues) {
286             mValues.put(PropKey.of(v), v);
287             if (triggerListeners) {
288                 sendEvent(v);
289             }
290         }
291     }
292 
293     @Override
getMinMaxSupportedValue(int propertyId, int areaId)294     public MinMaxSupportedPropertyValue getMinMaxSupportedValue(int propertyId, int areaId) {
295         // This is currently unused, so just return a fake result here that doesn't support
296         // min or max value.
297         return new MinMaxSupportedPropertyValue();
298     }
299 
300     @Override
301     @Nullable
getSupportedValuesList(int propertyId, int areaId)302     public List<RawPropertyValue> getSupportedValuesList(int propertyId, int areaId) {
303         // This is currently unused, so just return null indicating the hardware does not specify
304         // supported values list.
305         return null;
306     }
307 
308     @Override
registerSupportedValuesChangeCallback(List<PropIdAreaId> propIdAreaIds, ISupportedValuesChangeCallback callback)309     public void registerSupportedValuesChangeCallback(List<PropIdAreaId> propIdAreaIds,
310             ISupportedValuesChangeCallback callback) {
311         // This is currently unused, do nothing.
312     }
313 
314     @Override
unregisterSupportedValuesChangeCallback(List<PropIdAreaId> propIdAreaIds, ISupportedValuesChangeCallback callback)315     public void unregisterSupportedValuesChangeCallback(List<PropIdAreaId> propIdAreaIds,
316             ISupportedValuesChangeCallback callback) {
317         // This is currently unused, do nothing.
318     }
319 
sendEvent(CarPropertyValue v)320     private void sendEvent(CarPropertyValue v) {
321         synchronized (mLock) {
322             Set<ListenerInfo> listeners = mListenersByPropIdAreaId.get(v.getPropertyId(),
323                     v.getAreaId());
324             if (listeners != null) {
325                 for (ListenerInfo listenerInfo : listeners) {
326                     List<CarPropertyEvent> events = new ArrayList<>();
327                     events.add(new CarPropertyEvent(PROPERTY_EVENT_PROPERTY_CHANGE, v));
328                     try {
329                         listenerInfo.mListener.onEvent(events);
330                     } catch (RemoteException e) {
331                         // This is impossible as the code runs within the same process in test.
332                         throw new RuntimeException(e);
333                     }
334                 }
335             }
336         }
337     }
338 
339     @Override
getSetValues()340     public List<CarPropertyValue<?>> getSetValues() {
341         // Explicitly return the instance of this object rather than copying it such that test code
342         // will have a chance to clear this list if needed.
343         return mValuesSet;
344     }
345 
346     /** Consists of property id and area */
347     private static class PropKey {
348         final int mPropId;
349         final int mAreaId;
350 
PropKey(int propId, int areaId)351         private PropKey(int propId, int areaId) {
352             this.mPropId = propId;
353             this.mAreaId = areaId;
354         }
355 
of(int propId, int areaId)356         static PropKey of(int propId, int areaId) {
357             return new PropKey(propId, areaId);
358         }
359 
of(CarPropertyValue carPropertyValue)360         static PropKey of(CarPropertyValue carPropertyValue) {
361             return of(carPropertyValue.getPropertyId(), carPropertyValue.getAreaId());
362         }
363 
364         @Override
365 
equals(Object o)366         public boolean equals(Object o) {
367             if (this == o) {
368                 return true;
369             }
370             if (!(o instanceof PropKey)) {
371                 return false;
372             }
373             PropKey propKey = (PropKey) o;
374             return mPropId == propKey.mPropId && mAreaId == propKey.mAreaId;
375         }
376 
377         @Override
hashCode()378         public int hashCode() {
379             return Objects.hash(mPropId, mAreaId);
380         }
381     }
382 
383     private static class ListenerInfo {
384         private final ICarPropertyEventListener mListener;
385 
ListenerInfo(ICarPropertyEventListener listener)386         ListenerInfo(ICarPropertyEventListener listener) {
387             this.mListener = listener;
388         }
389 
390         @Override
equals(Object o)391         public boolean equals(Object o) {
392             if (this == o) {
393                 return true;
394             }
395             if (!(o instanceof ListenerInfo)) {
396                 return false;
397             }
398             ListenerInfo that = (ListenerInfo) o;
399             return Objects.equals(mListener, that.mListener);
400         }
401 
402         @Override
hashCode()403         public int hashCode() {
404             return Objects.hash(mListener);
405         }
406     }
407 
getPropertyType(int propId)408     private static Class<?> getPropertyType(int propId) {
409         int type = propId & VehiclePropertyType.MASK;
410         switch (type) {
411             case VehiclePropertyType.BOOLEAN:
412                 return Boolean.class;
413             case VehiclePropertyType.FLOAT:
414                 return Float.class;
415             case VehiclePropertyType.INT32:
416                 return Integer.class;
417             case VehiclePropertyType.INT64:
418                 return Long.class;
419             case VehiclePropertyType.FLOAT_VEC:
420                 return Float[].class;
421             case VehiclePropertyType.INT32_VEC:
422                 return Integer[].class;
423             case VehiclePropertyType.INT64_VEC:
424                 return Long[].class;
425             case VehiclePropertyType.STRING:
426                 return String.class;
427             case VehiclePropertyType.BYTES:
428                 return byte[].class;
429             case VehiclePropertyType.MIXED:
430                 return Object.class;
431             default:
432                 throw new IllegalArgumentException("Unexpected type: " + toHexString(type));
433         }
434     }
435 
getVehicleAreaType(int propId)436     private static int getVehicleAreaType(int propId) {
437         int halArea = propId & VehicleArea.MASK;
438         switch (halArea) {
439             case VehicleArea.GLOBAL:
440                 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
441             case VehicleArea.SEAT:
442                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
443             case VehicleArea.DOOR:
444                 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR;
445             case VehicleArea.WINDOW:
446                 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW;
447             case VehicleArea.MIRROR:
448                 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR;
449             case VehicleArea.WHEEL:
450                 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL;
451             case VehicleArea.VENDOR:
452                 return VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR;
453             default:
454                 throw new RuntimeException("Unsupported area type " + halArea);
455         }
456     }
457 
458     /** Copy from VHAL generated file VehicleArea.java */
459     private static final class VehicleArea {
460         static final int GLOBAL = 0x01000000;
461         static final int WINDOW = 0x03000000;
462         static final int MIRROR = 0x04000000;
463         static final int SEAT = 0x05000000;
464         static final int DOOR = 0x06000000;
465         static final int WHEEL = 0x07000000;
466         static final int VENDOR = 0x08000000;
467         static final int MASK = 0x0f000000;
468     }
469 }
470