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 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