Home | History | Annotate | Download | only in test
      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 {
     55         void onPropertySet(VehiclePropValue value);
     56         VehiclePropValue onPropertyGet(VehiclePropValue value);
     57         void onPropertySubscribe(int property, float sampleRate, int zones);
     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      */
     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      */
     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      */
     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      */
    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      */
    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. */
    127     public synchronized boolean isStarted() {
    128         return mStarted;
    129     }
    130 
    131     /**
    132      * Stop emulation. should be done before finishing test.
    133      */
    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      */
    146     public void injectEvent(VehiclePropValue value) {
    147         mCarTestManager.injectEvent(value);
    148     }
    149 
    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 
    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 
    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 
    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 
    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 
    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 
    238     private synchronized void handlePropertySet(VehiclePropValue value) {
    239         getHalPropertyLocked(value.getProp()).handler.onPropertySet(value);
    240     }
    241 
    242     private synchronized VehiclePropValue handlePropertyGet(VehiclePropValue value) {
    243         return getHalPropertyLocked(value.getProp()).handler.onPropertyGet(value);
    244     }
    245 
    246     private synchronized void handlePropertySubscribe(int property, float sampleRate, int zones) {
    247         getHalPropertyLocked(property).handler.onPropertySubscribe(property, sampleRate, zones);
    248     }
    249 
    250     private synchronized void handlePropertyUnsubscribe(int property) {
    251         getHalPropertyLocked(property).handler.onPropertyUnsubscribe(property);
    252     }
    253 
    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 
    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 
    279         public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) {
    280             mConfig = config;
    281             mValue = initialValue;
    282         }
    283 
    284         @Override
    285         public synchronized void onPropertySet(VehiclePropValue value) {
    286             assertPropertyForSet(mConfig, value);
    287             mValue = value;
    288         }
    289 
    290         @Override
    291         public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
    292             assertPropertyForGet(mConfig, value.getProp());
    293             return mValue;
    294         }
    295 
    296         @Override
    297         public synchronized void onPropertySubscribe(int property, float sampleRate, int zones) {
    298             assertPropertyForSubscribe(mConfig, property, sampleRate, zones);
    299             mSubscribed = true;
    300         }
    301 
    302         @Override
    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
    317         public VehiclePropConfigs onListProperties() {
    318             return handleListProperties();
    319         }
    320 
    321         @Override
    322         public void onPropertySet(VehiclePropValue value) {
    323             handlePropertySet(value);
    324         }
    325 
    326         @Override
    327         public VehiclePropValue onPropertyGet(VehiclePropValue value) {
    328             return handlePropertyGet(value);
    329         }
    330 
    331         @Override
    332         public void onPropertySubscribe(int property, float sampleRate, int zones) {
    333             handlePropertySubscribe(property, sampleRate, zones);
    334         }
    335 
    336         @Override
    337         public void onPropertyUnsubscribe(int property) {
    338             handlePropertyUnsubscribe(property);
    339         }
    340     }
    341 }
    342