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.test; 17 18 import android.car.test.CarTestManager; 19 import android.car.test.CarTestManagerBinderWrapper; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.ServiceConnection; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 25 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 26 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; 27 import android.os.Binder; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.test.AndroidTestCase; 32 import android.util.Log; 33 34 import com.android.car.CarPowerManagementService; 35 import com.android.car.ICarImpl; 36 import com.android.car.SystemInterface; 37 import com.android.car.vehiclehal.test.MockedVehicleHal; 38 import com.android.car.vehiclehal.test.MockedVehicleHal.DefaultPropertyHandler; 39 import com.android.car.vehiclehal.test.MockedVehicleHal.StaticPropertyHandler; 40 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 41 import com.android.car.vehiclehal.test.VehiclePropConfigBuilder; 42 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.Map; 46 import java.util.concurrent.Semaphore; 47 import java.util.concurrent.TimeUnit; 48 49 /** 50 * Base class for testing with mocked vehicle HAL (=car). 51 * It is up to each app to start emulation by getMockedVehicleHal().start() as there will be 52 * per test set up that should be done before starting. 53 */ 54 public class MockedCarTestBase extends AndroidTestCase { 55 private static final String TAG = MockedCarTestBase.class.getSimpleName(); 56 static final long DEFAULT_WAIT_TIMEOUT_MS = 3000; 57 static final long SHORT_WAIT_TIMEOUT_MS = 500; 58 59 private android.car.Car mCar; 60 private ICarImpl mCarImpl; 61 private MockedVehicleHal mMockedVehicleHal; 62 private FakeSystemInterface mFakeSystemInterface; 63 64 private final Semaphore mWaitForMain = new Semaphore(0); 65 private final Handler mMainHandler = new Handler(Looper.getMainLooper()); 66 67 private static final IBinder mCarServiceToken = new Binder(); 68 private static boolean mRealCarServiceReleased = false; 69 70 protected synchronized MockedVehicleHal createMockedVehicleHal() { 71 return new MockedVehicleHal(); 72 } 73 74 protected synchronized MockedVehicleHal getMockedVehicleHal() { 75 return mMockedVehicleHal; 76 } 77 78 protected synchronized void configureMockedHal() { 79 } 80 81 @Override 82 protected synchronized void setUp() throws Exception { 83 super.setUp(); 84 85 releaseRealCarService(getContext()); 86 87 mMockedVehicleHal = createMockedVehicleHal(); 88 configureMockedHal(); 89 90 mFakeSystemInterface = new FakeSystemInterface(); 91 Context context = getCarServiceContext(); 92 mCarImpl = new ICarImpl(context, mMockedVehicleHal, mFakeSystemInterface, 93 null /* error notifier */); 94 95 initMockedHal(false /* no need to release */); 96 97 mCar = new android.car.Car(context, mCarImpl, null /* handler */); 98 } 99 100 @Override 101 protected synchronized void tearDown() throws Exception { 102 super.tearDown(); 103 104 mCar.disconnect(); 105 mCarImpl.release(); 106 } 107 108 protected Context getCarServiceContext() throws NameNotFoundException { 109 return getContext() 110 .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY); 111 } 112 113 protected synchronized void reinitializeMockedHal() { 114 initMockedHal(true /* release */); 115 } 116 117 private synchronized void initMockedHal(boolean release) { 118 if (release) { 119 mCarImpl.release(); 120 } 121 122 for (Map.Entry<VehiclePropConfigBuilder, VehicleHalPropertyHandler> entry 123 : mHalConfig.entrySet()) { 124 mMockedVehicleHal.addProperty(entry.getKey().build(), entry.getValue()); 125 } 126 mHalConfig.clear(); 127 mCarImpl.init(); 128 } 129 130 private final Map<VehiclePropConfigBuilder, VehicleHalPropertyHandler> mHalConfig = 131 new HashMap<>(); 132 133 protected synchronized VehiclePropConfigBuilder addProperty(int propertyId, 134 VehicleHalPropertyHandler propertyHandler) { 135 VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId); 136 mHalConfig.put(builder, propertyHandler); 137 return builder; 138 } 139 140 protected synchronized VehiclePropConfigBuilder addProperty(int propertyId) { 141 VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId); 142 mHalConfig.put(builder, new DefaultPropertyHandler(builder.build(), null)); 143 return builder; 144 } 145 146 protected synchronized VehiclePropConfigBuilder addProperty(int propertyId, 147 VehiclePropValue value) { 148 VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId); 149 mHalConfig.put(builder, new DefaultPropertyHandler(builder.build(), value)); 150 return builder; 151 } 152 153 protected synchronized VehiclePropConfigBuilder addStaticProperty(int propertyId, 154 VehiclePropValue value) { 155 VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId) 156 .setChangeMode(VehiclePropertyChangeMode.STATIC) 157 .setAccess(VehiclePropertyAccess.READ); 158 159 mHalConfig.put(builder, new StaticPropertyHandler(value)); 160 return builder; 161 } 162 163 protected synchronized android.car.Car getCar() { 164 return mCar; 165 } 166 167 protected void runOnMain(final Runnable r) { 168 mMainHandler.post(r); 169 } 170 171 protected void runOnMainSync(final Runnable r) throws Exception { 172 mMainHandler.post(new Runnable() { 173 @Override 174 public void run() { 175 r.run(); 176 mWaitForMain.release(); 177 } 178 }); 179 mWaitForMain.acquire(); 180 } 181 182 protected boolean waitForFakeDisplayState(boolean expectedState) throws Exception { 183 return mFakeSystemInterface.waitForDisplayState(expectedState, SHORT_WAIT_TIMEOUT_MS); 184 } 185 186 public static <T> void assertArrayEquals(T[] expected, T[] actual) { 187 if (!Arrays.equals(expected, actual)) { 188 fail("expected:<" + Arrays.toString(expected) + 189 "> but was:<" + Arrays.toString(actual) + ">"); 190 } 191 } 192 193 public static void assertArrayEquals(int[] expected, int[] actual) { 194 if (!Arrays.equals(expected, actual)) { 195 fail("expected:<" + Arrays.toString(expected) + 196 "> but was:<" + Arrays.toString(actual) + ">"); 197 } 198 } 199 200 /* 201 * In order to eliminate interfering with real car service we will disable it. It will be 202 * enabled back in CarTestService when mCarServiceToken will go away (tests finish). 203 */ 204 private synchronized static void releaseRealCarService(Context context) throws Exception { 205 if (mRealCarServiceReleased) { 206 return; // We just want to release it once. 207 } 208 209 mRealCarServiceReleased = true; // To make sure it was called once. 210 211 Object waitForConnection = new Object(); 212 android.car.Car car = android.car.Car.createCar(context, new ServiceConnection() { 213 @Override 214 public void onServiceConnected(ComponentName name, IBinder service) { 215 synchronized (waitForConnection) { 216 waitForConnection.notify(); 217 } 218 } 219 220 @Override 221 public void onServiceDisconnected(ComponentName name) { } 222 }); 223 224 car.connect(); 225 synchronized (waitForConnection) { 226 if (!car.isConnected()) { 227 waitForConnection.wait(DEFAULT_WAIT_TIMEOUT_MS); 228 } 229 } 230 231 if (car.isConnected()) { 232 Log.i(TAG, "Connected to real car service"); 233 CarTestManagerBinderWrapper binderWrapper = 234 (CarTestManagerBinderWrapper) car.getCarManager(android.car.Car.TEST_SERVICE); 235 assertNotNull(binderWrapper); 236 237 CarTestManager mgr = new CarTestManager(binderWrapper.binder); 238 mgr.stopCarService(mCarServiceToken); 239 } 240 } 241 242 private static class FakeSystemInterface extends SystemInterface { 243 244 private boolean mDisplayOn = true; 245 private final Semaphore mDisplayStateWait = new Semaphore(0); 246 247 @Override 248 public synchronized void setDisplayState(boolean on) { 249 mDisplayOn = on; 250 mDisplayStateWait.release(); 251 } 252 253 boolean waitForDisplayState(boolean expectedState, long timeoutMs) 254 throws Exception { 255 if (expectedState == mDisplayOn) { 256 return true; 257 } 258 mDisplayStateWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS); 259 return expectedState == mDisplayOn; 260 } 261 262 @Override 263 public void releaseAllWakeLocks() { 264 } 265 266 @Override 267 public void shutdown() { } 268 269 @Override 270 public void enterDeepSleep(int wakeupTimeSec) { } 271 272 @Override 273 public boolean isSystemSupportingDeepSleep() { 274 return false; 275 } 276 277 @Override 278 public void switchToPartialWakeLock() { 279 } 280 281 @Override 282 public void switchToFullWakeLock() { 283 } 284 285 @Override 286 public void startDisplayStateMonitoring(CarPowerManagementService service) { 287 } 288 289 @Override 290 public void stopDisplayStateMonitoring() { 291 } 292 293 @Override 294 public boolean isWakeupCausedByTimer() { return false; } 295 } 296 } 297