1 /* 2 * Copyright (C) 2015 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 package android.car.test; 17 18 import android.car.Car; 19 import android.car.CarNotConnectedException; 20 import android.util.Log; 21 22 import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkHalMock; 23 import com.android.car.vehiclenetwork.VehicleNetworkConsts; 24 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel; 25 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess; 26 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode; 27 import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType; 28 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig; 29 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs; 30 import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue; 31 import com.android.car.vehiclenetwork.VehiclePropValueUtil; 32 33 import java.lang.reflect.Field; 34 import java.util.HashMap; 35 36 /** 37 * This is for mocking vehicle HAL and testing system's internal behavior. 38 * By default, emulated vehicle HAL will have all properties defined with default values 39 * returned for get call. For interested properties, each test can replace default behavior with 40 * {@link #addProperty(VehiclePropConfig, VehicleHalPropertyHandler)} or 41 * {@link #addStaticProperty(VehiclePropConfig, VehiclePropValue)}. 42 * To test a case where specific property should not be present, test can call 43 * {@link #removeProperty(int)}. 44 * 45 * Adding / removing properties should be done before calling {@link #start()} as the call will 46 * start emulating with properties added / removed up to now. 47 * @hide 48 */ 49 public class VehicleHalEmulator { 50 private static final String TAG = VehicleHalEmulator.class.getSimpleName(); 51 /** 52 * Interface for handler of each property. 53 */ 54 public interface VehicleHalPropertyHandler { onPropertySet(VehiclePropValue value)55 void onPropertySet(VehiclePropValue value); onPropertyGet(VehiclePropValue value)56 VehiclePropValue onPropertyGet(VehiclePropValue value); onPropertySubscribe(int property, float sampleRate, int zones)57 void onPropertySubscribe(int property, float sampleRate, int zones); onPropertyUnsubscribe(int property)58 void onPropertyUnsubscribe(int property); 59 } 60 61 private final HashMap<Integer, VehicleHalProperty> mProperties = 62 new HashMap<>(); 63 64 private final CarTestManager mCarTestManager; 65 private final HalMock mMock = new HalMock(); 66 private boolean mDefaultPropertiesPopulated = false; 67 private boolean mStarted = false; 68 69 /** 70 * Constructor. Car instance passed should be already connected to car service. 71 * @param car 72 */ VehicleHalEmulator(Car car)73 public VehicleHalEmulator(Car car) { 74 try { 75 mCarTestManager = new CarTestManager( 76 (CarTestManagerBinderWrapper) car.getCarManager(Car.TEST_SERVICE)); 77 } catch (CarNotConnectedException e) { 78 throw new RuntimeException(e); 79 } 80 } 81 82 /** 83 * Add property to mocked vehicle hal. 84 * @param config 85 * @param handler 86 */ addProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler)87 public synchronized void addProperty(VehiclePropConfig config, 88 VehicleHalPropertyHandler handler) { 89 populateDefaultPropertiesIfNecessary(); 90 VehicleHalProperty halProp = new VehicleHalProperty(config, handler); 91 mProperties.put(config.getProp(), halProp); 92 } 93 94 /** 95 * Add static property to mocked vehicle hal. 96 * @param config 97 * @param value 98 */ addStaticProperty(VehiclePropConfig config, VehiclePropValue value)99 public synchronized void addStaticProperty(VehiclePropConfig config, VehiclePropValue value) { 100 populateDefaultPropertiesIfNecessary(); 101 DefaultPropertyHandler handler = new DefaultPropertyHandler(config, value); 102 VehicleHalProperty halProp = new VehicleHalProperty(config, handler); 103 mProperties.put(config.getProp(), halProp); 104 } 105 106 /** 107 * Remove this property from vehicle HAL properties. Emulated vehicle HAL will not have this 108 * property. This is useful to test the case where specific property is not present. 109 * @param property 110 */ removeProperty(int property)111 public synchronized void removeProperty(int property) { 112 populateDefaultPropertiesIfNecessary(); 113 mProperties.remove(property); 114 } 115 116 /** 117 * Start emulation. All necessary properties should have been added / removed before this. 118 */ start()119 public void start() { 120 mCarTestManager.startMocking(mMock, CarTestManager.FLAG_MOCKING_NONE); 121 synchronized (this) { 122 mStarted = true; 123 } 124 } 125 126 /** Whether emulation is started or not. */ isStarted()127 public synchronized boolean isStarted() { 128 return mStarted; 129 } 130 131 /** 132 * Stop emulation. should be done before finishing test. 133 */ stop()134 public void stop() { 135 mCarTestManager.stopMocking(); 136 synchronized (this) { 137 mStarted = false; 138 } 139 } 140 141 /** 142 * Inject given value to VNS which ultimately delivered as HAL event to clients. 143 * This can be used to emulate H/W side change. 144 * @param value 145 */ injectEvent(VehiclePropValue value)146 public void injectEvent(VehiclePropValue value) { 147 mCarTestManager.injectEvent(value); 148 } 149 assertPropertyForGet(VehiclePropConfig config, int property)150 public static void assertPropertyForGet(VehiclePropConfig config, int property) { 151 assertProperty(config, property); 152 if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ) == 0) { 153 throw new IllegalArgumentException("cannot set write-only property 0x" + 154 Integer.toHexString(config.getProp())); 155 } 156 } 157 assertPropertyForSet(VehiclePropConfig config, VehiclePropValue value)158 public static void assertPropertyForSet(VehiclePropConfig config, VehiclePropValue value) { 159 assertProperty(config, value.getProp()); 160 if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE) == 0) { 161 throw new IllegalArgumentException("cannot set read-only property 0x" + 162 Integer.toHexString(config.getProp())); 163 } 164 } 165 assertPropertyForSubscribe(VehiclePropConfig config, int property, float sampleRate, int zones)166 public static void assertPropertyForSubscribe(VehiclePropConfig config, int property, 167 float sampleRate, int zones) { 168 assertPropertyForGet(config, property); 169 if (config.getChangeMode() == VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC) { 170 throw new IllegalStateException("cannot subscribe static property 0x" + 171 Integer.toHexString(config.getProp())); 172 } 173 } 174 assertProperty(VehiclePropConfig config, int property)175 public static void assertProperty(VehiclePropConfig config, int property) { 176 if (config.getProp() != property) { 177 throw new IllegalStateException("Wrong prop, expecting 0x" + 178 Integer.toHexString(config.getProp()) + " while got 0x" + 179 Integer.toHexString(property)); 180 } 181 } 182 populateDefaultPropertiesIfNecessary()183 private synchronized void populateDefaultPropertiesIfNecessary() { 184 if (mDefaultPropertiesPopulated) { 185 return; 186 } 187 for (Field f : VehicleNetworkConsts.class.getDeclaredFields()) { 188 if (f.getType() == int.class) { 189 int property = 0; 190 try { 191 property = f.getInt(null); 192 } catch (IllegalAccessException e) { 193 continue; 194 } 195 int valueType = VehicleNetworkConsts.getVehicleValueType(property); 196 if (valueType == VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE) { 197 // invalid property or not a property 198 continue; 199 } 200 int changeMode = VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC; 201 int[] changeModes = VehicleNetworkConsts.getVehicleChangeMode(property); 202 if (changeModes != null) { 203 changeMode = changeModes[0]; 204 } 205 int[] accesses = VehicleNetworkConsts.getVehicleAccess(property); 206 if (accesses == null) { // invalid 207 continue; 208 } 209 VehiclePropConfig config = VehiclePropConfig.newBuilder(). 210 setProp(property). 211 setAccess(accesses[0]). 212 setChangeMode(changeMode). 213 setValueType(valueType). 214 setPermissionModel( 215 VehiclePermissionModel.VEHICLE_PERMISSION_NO_RESTRICTION). 216 addConfigArray(0). 217 setSampleRateMax(0). 218 setSampleRateMin(0). 219 build(); 220 VehiclePropValue initialValue = VehiclePropValueUtil.createDummyValue(property, 221 valueType); 222 DefaultPropertyHandler handler = new DefaultPropertyHandler(config, initialValue); 223 VehicleHalProperty halProp = new VehicleHalProperty(config, handler); 224 mProperties.put(property, halProp); 225 } 226 } 227 mDefaultPropertiesPopulated = true; 228 } 229 handleListProperties()230 private synchronized VehiclePropConfigs handleListProperties() { 231 VehiclePropConfigs.Builder builder = VehiclePropConfigs.newBuilder(); 232 for (VehicleHalProperty halProp : mProperties.values()) { 233 builder.addConfigs(halProp.config); 234 } 235 return builder.build(); 236 } 237 handlePropertySet(VehiclePropValue value)238 private synchronized void handlePropertySet(VehiclePropValue value) { 239 getHalPropertyLocked(value.getProp()).handler.onPropertySet(value); 240 } 241 handlePropertyGet(VehiclePropValue value)242 private synchronized VehiclePropValue handlePropertyGet(VehiclePropValue value) { 243 return getHalPropertyLocked(value.getProp()).handler.onPropertyGet(value); 244 } 245 handlePropertySubscribe(int property, float sampleRate, int zones)246 private synchronized void handlePropertySubscribe(int property, float sampleRate, int zones) { 247 getHalPropertyLocked(property).handler.onPropertySubscribe(property, sampleRate, zones); 248 } 249 handlePropertyUnsubscribe(int property)250 private synchronized void handlePropertyUnsubscribe(int property) { 251 getHalPropertyLocked(property).handler.onPropertyUnsubscribe(property); 252 } 253 getHalPropertyLocked(int property)254 private VehicleHalProperty getHalPropertyLocked(int property) { 255 VehicleHalProperty halProp = mProperties.get(property); 256 if (halProp == null) { 257 IllegalArgumentException e = new IllegalArgumentException(); 258 Log.i(TAG, "property not supported:" + Integer.toHexString(property), e); 259 throw e; 260 } 261 return halProp; 262 } 263 264 private static class VehicleHalProperty { 265 public final VehiclePropConfig config; 266 public final VehicleHalPropertyHandler handler; 267 VehicleHalProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler)268 public VehicleHalProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler) { 269 this.config = config; 270 this.handler = handler; 271 } 272 } 273 274 private static class DefaultPropertyHandler implements VehicleHalPropertyHandler { 275 private final VehiclePropConfig mConfig; 276 private VehiclePropValue mValue; 277 private boolean mSubscribed = false; 278 DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue)279 public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) { 280 mConfig = config; 281 mValue = initialValue; 282 } 283 284 @Override onPropertySet(VehiclePropValue value)285 public synchronized void onPropertySet(VehiclePropValue value) { 286 assertPropertyForSet(mConfig, value); 287 mValue = value; 288 } 289 290 @Override onPropertyGet(VehiclePropValue value)291 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 292 assertPropertyForGet(mConfig, value.getProp()); 293 return mValue; 294 } 295 296 @Override onPropertySubscribe(int property, float sampleRate, int zones)297 public synchronized void onPropertySubscribe(int property, float sampleRate, int zones) { 298 assertPropertyForSubscribe(mConfig, property, sampleRate, zones); 299 mSubscribed = true; 300 } 301 302 @Override onPropertyUnsubscribe(int property)303 public synchronized void onPropertyUnsubscribe(int property) { 304 assertProperty(mConfig, property); 305 if (!mSubscribed) { 306 throw new IllegalArgumentException("unsubscibe for not subscribed property 0x" + 307 Integer.toHexString(property)); 308 } 309 mSubscribed = false; 310 } 311 312 } 313 314 private class HalMock implements VehicleNetworkHalMock { 315 316 @Override onListProperties()317 public VehiclePropConfigs onListProperties() { 318 return handleListProperties(); 319 } 320 321 @Override onPropertySet(VehiclePropValue value)322 public void onPropertySet(VehiclePropValue value) { 323 handlePropertySet(value); 324 } 325 326 @Override onPropertyGet(VehiclePropValue value)327 public VehiclePropValue onPropertyGet(VehiclePropValue value) { 328 return handlePropertyGet(value); 329 } 330 331 @Override onPropertySubscribe(int property, float sampleRate, int zones)332 public void onPropertySubscribe(int property, float sampleRate, int zones) { 333 handlePropertySubscribe(property, sampleRate, zones); 334 } 335 336 @Override onPropertyUnsubscribe(int property)337 public void onPropertyUnsubscribe(int property) { 338 handlePropertyUnsubscribe(property); 339 } 340 } 341 } 342