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