Home | History | Annotate | Download | only in car
      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 com.android.car;
     17 
     18 import static org.junit.Assert.assertNotNull;
     19 import static org.junit.Assert.fail;
     20 
     21 import android.car.Car;
     22 import android.car.test.CarTestManager;
     23 import android.car.test.CarTestManagerBinderWrapper;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.ContextWrapper;
     27 import android.content.ServiceConnection;
     28 import android.content.pm.PackageManager.NameNotFoundException;
     29 import android.content.res.Resources;
     30 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
     31 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
     32 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
     33 import android.os.Binder;
     34 import android.os.Handler;
     35 import android.os.IBinder;
     36 import android.os.Looper;
     37 import android.support.test.InstrumentationRegistry;
     38 import android.support.test.annotation.UiThreadTest;
     39 import android.util.Log;
     40 import android.util.SparseArray;
     41 
     42 import com.android.car.pm.CarPackageManagerService;
     43 import com.android.car.systeminterface.DisplayInterface;
     44 import com.android.car.systeminterface.IOInterface;
     45 import com.android.car.systeminterface.StorageMonitoringInterface;
     46 import com.android.car.systeminterface.SystemInterface;
     47 import com.android.car.systeminterface.SystemInterface.Builder;
     48 import com.android.car.systeminterface.SystemStateInterface;
     49 import com.android.car.systeminterface.TimeInterface;
     50 import com.android.car.systeminterface.WakeLockInterface;
     51 import com.android.car.test.CarServiceTestApp;
     52 import com.android.car.test.utils.TemporaryDirectory;
     53 import com.android.car.vehiclehal.test.MockedVehicleHal;
     54 import com.android.car.vehiclehal.test.MockedVehicleHal.DefaultPropertyHandler;
     55 import com.android.car.vehiclehal.test.MockedVehicleHal.StaticPropertyHandler;
     56 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
     57 import com.android.car.vehiclehal.test.VehiclePropConfigBuilder;
     58 
     59 import org.junit.After;
     60 import org.junit.Before;
     61 
     62 import java.io.File;
     63 import java.io.IOException;
     64 import java.time.Duration;
     65 import java.util.HashMap;
     66 import java.util.Map;
     67 
     68 /**
     69  * Base class for testing with mocked vehicle HAL (=car).
     70  * It is up to each app to start emulation by getMockedVehicleHal().start() as there will be
     71  * per test set up that should be done before starting.
     72  */
     73 public class MockedCarTestBase {
     74     private static final String TAG = MockedCarTestBase.class.getSimpleName();
     75     static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
     76     static final long SHORT_WAIT_TIMEOUT_MS = 500;
     77 
     78     private Car mCar;
     79     private ICarImpl mCarImpl;
     80     private MockedVehicleHal mMockedVehicleHal;
     81     private SystemInterface mFakeSystemInterface;
     82     private MockContext mMockContext;
     83     private final MockIOInterface mMockIOInterface = new MockIOInterface();
     84 
     85     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
     86 
     87     private final Map<VehiclePropConfigBuilder, VehicleHalPropertyHandler> mHalConfig =
     88             new HashMap<>();
     89     private final SparseArray<VehiclePropConfigBuilder> mPropToConfigBuilder = new SparseArray<>();
     90 
     91     private static final IBinder mCarServiceToken = new Binder();
     92     private static boolean mRealCarServiceReleased = false;
     93 
     94     protected synchronized MockedVehicleHal createMockedVehicleHal() {
     95         return new MockedVehicleHal();
     96     }
     97 
     98     protected synchronized MockedVehicleHal getMockedVehicleHal() {
     99         return mMockedVehicleHal;
    100     }
    101 
    102     protected synchronized SystemInterface getFakeSystemInterface() {
    103         return mFakeSystemInterface;
    104     }
    105 
    106     protected synchronized void configureMockedHal() {
    107     }
    108 
    109     protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
    110         return Builder.newSystemInterface()
    111                 .withSystemStateInterface(new MockSystemStateInterface())
    112                 .withDisplayInterface(new MockDisplayInterface())
    113                 .withIOInterface(mMockIOInterface)
    114                 .withStorageMonitoringInterface(new MockStorageMonitoringInterface())
    115                 .withTimeInterface(new MockTimeInterface())
    116                 .withWakeLockInterface(new MockWakeLockInterface());
    117     }
    118 
    119     protected synchronized void configureFakeSystemInterface() {}
    120 
    121     protected synchronized void configureResourceOverrides(MockResources resources) {
    122         resources.overrideResource(com.android.car.R.string.instrumentClusterRendererService, "");
    123     }
    124 
    125     protected Context getContext() {
    126         return InstrumentationRegistry.getTargetContext();
    127     }
    128 
    129     protected Context getTestContext() {
    130         return InstrumentationRegistry.getContext();
    131     }
    132 
    133     protected String getFlattenComponent(Class cls) {
    134         ComponentName cn = new ComponentName(getTestContext(), cls);
    135         return cn.flattenToString();
    136     }
    137 
    138     @Before
    139     @UiThreadTest
    140     public void setUp() throws Exception {
    141         Log.i(TAG, "setUp");
    142         releaseRealCarService(getContext());
    143 
    144         mMockedVehicleHal = createMockedVehicleHal();
    145         configureMockedHal();
    146 
    147         mFakeSystemInterface = getSystemInterfaceBuilder().build();
    148         configureFakeSystemInterface();
    149 
    150         MockContext context = getCarServiceContext();
    151         configureResourceOverrides(context.getResources());
    152 
    153         mCarImpl = new ICarImpl(context, mMockedVehicleHal, mFakeSystemInterface,
    154                 null /* error notifier */, "MockedCar");
    155 
    156         initMockedHal(false /* no need to release */);
    157 
    158         mCar = new Car(context, mCarImpl, null /* handler */);
    159     }
    160 
    161     @After
    162     public void tearDown() throws Exception {
    163         mCar.disconnect();
    164         mCarImpl.release();
    165 
    166         mMockIOInterface.tearDown();
    167     }
    168 
    169     public CarPackageManagerService getPackageManagerService() {
    170         return (CarPackageManagerService) mCarImpl.getCarService(Car.PACKAGE_SERVICE);
    171     }
    172 
    173     protected MockContext getCarServiceContext() throws NameNotFoundException {
    174         if (mMockContext == null) {
    175             mMockContext = new MockContext(getContext()
    176                 .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY));
    177         }
    178         return mMockContext;
    179     }
    180 
    181     protected synchronized void reinitializeMockedHal() throws Exception {
    182         initMockedHal(true /* release */);
    183     }
    184 
    185     private synchronized void initMockedHal(boolean release) throws Exception {
    186         if (release) {
    187             mCarImpl.release();
    188         }
    189 
    190         for (Map.Entry<VehiclePropConfigBuilder, VehicleHalPropertyHandler> entry
    191                 : mHalConfig.entrySet()) {
    192             mMockedVehicleHal.addProperty(entry.getKey().build(), entry.getValue());
    193         }
    194         mHalConfig.clear();
    195         mCarImpl.init();
    196     }
    197 
    198     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
    199             VehicleHalPropertyHandler propertyHandler) {
    200         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
    201         setConfigBuilder(builder, propertyHandler);
    202         return builder;
    203     }
    204 
    205     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId) {
    206         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
    207         setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), null));
    208         return builder;
    209     }
    210 
    211     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
    212             VehiclePropValue value) {
    213         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
    214         setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), value));
    215         return builder;
    216     }
    217 
    218     protected synchronized VehiclePropConfigBuilder addStaticProperty(int propertyId,
    219             VehiclePropValue value) {
    220         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId)
    221                 .setChangeMode(VehiclePropertyChangeMode.STATIC)
    222                 .setAccess(VehiclePropertyAccess.READ);
    223 
    224         setConfigBuilder(builder, new StaticPropertyHandler(value));
    225         return builder;
    226     }
    227 
    228     private void setConfigBuilder(VehiclePropConfigBuilder builder,
    229             VehicleHalPropertyHandler propertyHandler) {
    230         int propId = builder.build().prop;
    231 
    232         // Override previous property config if exists.
    233         VehiclePropConfigBuilder prevBuilder = mPropToConfigBuilder.get(propId);
    234         if (prevBuilder != null) {
    235             mHalConfig.remove(prevBuilder);
    236         }
    237         mPropToConfigBuilder.put(propId, builder);
    238         mHalConfig.put(builder, propertyHandler);
    239     }
    240 
    241     protected synchronized android.car.Car getCar() {
    242         return mCar;
    243     }
    244 
    245     /*
    246      * In order to eliminate interfering with real car service we will disable it. It will be
    247      * enabled back in CarTestService when mCarServiceToken will go away (tests finish).
    248      */
    249     private synchronized static void releaseRealCarService(Context context) throws Exception {
    250         if (mRealCarServiceReleased) {
    251             return;  // We just want to release it once.
    252         }
    253 
    254         mRealCarServiceReleased = true;  // To make sure it was called once.
    255 
    256         Object waitForConnection = new Object();
    257         android.car.Car car = android.car.Car.createCar(context, new ServiceConnection() {
    258             @Override
    259             public void onServiceConnected(ComponentName name, IBinder service) {
    260                 synchronized (waitForConnection) {
    261                     waitForConnection.notify();
    262                 }
    263             }
    264 
    265             @Override
    266             public void onServiceDisconnected(ComponentName name) { }
    267         });
    268 
    269         car.connect();
    270         synchronized (waitForConnection) {
    271             if (!car.isConnected()) {
    272                 waitForConnection.wait(DEFAULT_WAIT_TIMEOUT_MS);
    273             }
    274         }
    275 
    276         if (car.isConnected()) {
    277             Log.i(TAG, "Connected to real car service");
    278             CarTestManagerBinderWrapper binderWrapper =
    279                     (CarTestManagerBinderWrapper) car.getCarManager(android.car.Car.TEST_SERVICE);
    280             assertNotNull(binderWrapper);
    281 
    282             CarTestManager mgr = new CarTestManager(binderWrapper.binder);
    283             mgr.stopCarService(mCarServiceToken);
    284         }
    285     }
    286 
    287     static final class MockDisplayInterface implements DisplayInterface {
    288 
    289         @Override
    290         public void setDisplayBrightness(int brightness) {}
    291 
    292         @Override
    293         public void setDisplayState(boolean on) {}
    294 
    295         @Override
    296         public void startDisplayStateMonitoring(CarPowerManagementService service) {}
    297 
    298         @Override
    299         public void stopDisplayStateMonitoring() {}
    300     }
    301 
    302     static final class MockIOInterface implements IOInterface {
    303         private TemporaryDirectory mFilesDir = null;
    304 
    305         @Override
    306         public File getFilesDir() {
    307             if (mFilesDir == null) {
    308                 try {
    309                     mFilesDir = new TemporaryDirectory(TAG);
    310                 } catch (IOException e) {
    311                     Log.e(TAG, "failed to create temporary directory", e);
    312                     fail("failed to create temporary directory. exception was: " + e);
    313                 }
    314             }
    315             return mFilesDir.getDirectory();
    316         }
    317 
    318         public void tearDown() {
    319             if (mFilesDir != null) {
    320                 try {
    321                     mFilesDir.close();
    322                 } catch (Exception e) {
    323                     Log.w(TAG, "could not remove temporary directory", e);
    324                 }
    325             }
    326         }
    327     }
    328 
    329     static final class MockResources extends Resources {
    330         private final HashMap<Integer, Integer> mIntegerOverrides = new HashMap<>();
    331         private final HashMap<Integer, String> mStringOverrides = new HashMap<>();
    332         private final HashMap<Integer, String[]> mStringArrayOverrides = new HashMap<>();
    333 
    334         MockResources(Resources resources) {
    335             super(resources.getAssets(),
    336                     resources.getDisplayMetrics(),
    337                     resources.getConfiguration());
    338         }
    339 
    340         @Override
    341         public int getInteger(int id) {
    342             return mIntegerOverrides.getOrDefault(id,
    343                     super.getInteger(id));
    344         }
    345 
    346         @Override
    347         public String getString(int id) {
    348             return mStringOverrides.getOrDefault(id,
    349                     super.getString(id));
    350         }
    351 
    352         @Override
    353         public String[] getStringArray(int id) {
    354             return mStringArrayOverrides.getOrDefault(id,
    355                     super.getStringArray(id));
    356         }
    357 
    358         MockResources overrideResource(int id, int value) {
    359             mIntegerOverrides.put(id, value);
    360             return this;
    361         }
    362 
    363         MockResources overrideResource(int id, String value) {
    364             mStringOverrides.put(id, value);
    365             return this;
    366         }
    367 
    368         MockResources overrideResource(int id, String[] value) {
    369             mStringArrayOverrides.put(id, value);
    370             return this;
    371         }
    372     }
    373 
    374     static class MockContext extends ContextWrapper {
    375         private final MockResources mResources;
    376 
    377         MockContext(Context base) {
    378             super(base);
    379             mResources = new MockResources(super.getResources());
    380         }
    381 
    382         @Override
    383         public MockResources getResources() {
    384             return mResources;
    385         }
    386 
    387         // BLUETOOTH_SERVICE needs to be retrieved with an Application context, which neither
    388         // this MockContext, nor its base Context are. In order to make this work, we save
    389         // an Application context at startup and use it when necessary. Add other services
    390         // here as needed.
    391         @Override
    392         public Object getSystemService(String name) {
    393             switch (name) {
    394                 case BLUETOOTH_SERVICE:
    395                     return CarServiceTestApp.getAppContext().getSystemService(name);
    396                 default:
    397                     return super.getSystemService(name);
    398             }
    399         }
    400 
    401         @Override
    402         public Context getApplicationContext() {
    403             return this;
    404         }
    405     }
    406 
    407     static final class MockStorageMonitoringInterface implements StorageMonitoringInterface {}
    408 
    409     static final class MockSystemStateInterface implements SystemStateInterface {
    410         @Override
    411         public void shutdown() {}
    412 
    413         @Override
    414         public boolean enterDeepSleep(int wakeupTimeSec) {
    415             return true;
    416         }
    417 
    418         @Override
    419         public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
    420     }
    421 
    422     static final class MockTimeInterface implements TimeInterface {
    423 
    424         @Override
    425         public void scheduleAction(Runnable r, long delayMs) {}
    426 
    427         @Override
    428         public void cancelAllActions() {}
    429     }
    430 
    431     static final class MockWakeLockInterface implements WakeLockInterface {
    432 
    433         @Override
    434         public void releaseAllWakeLocks() {}
    435 
    436         @Override
    437         public void switchToPartialWakeLock() {}
    438 
    439         @Override
    440         public void switchToFullWakeLock() {}
    441     }
    442 
    443 }
    444