• 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 com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
19 
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.fail;
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.Mockito.doAnswer;
24 import static org.mockito.Mockito.mock;
25 
26 import android.car.Car;
27 import android.car.test.CarTestManager;
28 import android.car.test.CarTestManagerBinderWrapper;
29 import android.car.user.CarUserManager.UserLifecycleListener;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.ContextWrapper;
33 import android.content.Intent;
34 import android.content.ServiceConnection;
35 import android.content.res.Resources;
36 import android.frameworks.automotive.powerpolicy.internal.ICarPowerPolicySystemNotification;
37 import android.frameworks.automotive.powerpolicy.internal.PolicyState;
38 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
39 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
40 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
41 import android.os.Binder;
42 import android.os.Handler;
43 import android.os.IBinder;
44 import android.os.Looper;
45 import android.os.UserHandle;
46 import android.util.Log;
47 import android.util.SparseArray;
48 
49 import androidx.test.annotation.UiThreadTest;
50 import androidx.test.platform.app.InstrumentationRegistry;
51 
52 import com.android.car.power.CarPowerManagementService;
53 import com.android.car.systeminterface.ActivityManagerInterface;
54 import com.android.car.systeminterface.DisplayInterface;
55 import com.android.car.systeminterface.IOInterface;
56 import com.android.car.systeminterface.StorageMonitoringInterface;
57 import com.android.car.systeminterface.SystemInterface;
58 import com.android.car.systeminterface.SystemInterface.Builder;
59 import com.android.car.systeminterface.SystemStateInterface;
60 import com.android.car.systeminterface.TimeInterface;
61 import com.android.car.systeminterface.WakeLockInterface;
62 import com.android.car.test.utils.TemporaryDirectory;
63 import com.android.car.user.CarUserService;
64 import com.android.car.vehiclehal.test.MockedVehicleHal;
65 import com.android.car.vehiclehal.test.MockedVehicleHal.DefaultPropertyHandler;
66 import com.android.car.vehiclehal.test.MockedVehicleHal.StaticPropertyHandler;
67 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
68 import com.android.car.vehiclehal.test.VehiclePropConfigBuilder;
69 import com.android.car.watchdog.CarWatchdogService;
70 
71 import org.junit.After;
72 import org.junit.Before;
73 import org.mockito.MockitoSession;
74 import org.mockito.quality.Strictness;
75 
76 import java.io.File;
77 import java.io.IOException;
78 import java.time.Duration;
79 import java.util.ArrayList;
80 import java.util.HashMap;
81 import java.util.List;
82 import java.util.Map;
83 
84 /**
85  * Base class for testing with mocked vehicle HAL (=car).
86  * It is up to each app to start emulation by getMockedVehicleHal().start() as there will be
87  * per test set up that should be done before starting.
88  */
89 public class MockedCarTestBase {
90     protected static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
91     protected static final long SHORT_WAIT_TIMEOUT_MS = 500;
92     private static final String TAG = MockedCarTestBase.class.getSimpleName();
93     private static final IBinder sCarServiceToken = new Binder();
94     private static boolean sRealCarServiceReleased;
95 
96     private Car mCar;
97     private ICarImpl mCarImpl;
98     private MockedVehicleHal mMockedVehicleHal;
99     private SystemInterface mFakeSystemInterface;
100     private MockedCarTestContext mMockedCarTestContext;
101 
102     private final List<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();
103     private final CarUserService mCarUserService = mock(CarUserService.class);
104     private final MockIOInterface mMockIOInterface = new MockIOInterface();
105     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
106     private final Map<VehiclePropConfigBuilder, VehicleHalPropertyHandler> mHalConfig =
107             new HashMap<>();
108     private final SparseArray<VehiclePropConfigBuilder> mPropToConfigBuilder = new SparseArray<>();
109     private final CarWatchdogService mCarWatchdogService = mock(CarWatchdogService.class);
110     private final FakeCarPowerPolicyDaemon mPowerPolicyDaemon = new FakeCarPowerPolicyDaemon();
111 
112     private MockitoSession mSession;
113 
createMockedVehicleHal()114     protected synchronized MockedVehicleHal createMockedVehicleHal() {
115         return new MockedVehicleHal();
116     }
117 
getMockedVehicleHal()118     protected synchronized MockedVehicleHal getMockedVehicleHal() {
119         return mMockedVehicleHal;
120     }
121 
getFakeSystemInterface()122     protected synchronized SystemInterface getFakeSystemInterface() {
123         return mFakeSystemInterface;
124     }
125 
configureMockedHal()126     protected synchronized void configureMockedHal() {
127     }
128 
129     /**
130      * Called after {@codeICarImpl} is created and before {@code ICarImpl.init()} is called.
131      *
132      * <p> Subclass that intend to apply spyOn() to the service under testing should override this.
133      * <pre class="prettyprint">
134      * @Override
135      * protected synchronized void spyOnBeforeCarImplInit() {
136      *     mServiceUnderTest = CarLocalServices.getService(CarXXXService.class);
137      *     ExtendedMockito.spyOn(mServiceUnderTest);
138      * }
139      * </pre>
140      */
spyOnBeforeCarImplInit()141     protected synchronized void spyOnBeforeCarImplInit() {
142     }
143 
getSystemInterfaceBuilder()144     protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
145         return Builder.newSystemInterface()
146                 .withSystemStateInterface(new MockSystemStateInterface())
147                 .withActivityManagerInterface(new MockActivityManagerInterface())
148                 .withDisplayInterface(new MockDisplayInterface())
149                 .withIOInterface(mMockIOInterface)
150                 .withStorageMonitoringInterface(new MockStorageMonitoringInterface())
151                 .withTimeInterface(new MockTimeInterface())
152                 .withWakeLockInterface(new MockWakeLockInterface());
153     }
154 
configureFakeSystemInterface()155     protected synchronized void configureFakeSystemInterface() {}
156 
configureResourceOverrides(MockResources resources)157     protected synchronized void configureResourceOverrides(MockResources resources) {
158         resources.overrideResource(com.android.car.R.string.instrumentClusterRendererService, "");
159         resources.overrideResource(com.android.car.R.bool.audioUseDynamicRouting, false);
160         resources.overrideResource(com.android.car.R.array.config_earlyStartupServices,
161                 new String[0]);
162         resources.overrideResource(com.android.car.R.integer.maxGarageModeRunningDurationInSecs,
163                 900);
164     }
165 
getContext()166     protected synchronized Context getContext() {
167         if (mMockedCarTestContext == null) {
168             mMockedCarTestContext = createMockedCarTestContext(
169                     InstrumentationRegistry.getInstrumentation().getTargetContext());
170         }
171         return mMockedCarTestContext;
172     }
173 
createMockedCarTestContext(Context context)174     protected MockedCarTestContext createMockedCarTestContext(Context context) {
175         return new MockedCarTestContext(context);
176     }
177 
getTestContext()178     protected Context getTestContext() {
179         return InstrumentationRegistry.getInstrumentation().getContext();
180     }
181 
getFlattenComponent(Class cls)182     protected String getFlattenComponent(Class cls) {
183         ComponentName cn = new ComponentName(getTestContext(), cls);
184         return cn.flattenToString();
185     }
186 
187     /** Child class should override this to configure mocking in different way */
createMockingSession()188     protected MockitoSession createMockingSession() {
189         return mockitoSession()
190                 .initMocks(this)
191                 .strictness(Strictness.LENIENT)
192                 .startMocking();
193     }
194 
195     @Before
196     @UiThreadTest
setUp()197     public void setUp() throws Exception {
198         Log.i(TAG, "setUp");
199 
200         mSession = createMockingSession();
201 
202         releaseRealCarService(getContext());
203 
204         mMockedVehicleHal = createMockedVehicleHal();
205         configureMockedHal();
206 
207         mFakeSystemInterface = getSystemInterfaceBuilder().build();
208         configureFakeSystemInterface();
209 
210         mMockedCarTestContext = (MockedCarTestContext) getContext();
211         configureResourceOverrides((MockResources) mMockedCarTestContext.getResources());
212 
213         doAnswer((invocation) -> {
214             UserLifecycleListener listener = invocation.getArgument(0);
215             Log.d(TAG, "Adding UserLifecycleListener: " + listener);
216             mUserLifecycleListeners.add(listener);
217             return null;
218         }).when(mCarUserService).addUserLifecycleListener(any());
219 
220         doAnswer((invocation) -> {
221             UserLifecycleListener listener = invocation.getArgument(0);
222             Log.d(TAG, "Removing UserLifecycleListener: " + listener);
223             mUserLifecycleListeners.remove(listener);
224             return null;
225         }).when(mCarUserService).removeUserLifecycleListener(any());
226 
227         // ICarImpl will register new CarLocalServices services.
228         // This prevents one test failure in tearDown from triggering assertion failure for single
229         // CarLocalServices service.
230         CarLocalServices.removeAllServices();
231 
232         // This should be done here as feature property is accessed inside the constructor.
233         initMockedHal();
234         mCarImpl = new ICarImpl(mMockedCarTestContext, mMockedVehicleHal, mFakeSystemInterface,
235                 "MockedCar", mCarUserService, mCarWatchdogService,
236                 mPowerPolicyDaemon);
237 
238         spyOnBeforeCarImplInit();
239         mCarImpl.init();
240         mCar = new Car(mMockedCarTestContext, mCarImpl, null /* handler */);
241     }
242 
243     @After
244     @UiThreadTest
tearDown()245     public void tearDown() throws Exception {
246         Log.i(TAG, "tearDown");
247 
248         try {
249             if (mCar != null) {
250                 mCar.disconnect();
251                 mCar = null;
252             }
253             if (mCarImpl != null) {
254                 mCarImpl.release();
255                 mCarImpl = null;
256             }
257             CarServiceUtils.finishAllHandlerTasks();
258             if (mMockIOInterface != null) {
259                 mMockIOInterface.tearDown();
260             }
261             mMockedVehicleHal = null;
262         } finally {
263             if (mSession != null) {
264                 mSession.finishMocking();
265             }
266         }
267     }
268 
injectErrorEvent(int propId, int areaId, int errorCode)269     public void injectErrorEvent(int propId, int areaId, int errorCode) {
270         mMockedVehicleHal.injectError(errorCode, propId, areaId);
271     }
272 
273     /**
274      * Creates new Car instance for testing.
275      */
createNewCar()276     public Car createNewCar() {
277         return new Car(mMockedCarTestContext, mCarImpl, null /* handler */);
278     }
279 
reinitializeMockedHal()280     protected synchronized void reinitializeMockedHal() throws Exception {
281         mCarImpl.release();
282         initMockedHal();
283     }
284 
initMockedHal()285     private synchronized void initMockedHal() throws Exception {
286         for (Map.Entry<VehiclePropConfigBuilder, VehicleHalPropertyHandler> entry
287                 : mHalConfig.entrySet()) {
288             mMockedVehicleHal.addProperty(entry.getKey().build(), entry.getValue());
289         }
290         mHalConfig.clear();
291     }
292 
addProperty(int propertyId, VehicleHalPropertyHandler propertyHandler)293     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
294             VehicleHalPropertyHandler propertyHandler) {
295         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
296         setConfigBuilder(builder, propertyHandler);
297         return builder;
298     }
299 
addProperty(int propertyId)300     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId) {
301         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
302         setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), null));
303         return builder;
304     }
305 
addProperty(int propertyId, VehiclePropValue value)306     protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
307             VehiclePropValue value) {
308         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
309         setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), value));
310         return builder;
311     }
312 
addStaticProperty(int propertyId, VehiclePropValue value)313     protected synchronized VehiclePropConfigBuilder addStaticProperty(int propertyId,
314             VehiclePropValue value) {
315         VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId)
316                 .setChangeMode(VehiclePropertyChangeMode.STATIC)
317                 .setAccess(VehiclePropertyAccess.READ);
318 
319         setConfigBuilder(builder, new StaticPropertyHandler(value));
320         return builder;
321     }
322 
setConfigBuilder(VehiclePropConfigBuilder builder, VehicleHalPropertyHandler propertyHandler)323     private void setConfigBuilder(VehiclePropConfigBuilder builder,
324             VehicleHalPropertyHandler propertyHandler) {
325         int propId = builder.build().prop;
326 
327         // Override previous property config if exists.
328         VehiclePropConfigBuilder prevBuilder = mPropToConfigBuilder.get(propId);
329         if (prevBuilder != null) {
330             mHalConfig.remove(prevBuilder);
331         }
332         mPropToConfigBuilder.put(propId, builder);
333         mHalConfig.put(builder, propertyHandler);
334     }
335 
getCar()336     protected synchronized android.car.Car getCar() {
337         return mCar;
338     }
339 
340     /*
341      * In order to eliminate interfering with real car service we will disable it. It will be
342      * enabled back in CarTestService when sCarServiceToken will go away (tests finish).
343      */
releaseRealCarService(Context context)344     private synchronized static void releaseRealCarService(Context context) throws Exception {
345         if (sRealCarServiceReleased) {
346             return;  // We just want to release it once.
347         }
348         sRealCarServiceReleased = true;  // To make sure it was called once.
349 
350         Object waitForConnection = new Object();
351         Car car = android.car.Car.createCar(context, new ServiceConnection() {
352             @Override
353             public void onServiceConnected(ComponentName name, IBinder service) {
354                 synchronized (waitForConnection) {
355                     waitForConnection.notify();
356                 }
357             }
358 
359             @Override
360             public void onServiceDisconnected(ComponentName name) { }
361         });
362 
363         car.connect();
364         synchronized (waitForConnection) {
365             if (!car.isConnected()) {
366                 waitForConnection.wait(DEFAULT_WAIT_TIMEOUT_MS);
367             }
368         }
369 
370         if (car.isConnected()) {
371             Log.i(TAG, "Connected to real car service");
372             CarTestManagerBinderWrapper binderWrapper =
373                     (CarTestManagerBinderWrapper) car.getCarManager(Car.TEST_SERVICE);
374             assertNotNull(binderWrapper);
375 
376             CarTestManager mgr = new CarTestManager(car, binderWrapper.binder);
377             mgr.stopCarService(sCarServiceToken);
378         }
379     }
380 
381     static final class MockActivityManagerInterface implements ActivityManagerInterface {
382         @Override
sendBroadcastAsUser(Intent intent, UserHandle user)383         public void sendBroadcastAsUser(Intent intent, UserHandle user) {
384             Log.d(TAG, "Broadcast intent: " + intent.getAction() + " as user: " + user);
385         }
386     }
387 
388     static final class MockDisplayInterface implements DisplayInterface {
389 
390         @Override
setDisplayBrightness(int brightness)391         public void setDisplayBrightness(int brightness) {}
392 
393         @Override
setDisplayState(boolean on)394         public void setDisplayState(boolean on) {}
395 
396         @Override
startDisplayStateMonitoring(CarPowerManagementService service)397         public void startDisplayStateMonitoring(CarPowerManagementService service) {}
398 
399         @Override
stopDisplayStateMonitoring()400         public void stopDisplayStateMonitoring() {}
401 
402         @Override
refreshDisplayBrightness()403         public void refreshDisplayBrightness() {}
404 
405         @Override
isDisplayEnabled()406         public boolean isDisplayEnabled() {
407             return true;
408         }
409     }
410 
411     static final class MockIOInterface implements IOInterface {
412         private TemporaryDirectory mFilesDir = null;
413 
414         @Override
getSystemCarDir()415         public File getSystemCarDir() {
416             if (mFilesDir == null) {
417                 try {
418                     mFilesDir = new TemporaryDirectory(TAG);
419                 } catch (IOException e) {
420                     Log.e(TAG, "failed to create temporary directory", e);
421                     fail("failed to create temporary directory. exception was: " + e);
422                 }
423             }
424             return mFilesDir.getDirectory();
425         }
426 
tearDown()427         public void tearDown() {
428             if (mFilesDir != null) {
429                 try {
430                     mFilesDir.close();
431                 } catch (Exception e) {
432                     Log.w(TAG, "could not remove temporary directory", e);
433                 }
434             }
435         }
436     }
437 
438     /**
439      * Special version of {@link ContextWrapper} that overrides {@method getResources} by returning
440      * a {@link MockResources}, so tests are free to set resources. This class represents an
441      * alternative of using Mockito spy (see b/148240178).
442      *
443      * Tests may specialize this class. If they decide so, then they are required to override
444      * {@method newMockedCarContext} to provide their own context.
445      */
446     protected static class MockedCarTestContext extends ContextWrapper {
447 
448         private final Resources mMockedResources;
449 
MockedCarTestContext(Context base)450         MockedCarTestContext(Context base) {
451             super(base);
452             mMockedResources = new MockResources(base.getResources());
453         }
454 
455         @Override
getResources()456         public Resources getResources() {
457             return mMockedResources;
458         }
459     }
460 
461     protected static final class MockResources extends Resources {
462         private final HashMap<Integer, Boolean> mBooleanOverrides = new HashMap<>();
463         private final HashMap<Integer, Integer> mIntegerOverrides = new HashMap<>();
464         private final HashMap<Integer, String> mStringOverrides = new HashMap<>();
465         private final HashMap<Integer, String[]> mStringArrayOverrides = new HashMap<>();
466 
MockResources(Resources resources)467         MockResources(Resources resources) {
468             super(resources.getAssets(),
469                     resources.getDisplayMetrics(),
470                     resources.getConfiguration());
471         }
472 
473         @Override
getBoolean(int id)474         public boolean getBoolean(int id) {
475             return mBooleanOverrides.getOrDefault(id,
476                     super.getBoolean(id));
477         }
478 
479         @Override
getInteger(int id)480         public int getInteger(int id) {
481             return mIntegerOverrides.getOrDefault(id,
482                     super.getInteger(id));
483         }
484 
485         @Override
getString(int id)486         public String getString(int id) {
487             return mStringOverrides.getOrDefault(id,
488                     super.getString(id));
489         }
490 
491         @Override
getStringArray(int id)492         public String[] getStringArray(int id) {
493             return mStringArrayOverrides.getOrDefault(id,
494                     super.getStringArray(id));
495         }
496 
overrideResource(int id, boolean value)497         public MockResources overrideResource(int id, boolean value) {
498             mBooleanOverrides.put(id, value);
499             return this;
500         }
501 
overrideResource(int id, int value)502         public MockResources overrideResource(int id, int value) {
503             mIntegerOverrides.put(id, value);
504             return this;
505         }
506 
overrideResource(int id, String value)507         public MockResources overrideResource(int id, String value) {
508             mStringOverrides.put(id, value);
509             return this;
510         }
511 
overrideResource(int id, String[] value)512         public MockResources overrideResource(int id, String[] value) {
513             mStringArrayOverrides.put(id, value);
514             return this;
515         }
516     }
517 
518     static final class MockStorageMonitoringInterface implements StorageMonitoringInterface {}
519 
520     static final class MockSystemStateInterface implements SystemStateInterface {
521         @Override
shutdown()522         public void shutdown() {}
523 
524         @Override
enterDeepSleep()525         public boolean enterDeepSleep() {
526             return true;
527         }
528 
529         @Override
scheduleActionForBootCompleted(Runnable action, Duration delay)530         public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
531     }
532 
533     static final class MockTimeInterface implements TimeInterface {
534 
535         @Override
scheduleAction(Runnable r, long delayMs)536         public void scheduleAction(Runnable r, long delayMs) {}
537 
538         @Override
cancelAllActions()539         public void cancelAllActions() {}
540     }
541 
542     static final class MockWakeLockInterface implements WakeLockInterface {
543 
544         @Override
releaseAllWakeLocks()545         public void releaseAllWakeLocks() {}
546 
547         @Override
switchToPartialWakeLock()548         public void switchToPartialWakeLock() {}
549 
550         @Override
switchToFullWakeLock()551         public void switchToFullWakeLock() {}
552     }
553 
554     static final class FakeCarPowerPolicyDaemon extends ICarPowerPolicySystemNotification.Stub {
555         @Override
notifyCarServiceReady()556         public PolicyState notifyCarServiceReady() {
557             // do nothing
558             return null;
559         }
560 
561         @Override
notifyPowerPolicyChange(String policyId)562         public void notifyPowerPolicyChange(String policyId) {
563             // do nothing
564         }
565 
566         @Override
notifyPowerPolicyDefinition(String policyId, String[] enabledComponents, String[] disabledComponents)567         public void notifyPowerPolicyDefinition(String policyId, String[] enabledComponents,
568                 String[] disabledComponents) {
569             // do nothing
570         }
571     }
572 }
573