• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createMockedVehicleHal()94     protected synchronized MockedVehicleHal createMockedVehicleHal() {
95         return new MockedVehicleHal();
96     }
97 
getMockedVehicleHal()98     protected synchronized MockedVehicleHal getMockedVehicleHal() {
99         return mMockedVehicleHal;
100     }
101 
getFakeSystemInterface()102     protected synchronized SystemInterface getFakeSystemInterface() {
103         return mFakeSystemInterface;
104     }
105 
configureMockedHal()106     protected synchronized void configureMockedHal() {
107     }
108 
getSystemInterfaceBuilder()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 
configureFakeSystemInterface()119     protected synchronized void configureFakeSystemInterface() {}
120 
configureResourceOverrides(MockResources resources)121     protected synchronized void configureResourceOverrides(MockResources resources) {
122         resources.overrideResource(com.android.car.R.string.instrumentClusterRendererService, "");
123     }
124 
getContext()125     protected Context getContext() {
126         return InstrumentationRegistry.getTargetContext();
127     }
128 
getTestContext()129     protected Context getTestContext() {
130         return InstrumentationRegistry.getContext();
131     }
132 
getFlattenComponent(Class cls)133     protected String getFlattenComponent(Class cls) {
134         ComponentName cn = new ComponentName(getTestContext(), cls);
135         return cn.flattenToString();
136     }
137 
138     @Before
139     @UiThreadTest
setUp()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
tearDown()162     public void tearDown() throws Exception {
163         mCar.disconnect();
164         mCarImpl.release();
165 
166         mMockIOInterface.tearDown();
167     }
168 
getPackageManagerService()169     public CarPackageManagerService getPackageManagerService() {
170         return (CarPackageManagerService) mCarImpl.getCarService(Car.PACKAGE_SERVICE);
171     }
172 
getCarServiceContext()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 
reinitializeMockedHal()181     protected synchronized void reinitializeMockedHal() throws Exception {
182         initMockedHal(true /* release */);
183     }
184 
initMockedHal(boolean release)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 
addProperty(int propertyId, VehicleHalPropertyHandler propertyHandler)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 
addProperty(int propertyId)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 
addProperty(int propertyId, VehiclePropValue value)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 
addStaticProperty(int propertyId, VehiclePropValue value)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 
setConfigBuilder(VehiclePropConfigBuilder builder, VehicleHalPropertyHandler propertyHandler)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 
getCar()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      */
releaseRealCarService(Context context)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
setDisplayBrightness(int brightness)290         public void setDisplayBrightness(int brightness) {}
291 
292         @Override
setDisplayState(boolean on)293         public void setDisplayState(boolean on) {}
294 
295         @Override
startDisplayStateMonitoring(CarPowerManagementService service)296         public void startDisplayStateMonitoring(CarPowerManagementService service) {}
297 
298         @Override
stopDisplayStateMonitoring()299         public void stopDisplayStateMonitoring() {}
300     }
301 
302     static final class MockIOInterface implements IOInterface {
303         private TemporaryDirectory mFilesDir = null;
304 
305         @Override
getFilesDir()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 
tearDown()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 
MockResources(Resources resources)334         MockResources(Resources resources) {
335             super(resources.getAssets(),
336                     resources.getDisplayMetrics(),
337                     resources.getConfiguration());
338         }
339 
340         @Override
getInteger(int id)341         public int getInteger(int id) {
342             return mIntegerOverrides.getOrDefault(id,
343                     super.getInteger(id));
344         }
345 
346         @Override
getString(int id)347         public String getString(int id) {
348             return mStringOverrides.getOrDefault(id,
349                     super.getString(id));
350         }
351 
352         @Override
getStringArray(int id)353         public String[] getStringArray(int id) {
354             return mStringArrayOverrides.getOrDefault(id,
355                     super.getStringArray(id));
356         }
357 
overrideResource(int id, int value)358         MockResources overrideResource(int id, int value) {
359             mIntegerOverrides.put(id, value);
360             return this;
361         }
362 
overrideResource(int id, String value)363         MockResources overrideResource(int id, String value) {
364             mStringOverrides.put(id, value);
365             return this;
366         }
367 
overrideResource(int id, String[] value)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 
MockContext(Context base)377         MockContext(Context base) {
378             super(base);
379             mResources = new MockResources(super.getResources());
380         }
381 
382         @Override
getResources()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
getSystemService(String name)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
getApplicationContext()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
shutdown()411         public void shutdown() {}
412 
413         @Override
enterDeepSleep(int wakeupTimeSec)414         public boolean enterDeepSleep(int wakeupTimeSec) {
415             return true;
416         }
417 
418         @Override
scheduleActionForBootCompleted(Runnable action, Duration delay)419         public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
420     }
421 
422     static final class MockTimeInterface implements TimeInterface {
423 
424         @Override
scheduleAction(Runnable r, long delayMs)425         public void scheduleAction(Runnable r, long delayMs) {}
426 
427         @Override
cancelAllActions()428         public void cancelAllActions() {}
429     }
430 
431     static final class MockWakeLockInterface implements WakeLockInterface {
432 
433         @Override
releaseAllWakeLocks()434         public void releaseAllWakeLocks() {}
435 
436         @Override
switchToPartialWakeLock()437         public void switchToPartialWakeLock() {}
438 
439         @Override
switchToFullWakeLock()440         public void switchToFullWakeLock() {}
441     }
442 
443 }
444