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