• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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