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