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