MockedCarTestBase.java revision d3cb98e76080151bdfb5085b8971b5070f11d28b
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 */
16package com.android.car;
17
18import static org.junit.Assert.assertNotNull;
19import static org.junit.Assert.fail;
20
21import android.car.Car;
22import android.car.test.CarTestManager;
23import android.car.test.CarTestManagerBinderWrapper;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.ContextWrapper;
27import android.content.ServiceConnection;
28import android.content.pm.PackageManager.NameNotFoundException;
29import android.content.res.Resources;
30import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
31import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
33import android.os.Binder;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Looper;
37import android.support.test.InstrumentationRegistry;
38import android.support.test.annotation.UiThreadTest;
39import android.util.Log;
40import android.util.SparseArray;
41
42import com.android.car.pm.CarPackageManagerService;
43import com.android.car.systeminterface.DisplayInterface;
44import com.android.car.systeminterface.IOInterface;
45import com.android.car.systeminterface.StorageMonitoringInterface;
46import com.android.car.systeminterface.SystemInterface;
47import com.android.car.systeminterface.SystemInterface.Builder;
48import com.android.car.systeminterface.SystemStateInterface;
49import com.android.car.systeminterface.TimeInterface;
50import com.android.car.systeminterface.WakeLockInterface;
51import com.android.car.test.CarServiceTestApp;
52import com.android.car.test.utils.TemporaryDirectory;
53import com.android.car.vehiclehal.test.MockedVehicleHal;
54import com.android.car.vehiclehal.test.MockedVehicleHal.DefaultPropertyHandler;
55import com.android.car.vehiclehal.test.MockedVehicleHal.StaticPropertyHandler;
56import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
57import com.android.car.vehiclehal.test.VehiclePropConfigBuilder;
58
59import org.junit.After;
60import org.junit.Before;
61
62import java.io.File;
63import java.io.IOException;
64import java.time.Duration;
65import java.util.HashMap;
66import 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 */
73public 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