1 /* 2 * Copyright (C) 2016 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 17 package com.android.car; 18 19 import static org.mockito.ArgumentMatchers.anyInt; 20 import static org.mockito.Mockito.doReturn; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.times; 23 import static org.mockito.Mockito.verify; 24 25 import android.car.hardware.power.CarPowerManager.CarPowerStateListener; 26 import android.car.hardware.power.ICarPowerStateListener; 27 import android.car.userlib.CarUserManagerHelper; 28 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq; 29 import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam; 30 import android.os.RemoteException; 31 import android.test.AndroidTestCase; 32 import android.test.suitebuilder.annotation.SmallTest; 33 import android.util.Log; 34 35 import com.android.car.hal.PowerHalService; 36 import com.android.car.hal.PowerHalService.PowerState; 37 import com.android.car.systeminterface.DisplayInterface; 38 import com.android.car.systeminterface.IOInterface; 39 import com.android.car.systeminterface.SystemInterface; 40 import com.android.car.systeminterface.SystemStateInterface; 41 import com.android.car.systeminterface.WakeLockInterface; 42 import com.android.car.test.utils.TemporaryDirectory; 43 44 import java.io.File; 45 import java.io.IOException; 46 import java.time.Duration; 47 import java.util.concurrent.CompletableFuture; 48 import java.util.concurrent.Semaphore; 49 import java.util.concurrent.TimeUnit; 50 51 @SmallTest 52 public class CarPowerManagementServiceTest extends AndroidTestCase { 53 private static final String TAG = CarPowerManagementServiceTest.class.getSimpleName(); 54 private static final long WAIT_TIMEOUT_MS = 2000; 55 private static final long WAIT_TIMEOUT_LONG_MS = 5000; 56 57 private final MockDisplayInterface mDisplayInterface = new MockDisplayInterface(); 58 private final MockSystemStateInterface mSystemStateInterface = new MockSystemStateInterface(); 59 private final MockWakeLockInterface mWakeLockInterface = new MockWakeLockInterface(); 60 private final MockIOInterface mIOInterface = new MockIOInterface(); 61 private final PowerSignalListener mPowerSignalListener = new PowerSignalListener(); 62 private CarUserManagerHelper mCarUserManagerHelper; 63 64 private MockedPowerHalService mPowerHal; 65 private SystemInterface mSystemInterface; 66 private CarPowerManagementService mService; 67 private CompletableFuture<Void> mFuture; 68 69 @Override setUp()70 protected void setUp() throws Exception { 71 super.setUp(); 72 mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/, 73 true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/); 74 mSystemInterface = SystemInterface.Builder.defaultSystemInterface(getContext()) 75 .withDisplayInterface(mDisplayInterface) 76 .withSystemStateInterface(mSystemStateInterface) 77 .withWakeLockInterface(mWakeLockInterface) 78 .withIOInterface(mIOInterface).build(); 79 mCarUserManagerHelper = mock(CarUserManagerHelper.class); 80 doReturn(true).when(mCarUserManagerHelper).switchToUserId(anyInt()); 81 doReturn(10).when(mCarUserManagerHelper).getInitialUser(); 82 doReturn(10).when(mCarUserManagerHelper).getCurrentForegroundUserId(); 83 } 84 85 @Override tearDown()86 protected void tearDown() throws Exception { 87 super.tearDown(); 88 if (mService != null) { 89 mService.release(); 90 } 91 mIOInterface.tearDown(); 92 } 93 94 /** 95 * Helper method to create mService and initialize a test case 96 */ initTest(int wakeupTime)97 private void initTest(int wakeupTime) throws Exception { 98 mService = new CarPowerManagementService(getContext(), mPowerHal, mSystemInterface, 99 mCarUserManagerHelper); 100 mService.init(); 101 CarPowerManagementService.setShutdownPrepareTimeout(0); 102 mPowerHal.setSignalListener(mPowerSignalListener); 103 if (wakeupTime > 0) { 104 registerListenerToService(); 105 mService.scheduleNextWakeupTime(wakeupTime); 106 } 107 assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0); 108 } 109 testBootComplete()110 public void testBootComplete() throws Exception { 111 initTest(0); 112 } 113 testDisplayOn()114 public void testDisplayOn() throws Exception { 115 // start with display off 116 mSystemInterface.setDisplayState(false); 117 mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS); 118 initTest(0); 119 // Transition to ON state 120 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0)); 121 122 // display should be turned on as it started with off state. 123 assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 124 } 125 testShutdown()126 public void testShutdown() throws Exception { 127 initTest(0); 128 129 // Transition to ON state 130 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0)); 131 assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 132 133 mPowerHal.setCurrentPowerState( 134 new PowerState( 135 VehicleApPowerStateReq.SHUTDOWN_PREPARE, 136 VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY)); 137 // Since modules have to manually schedule next wakeup, we should not schedule next wakeup 138 // To test module behavior, we need to actually implement mock listener module. 139 assertStateReceived(PowerHalService.SET_SHUTDOWN_START, 0); 140 assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 141 mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS); 142 mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS); 143 } 144 testShutdownWithProcessing()145 public void testShutdownWithProcessing() throws Exception { 146 final int wakeupTime = 100; 147 initTest(wakeupTime); 148 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 0)); 149 assertStateReceivedForShutdownOrSleepWithPostpone( 150 PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_LONG_MS, wakeupTime); 151 mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS); 152 // Send the finished signal 153 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0)); 154 mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS); 155 } 156 testSleepEntryAndWakeup()157 public void testSleepEntryAndWakeup() throws Exception { 158 final int wakeupTime = 100; 159 initTest(wakeupTime); 160 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 161 VehicleApPowerStateShutdownParam.CAN_SLEEP)); 162 assertStateReceivedForShutdownOrSleepWithPostpone( 163 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime); 164 mPowerSignalListener.waitForSleepEntry(WAIT_TIMEOUT_MS); 165 // Send the finished signal from HAL to CPMS 166 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0)); 167 mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS); 168 assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0); 169 mPowerSignalListener.waitForSleepExit(WAIT_TIMEOUT_MS); 170 } 171 testSleepEntryAndWakeUpForProcessing()172 public void testSleepEntryAndWakeUpForProcessing() throws Exception { 173 final int wakeupTime = 100; 174 initTest(wakeupTime); 175 176 // set up for user switching after display on 177 final int currentUserId = 10; 178 final int newUserId = 11; 179 doReturn(newUserId).when(mCarUserManagerHelper).getInitialUser(); 180 doReturn(currentUserId).when(mCarUserManagerHelper).getCurrentForegroundUserId(); 181 182 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0)); 183 assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 184 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 185 VehicleApPowerStateShutdownParam.CAN_SLEEP)); 186 assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 187 assertStateReceivedForShutdownOrSleepWithPostpone( 188 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime); 189 mPowerSignalListener.waitForSleepEntry(WAIT_TIMEOUT_MS); 190 // Send the finished signal 191 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0)); 192 mSystemStateInterface.setWakeupCausedByTimer(true); 193 mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS); 194 assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0); 195 mPowerSignalListener.waitForSleepExit(WAIT_TIMEOUT_MS); 196 mService.scheduleNextWakeupTime(wakeupTime); 197 // second processing after wakeup 198 assertFalse(mDisplayInterface.getDisplayState()); 199 // do not skip user switching part. 200 mService.clearIsBooting(); 201 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0)); 202 assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)); 203 // user switching should have been requested. 204 verify(mCarUserManagerHelper, times(1)).switchToUserId(newUserId); 205 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 206 VehicleApPowerStateShutdownParam.CAN_SLEEP)); 207 assertStateReceivedForShutdownOrSleepWithPostpone( 208 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, wakeupTime); 209 mPowerSignalListener.waitForSleepEntry(WAIT_TIMEOUT_MS); 210 mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0)); 211 // PM will shutdown system as it was not woken-up due to timer and it is not power on. 212 mSystemStateInterface.setWakeupCausedByTimer(false); 213 mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS); 214 // Since we just woke up from shutdown, wake up time will be 0 215 assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0); 216 assertFalse(mDisplayInterface.getDisplayState()); 217 } 218 registerListenerToService()219 private void registerListenerToService() { 220 ICarPowerStateListener listenerToService = new ICarPowerStateListener.Stub() { 221 @Override 222 public void onStateChanged(int state) throws RemoteException { 223 if (state == CarPowerStateListener.SHUTDOWN_ENTER 224 || state == CarPowerStateListener.SUSPEND_ENTER) { 225 mFuture = new CompletableFuture<>(); 226 mFuture.whenComplete((res, ex) -> { 227 if (ex == null) { 228 mService.finished(this); 229 } 230 }); 231 } else { 232 mFuture = null; 233 } 234 } 235 }; 236 mService.registerListener(listenerToService); 237 } 238 assertStateReceived(int expectedState, int expectedParam)239 private void assertStateReceived(int expectedState, int expectedParam) throws Exception { 240 int[] state = mPowerHal.waitForSend(WAIT_TIMEOUT_MS); 241 assertEquals(expectedState, state[0]); 242 assertEquals(expectedParam, state[1]); 243 } 244 assertStateReceivedForShutdownOrSleepWithPostpone( int lastState, long timeoutMs, int expectedParamForShutdownOrSuspend)245 private void assertStateReceivedForShutdownOrSleepWithPostpone( 246 int lastState, long timeoutMs, int expectedParamForShutdownOrSuspend) throws Exception { 247 while (true) { 248 if (mFuture != null && !mFuture.isDone()) { 249 mFuture.complete(null); 250 } 251 int[] state = mPowerHal.waitForSend(timeoutMs); 252 if (state[0] == PowerHalService.SET_SHUTDOWN_POSTPONE) { 253 continue; 254 } 255 if (state[0] == lastState) { 256 assertEquals(expectedParamForShutdownOrSuspend, state[1]); 257 return; 258 } 259 } 260 } 261 waitForSemaphore(Semaphore semaphore, long timeoutMs)262 private static void waitForSemaphore(Semaphore semaphore, long timeoutMs) 263 throws InterruptedException { 264 if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 265 throw new IllegalStateException("timeout"); 266 } 267 } 268 269 private static final class MockDisplayInterface implements DisplayInterface { 270 private boolean mDisplayOn = true; 271 private final Semaphore mDisplayStateWait = new Semaphore(0); 272 273 @Override setDisplayBrightness(int brightness)274 public void setDisplayBrightness(int brightness) {} 275 276 @Override setDisplayState(boolean on)277 public synchronized void setDisplayState(boolean on) { 278 mDisplayOn = on; 279 mDisplayStateWait.release(); 280 } 281 getDisplayState()282 public synchronized boolean getDisplayState() { 283 return mDisplayOn; 284 } 285 waitForDisplayStateChange(long timeoutMs)286 public boolean waitForDisplayStateChange(long timeoutMs) throws Exception { 287 waitForSemaphore(mDisplayStateWait, timeoutMs); 288 return mDisplayOn; 289 } 290 291 @Override startDisplayStateMonitoring(CarPowerManagementService service)292 public void startDisplayStateMonitoring(CarPowerManagementService service) {} 293 294 @Override stopDisplayStateMonitoring()295 public void stopDisplayStateMonitoring() {} 296 297 @Override refreshDisplayBrightness()298 public void refreshDisplayBrightness() {} 299 300 @Override reconfigureSecondaryDisplays()301 public void reconfigureSecondaryDisplays() {} 302 } 303 304 private static final class MockSystemStateInterface implements SystemStateInterface { 305 private final Semaphore mShutdownWait = new Semaphore(0); 306 private final Semaphore mSleepWait = new Semaphore(0); 307 private final Semaphore mSleepExitWait = new Semaphore(0); 308 private boolean mWakeupCausedByTimer = false; 309 310 @Override shutdown()311 public void shutdown() { 312 mShutdownWait.release(); 313 } 314 waitForShutdown(long timeoutMs)315 public void waitForShutdown(long timeoutMs) throws Exception { 316 waitForSemaphore(mShutdownWait, timeoutMs); 317 } 318 319 @Override enterDeepSleep()320 public boolean enterDeepSleep() { 321 mSleepWait.release(); 322 try { 323 mSleepExitWait.acquire(); 324 } catch (InterruptedException e) { 325 } 326 return true; 327 } 328 waitForSleepEntryAndWakeup(long timeoutMs)329 public void waitForSleepEntryAndWakeup(long timeoutMs) throws Exception { 330 waitForSemaphore(mSleepWait, timeoutMs); 331 mSleepExitWait.release(); 332 } 333 334 @Override scheduleActionForBootCompleted(Runnable action, Duration delay)335 public void scheduleActionForBootCompleted(Runnable action, Duration delay) {} 336 337 @Override isWakeupCausedByTimer()338 public boolean isWakeupCausedByTimer() { 339 Log.i(TAG, "isWakeupCausedByTimer:" + mWakeupCausedByTimer); 340 return mWakeupCausedByTimer; 341 } 342 setWakeupCausedByTimer(boolean set)343 public synchronized void setWakeupCausedByTimer(boolean set) { 344 mWakeupCausedByTimer = set; 345 } 346 347 @Override isSystemSupportingDeepSleep()348 public boolean isSystemSupportingDeepSleep() { 349 return true; 350 } 351 } 352 353 private static final class MockWakeLockInterface implements WakeLockInterface { 354 355 @Override releaseAllWakeLocks()356 public void releaseAllWakeLocks() {} 357 358 @Override switchToPartialWakeLock()359 public void switchToPartialWakeLock() {} 360 361 @Override switchToFullWakeLock()362 public void switchToFullWakeLock() {} 363 } 364 365 private static final class MockIOInterface implements IOInterface { 366 private TemporaryDirectory mFilesDir; 367 368 @Override getSystemCarDir()369 public File getSystemCarDir() { 370 if (mFilesDir == null) { 371 try { 372 mFilesDir = new TemporaryDirectory(TAG); 373 } catch (IOException e) { 374 Log.e(TAG, "failed to create temporary directory", e); 375 fail("failed to create temporary directory. exception was: " + e); 376 } 377 } 378 return mFilesDir.getDirectory(); 379 } 380 tearDown()381 public void tearDown() { 382 if (mFilesDir != null) { 383 try { 384 mFilesDir.close(); 385 } catch (Exception e) { 386 Log.w(TAG, "could not remove temporary directory", e); 387 } 388 } 389 } 390 } 391 392 private class PowerSignalListener implements MockedPowerHalService.SignalListener { 393 private final Semaphore mShutdownWait = new Semaphore(0); 394 private final Semaphore mSleepEntryWait = new Semaphore(0); 395 private final Semaphore mSleepExitWait = new Semaphore(0); 396 waitForSleepExit(long timeoutMs)397 public void waitForSleepExit(long timeoutMs) throws Exception { 398 waitForSemaphore(mSleepExitWait, timeoutMs); 399 } 400 waitForShutdown(long timeoutMs)401 public void waitForShutdown(long timeoutMs) throws Exception { 402 waitForSemaphore(mShutdownWait, timeoutMs); 403 } 404 waitForSleepEntry(long timeoutMs)405 public void waitForSleepEntry(long timeoutMs) throws Exception { 406 waitForSemaphore(mSleepEntryWait, timeoutMs); 407 } 408 409 @Override sendingSignal(int signal)410 public void sendingSignal(int signal) { 411 if (signal == PowerHalService.SET_SHUTDOWN_START) { 412 mShutdownWait.release(); 413 return; 414 } 415 if (signal == PowerHalService.SET_DEEP_SLEEP_ENTRY) { 416 mSleepEntryWait.release(); 417 return; 418 } 419 if (signal == PowerHalService.SET_DEEP_SLEEP_EXIT) { 420 mSleepExitWait.release(); 421 return; 422 } 423 } 424 } 425 } 426