• 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.VehicleAreaType;
25 import android.car.VehiclePropertyType;
26 import android.car.hardware.CarPropertyConfig;
27 import android.car.hardware.CarPropertyValue;
28 import android.car.hardware.property.CarPropertyEvent;
29 import android.car.hardware.property.ICarProperty;
30 import android.car.hardware.property.ICarPropertyEventListener;
31 import android.os.RemoteException;
32 
33 import com.android.car.internal.PropertyPermissionMapping;
34 import com.android.car.internal.property.AsyncPropertyServiceRequest;
35 import com.android.car.internal.property.AsyncPropertyServiceRequestList;
36 import com.android.car.internal.property.CarPropertyConfigList;
37 import com.android.car.internal.property.GetSetValueResult;
38 import com.android.car.internal.property.GetSetValueResultList;
39 import com.android.car.internal.property.IAsyncPropertyResultCallback;
40 
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Objects;
47 import java.util.Set;
48 
49 /**
50  * This is fake implementation of the service which is used in
51  * {@link android.car.hardware.property.CarPropertyManager}.
52  *
53  * @hide
54  */
55 class FakeCarPropertyService extends ICarProperty.Stub implements CarPropertyController {
56     private final Map<Integer, CarPropertyConfig> mConfigs = new HashMap<>();
57     private final Map<PropKey, CarPropertyValue> mValues = new HashMap<>();
58 
59     private final PropertyPermissionMapping mPermissions = new PropertyPermissionMapping();
60 
61     // Contains a list of values that were set from the manager.
62     private final ArrayList<CarPropertyValue<?>> mValuesSet = new ArrayList<>();
63 
64     // Mapping between propertyId and a set of listeners.
65     private final Map<Integer, Set<ListenerInfo>> mListeners = new HashMap<>();
66 
67     @Override
registerListener(int propId, float rate, ICarPropertyEventListener listener)68     public void registerListener(int propId, float rate, ICarPropertyEventListener listener)
69             throws RemoteException {
70         Set<ListenerInfo> propListeners = mListeners.get(propId);
71         if (propListeners == null) {
72             propListeners = new HashSet<>();
73             mListeners.put(propId, propListeners);
74         }
75 
76         propListeners.add(new ListenerInfo(listener));
77     }
78 
79     @Override
unregisterListener(int propId, ICarPropertyEventListener listener)80     public void unregisterListener(int propId, ICarPropertyEventListener listener)
81             throws RemoteException {
82         Set<ListenerInfo> propListeners = mListeners.get(propId);
83         if (propListeners != null && propListeners.remove(new ListenerInfo(listener))) {
84             if (propListeners.isEmpty()) {
85                 mListeners.remove(propId);
86             }
87         }
88     }
89 
90     @Override
getPropertyList()91     public CarPropertyConfigList getPropertyList() throws RemoteException {
92         return new CarPropertyConfigList(new ArrayList<>(mConfigs.values()));
93     }
94 
95     @Override
getPropertyConfigList(int[] propIds)96     public CarPropertyConfigList getPropertyConfigList(int[] propIds) {
97         List<CarPropertyConfig> configs = new ArrayList<>(propIds.length);
98         for (int prop : propIds) {
99             CarPropertyConfig cfg = mConfigs.get(prop);
100             if (cfg != null) {
101                 configs.add(cfg);
102             }
103         }
104         return new CarPropertyConfigList(configs);
105     }
106 
107     @Override
getPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)108     public void getPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
109             IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)
110             throws RemoteException {
111         List<AsyncPropertyServiceRequest> asyncPropertyServiceRequestList =
112                 asyncPropertyServiceRequests.getList();
113         List<GetSetValueResult> getValueResults = new ArrayList<>();
114         for (int i = 0; i < asyncPropertyServiceRequestList.size(); i++) {
115             AsyncPropertyServiceRequest asyncPropertyServiceRequest =
116                     asyncPropertyServiceRequestList.get(i);
117             getValueResults.add(GetSetValueResult.newGetValueResult(
118                     asyncPropertyServiceRequest.getRequestId(),
119                     getProperty(asyncPropertyServiceRequest.getPropertyId(),
120                             asyncPropertyServiceRequest.getAreaId())));
121         }
122         asyncPropertyResultCallback.onGetValueResults(new GetSetValueResultList(getValueResults));
123     }
124 
125     @Override
setPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests, IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)126     public void setPropertiesAsync(AsyncPropertyServiceRequestList asyncPropertyServiceRequests,
127             IAsyncPropertyResultCallback asyncPropertyResultCallback, long timeoutInMs)
128             throws RemoteException {
129         List<AsyncPropertyServiceRequest> asyncPropertyServiceRequestList =
130                 asyncPropertyServiceRequests.getList();
131         List<GetSetValueResult> setValueResults = new ArrayList<>();
132         for (int i = 0; i < asyncPropertyServiceRequestList.size(); i++) {
133             AsyncPropertyServiceRequest asyncPropertyServiceRequest =
134                     asyncPropertyServiceRequestList.get(i);
135             setProperty(asyncPropertyServiceRequest.getCarPropertyValue(), /* listener= */ null);
136             setValueResults.add(GetSetValueResult.newSetValueResult(
137                     asyncPropertyServiceRequest.getRequestId(), /* updateTimestampNanos= */ 0));
138         }
139         asyncPropertyResultCallback.onSetValueResults(new GetSetValueResultList(setValueResults));
140     }
141 
142     @Override
getProperty(int prop, int zone)143     public CarPropertyValue getProperty(int prop, int zone) throws RemoteException {
144         return mValues.get(PropKey.of(prop, zone));
145     }
146 
147     @Override
setProperty(CarPropertyValue prop, ICarPropertyEventListener listener)148     public void setProperty(CarPropertyValue prop, ICarPropertyEventListener listener)
149             throws RemoteException {
150         mValues.put(PropKey.of(prop), prop);
151         mValuesSet.add(prop);
152         sendEvent(prop);
153     }
154 
155     @Override
cancelRequests(int[] serviceRequestIds)156     public void cancelRequests(int[] serviceRequestIds) {
157         // Do nothing.
158     }
159 
160     @Override
getReadPermission(int propId)161     public String getReadPermission(int propId) throws RemoteException {
162         return mConfigs.containsKey(propId) ? mPermissions.getReadPermission(propId) : null;
163     }
164 
165     @Override
getWritePermission(int propId)166     public String getWritePermission(int propId) throws RemoteException {
167         return mConfigs.containsKey(propId) ? mPermissions.getWritePermission(propId) : null;
168     }
169 
170     @Override
addProperty(Integer propId, Object value)171     public CarPropertyController addProperty(Integer propId, Object value) {
172         int areaType = getVehicleAreaType(propId);
173         Class<?> type = getPropertyType(propId);
174         CarPropertyConfig.Builder<?> builder = CarPropertyConfig
175                 .newBuilder(type, propId, areaType);
176         mConfigs.put(propId, builder.build());
177         if (value != null) {
178             updateValues(false, new CarPropertyValue<>(propId, 0, value));
179         }
180 
181         return this;
182     }
183 
184     @Override
addProperty(CarPropertyConfig<?> config, @Nullable CarPropertyValue<?> value)185     public CarPropertyController addProperty(CarPropertyConfig<?> config,
186             @Nullable CarPropertyValue<?> value) {
187         mConfigs.put(config.getPropertyId(), config);
188         if (value != null) {
189             updateValues(false, value);
190         }
191         return this;
192     }
193 
194     @Override
updateValues(boolean triggerListeners, CarPropertyValue<?>... propValues)195     public void updateValues(boolean triggerListeners, CarPropertyValue<?>... propValues) {
196         for (CarPropertyValue v : propValues) {
197             mValues.put(PropKey.of(v), v);
198             if (triggerListeners) {
199                 sendEvent(v);
200             }
201         }
202     }
203 
sendEvent(CarPropertyValue v)204     private void sendEvent(CarPropertyValue v) {
205         Set<ListenerInfo> listeners = mListeners.get(v.getPropertyId());
206         if (listeners != null) {
207             for (ListenerInfo listenerInfo : listeners) {
208                 List<CarPropertyEvent> events = new ArrayList<>();
209                 events.add(new CarPropertyEvent(PROPERTY_EVENT_PROPERTY_CHANGE, v));
210                 try {
211                     listenerInfo.mListener.onEvent(events);
212                 } catch (RemoteException e) {
213                     // This is impossible as the code runs within the same process in test.
214                     throw new RuntimeException(e);
215                 }
216             }
217         }
218     }
219 
220     @Override
getSetValues()221     public List<CarPropertyValue<?>> getSetValues() {
222         // Explicitly return the instance of this object rather than copying it such that test code
223         // will have a chance to clear this list if needed.
224         return mValuesSet;
225     }
226 
227     /** Consists of property id and area */
228     private static class PropKey {
229         final int mPropId;
230         final int mAreaId;
231 
PropKey(int propId, int areaId)232         private PropKey(int propId, int areaId) {
233             this.mPropId = propId;
234             this.mAreaId = areaId;
235         }
236 
of(int propId, int areaId)237         static PropKey of(int propId, int areaId) {
238             return new PropKey(propId, areaId);
239         }
240 
of(CarPropertyValue carPropertyValue)241         static PropKey of(CarPropertyValue carPropertyValue) {
242             return of(carPropertyValue.getPropertyId(), carPropertyValue.getAreaId());
243         }
244 
245         @Override
246 
equals(Object o)247         public boolean equals(Object o) {
248             if (this == o) {
249                 return true;
250             }
251             if (!(o instanceof PropKey)) {
252                 return false;
253             }
254             PropKey propKey = (PropKey) o;
255             return mPropId == propKey.mPropId && mAreaId == propKey.mAreaId;
256         }
257 
258         @Override
hashCode()259         public int hashCode() {
260             return Objects.hash(mPropId, mAreaId);
261         }
262     }
263 
264     private static class ListenerInfo {
265         private final ICarPropertyEventListener mListener;
266 
ListenerInfo(ICarPropertyEventListener listener)267         ListenerInfo(ICarPropertyEventListener listener) {
268             this.mListener = listener;
269         }
270 
271         @Override
equals(Object o)272         public boolean equals(Object o) {
273             if (this == o) {
274                 return true;
275             }
276             if (!(o instanceof ListenerInfo)) {
277                 return false;
278             }
279             ListenerInfo that = (ListenerInfo) o;
280             return Objects.equals(mListener, that.mListener);
281         }
282 
283         @Override
hashCode()284         public int hashCode() {
285             return Objects.hash(mListener);
286         }
287     }
288 
getPropertyType(int propId)289     private static Class<?> getPropertyType(int propId) {
290         int type = propId & VehiclePropertyType.MASK;
291         switch (type) {
292             case VehiclePropertyType.BOOLEAN:
293                 return Boolean.class;
294             case VehiclePropertyType.FLOAT:
295                 return Float.class;
296             case VehiclePropertyType.INT32:
297                 return Integer.class;
298             case VehiclePropertyType.INT64:
299                 return Long.class;
300             case VehiclePropertyType.FLOAT_VEC:
301                 return Float[].class;
302             case VehiclePropertyType.INT32_VEC:
303                 return Integer[].class;
304             case VehiclePropertyType.INT64_VEC:
305                 return Long[].class;
306             case VehiclePropertyType.STRING:
307                 return String.class;
308             case VehiclePropertyType.BYTES:
309                 return byte[].class;
310             case VehiclePropertyType.MIXED:
311                 return Object.class;
312             default:
313                 throw new IllegalArgumentException("Unexpected type: " + toHexString(type));
314         }
315     }
316 
getVehicleAreaType(int propId)317     private static int getVehicleAreaType(int propId) {
318         int halArea = propId & VehicleArea.MASK;
319         switch (halArea) {
320             case VehicleArea.GLOBAL:
321                 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
322             case VehicleArea.SEAT:
323                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
324             case VehicleArea.DOOR:
325                 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR;
326             case VehicleArea.WINDOW:
327                 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW;
328             case VehicleArea.MIRROR:
329                 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR;
330             case VehicleArea.WHEEL:
331                 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL;
332             default:
333                 throw new RuntimeException("Unsupported area type " + halArea);
334         }
335     }
336 
337     /** Copy from VHAL generated file VehicleArea.java */
338     private static final class VehicleArea {
339         static final int GLOBAL = 16777216 /* 0x01000000 */;
340         static final int WINDOW = 50331648 /* 0x03000000 */;
341         static final int MIRROR = 67108864 /* 0x04000000 */;
342         static final int SEAT = 83886080 /* 0x05000000 */;
343         static final int DOOR = 100663296 /* 0x06000000 */;
344         static final int WHEEL = 117440512 /* 0x07000000 */;
345         static final int MASK = 251658240 /* 0x0f000000 */;
346     }
347 }
348