/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.test; import android.car.Car; import android.car.CarNotConnectedException; import android.util.Log; import com.android.car.vehiclenetwork.VehicleNetwork.VehicleNetworkHalMock; import com.android.car.vehiclenetwork.VehicleNetworkConsts; import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel; import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess; import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode; import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType; import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig; import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfigs; import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue; import com.android.car.vehiclenetwork.VehiclePropValueUtil; import java.lang.reflect.Field; import java.util.HashMap; /** * This is for mocking vehicle HAL and testing system's internal behavior. * By default, emulated vehicle HAL will have all properties defined with default values * returned for get call. For interested properties, each test can replace default behavior with * {@link #addProperty(VehiclePropConfig, VehicleHalPropertyHandler)} or * {@link #addStaticProperty(VehiclePropConfig, VehiclePropValue)}. * To test a case where specific property should not be present, test can call * {@link #removeProperty(int)}. * * Adding / removing properties should be done before calling {@link #start()} as the call will * start emulating with properties added / removed up to now. * @hide */ public class VehicleHalEmulator { private static final String TAG = VehicleHalEmulator.class.getSimpleName(); /** * Interface for handler of each property. */ public interface VehicleHalPropertyHandler { void onPropertySet(VehiclePropValue value); VehiclePropValue onPropertyGet(VehiclePropValue value); void onPropertySubscribe(int property, float sampleRate, int zones); void onPropertyUnsubscribe(int property); } private final HashMap mProperties = new HashMap<>(); private final CarTestManager mCarTestManager; private final HalMock mMock = new HalMock(); private boolean mDefaultPropertiesPopulated = false; private boolean mStarted = false; /** * Constructor. Car instance passed should be already connected to car service. * @param car */ public VehicleHalEmulator(Car car) { try { mCarTestManager = new CarTestManager( (CarTestManagerBinderWrapper) car.getCarManager(Car.TEST_SERVICE)); } catch (CarNotConnectedException e) { throw new RuntimeException(e); } } /** * Add property to mocked vehicle hal. * @param config * @param handler */ public synchronized void addProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler) { populateDefaultPropertiesIfNecessary(); VehicleHalProperty halProp = new VehicleHalProperty(config, handler); mProperties.put(config.getProp(), halProp); } /** * Add static property to mocked vehicle hal. * @param config * @param value */ public synchronized void addStaticProperty(VehiclePropConfig config, VehiclePropValue value) { populateDefaultPropertiesIfNecessary(); DefaultPropertyHandler handler = new DefaultPropertyHandler(config, value); VehicleHalProperty halProp = new VehicleHalProperty(config, handler); mProperties.put(config.getProp(), halProp); } /** * Remove this property from vehicle HAL properties. Emulated vehicle HAL will not have this * property. This is useful to test the case where specific property is not present. * @param property */ public synchronized void removeProperty(int property) { populateDefaultPropertiesIfNecessary(); mProperties.remove(property); } /** * Start emulation. All necessary properties should have been added / removed before this. */ public void start() { mCarTestManager.startMocking(mMock, CarTestManager.FLAG_MOCKING_NONE); synchronized (this) { mStarted = true; } } /** Whether emulation is started or not. */ public synchronized boolean isStarted() { return mStarted; } /** * Stop emulation. should be done before finishing test. */ public void stop() { mCarTestManager.stopMocking(); synchronized (this) { mStarted = false; } } /** * Inject given value to VNS which ultimately delivered as HAL event to clients. * This can be used to emulate H/W side change. * @param value */ public void injectEvent(VehiclePropValue value) { mCarTestManager.injectEvent(value); } public static void assertPropertyForGet(VehiclePropConfig config, int property) { assertProperty(config, property); if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ) == 0) { throw new IllegalArgumentException("cannot set write-only property 0x" + Integer.toHexString(config.getProp())); } } public static void assertPropertyForSet(VehiclePropConfig config, VehiclePropValue value) { assertProperty(config, value.getProp()); if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE) == 0) { throw new IllegalArgumentException("cannot set read-only property 0x" + Integer.toHexString(config.getProp())); } } public static void assertPropertyForSubscribe(VehiclePropConfig config, int property, float sampleRate, int zones) { assertPropertyForGet(config, property); if (config.getChangeMode() == VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC) { throw new IllegalStateException("cannot subscribe static property 0x" + Integer.toHexString(config.getProp())); } } public static void assertProperty(VehiclePropConfig config, int property) { if (config.getProp() != property) { throw new IllegalStateException("Wrong prop, expecting 0x" + Integer.toHexString(config.getProp()) + " while got 0x" + Integer.toHexString(property)); } } private synchronized void populateDefaultPropertiesIfNecessary() { if (mDefaultPropertiesPopulated) { return; } for (Field f : VehicleNetworkConsts.class.getDeclaredFields()) { if (f.getType() == int.class) { int property = 0; try { property = f.getInt(null); } catch (IllegalAccessException e) { continue; } int valueType = VehicleNetworkConsts.getVehicleValueType(property); if (valueType == VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE) { // invalid property or not a property continue; } int changeMode = VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC; int[] changeModes = VehicleNetworkConsts.getVehicleChangeMode(property); if (changeModes != null) { changeMode = changeModes[0]; } int[] accesses = VehicleNetworkConsts.getVehicleAccess(property); if (accesses == null) { // invalid continue; } VehiclePropConfig config = VehiclePropConfig.newBuilder(). setProp(property). setAccess(accesses[0]). setChangeMode(changeMode). setValueType(valueType). setPermissionModel( VehiclePermissionModel.VEHICLE_PERMISSION_NO_RESTRICTION). addConfigArray(0). setSampleRateMax(0). setSampleRateMin(0). build(); VehiclePropValue initialValue = VehiclePropValueUtil.createDummyValue(property, valueType); DefaultPropertyHandler handler = new DefaultPropertyHandler(config, initialValue); VehicleHalProperty halProp = new VehicleHalProperty(config, handler); mProperties.put(property, halProp); } } mDefaultPropertiesPopulated = true; } private synchronized VehiclePropConfigs handleListProperties() { VehiclePropConfigs.Builder builder = VehiclePropConfigs.newBuilder(); for (VehicleHalProperty halProp : mProperties.values()) { builder.addConfigs(halProp.config); } return builder.build(); } private synchronized void handlePropertySet(VehiclePropValue value) { getHalPropertyLocked(value.getProp()).handler.onPropertySet(value); } private synchronized VehiclePropValue handlePropertyGet(VehiclePropValue value) { return getHalPropertyLocked(value.getProp()).handler.onPropertyGet(value); } private synchronized void handlePropertySubscribe(int property, float sampleRate, int zones) { getHalPropertyLocked(property).handler.onPropertySubscribe(property, sampleRate, zones); } private synchronized void handlePropertyUnsubscribe(int property) { getHalPropertyLocked(property).handler.onPropertyUnsubscribe(property); } private VehicleHalProperty getHalPropertyLocked(int property) { VehicleHalProperty halProp = mProperties.get(property); if (halProp == null) { IllegalArgumentException e = new IllegalArgumentException(); Log.i(TAG, "property not supported:" + Integer.toHexString(property), e); throw e; } return halProp; } private static class VehicleHalProperty { public final VehiclePropConfig config; public final VehicleHalPropertyHandler handler; public VehicleHalProperty(VehiclePropConfig config, VehicleHalPropertyHandler handler) { this.config = config; this.handler = handler; } } private static class DefaultPropertyHandler implements VehicleHalPropertyHandler { private final VehiclePropConfig mConfig; private VehiclePropValue mValue; private boolean mSubscribed = false; public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) { mConfig = config; mValue = initialValue; } @Override public synchronized void onPropertySet(VehiclePropValue value) { assertPropertyForSet(mConfig, value); mValue = value; } @Override public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { assertPropertyForGet(mConfig, value.getProp()); return mValue; } @Override public synchronized void onPropertySubscribe(int property, float sampleRate, int zones) { assertPropertyForSubscribe(mConfig, property, sampleRate, zones); mSubscribed = true; } @Override public synchronized void onPropertyUnsubscribe(int property) { assertProperty(mConfig, property); if (!mSubscribed) { throw new IllegalArgumentException("unsubscibe for not subscribed property 0x" + Integer.toHexString(property)); } mSubscribed = false; } } private class HalMock implements VehicleNetworkHalMock { @Override public VehiclePropConfigs onListProperties() { return handleListProperties(); } @Override public void onPropertySet(VehiclePropValue value) { handlePropertySet(value); } @Override public VehiclePropValue onPropertyGet(VehiclePropValue value) { return handlePropertyGet(value); } @Override public void onPropertySubscribe(int property, float sampleRate, int zones) { handlePropertySubscribe(property, sampleRate, zones); } @Override public void onPropertyUnsubscribe(int property) { handlePropertyUnsubscribe(property); } } }