blob: 6100dbc760a2b64d982e96fe9c2512be0eb46f25 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.hardware.power;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import android.car.Car;
import android.car.hardware.power.CarPowerManager;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.test.mocks.JavaMockitoHelper;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.car.CarPowerManagementService;
import com.android.car.MockedPowerHalService;
import com.android.car.R;
import com.android.car.hal.PowerHalService;
import com.android.car.hal.PowerHalService.PowerState;
import com.android.car.systeminterface.DisplayInterface;
import com.android.car.systeminterface.SystemInterface;
import com.android.car.systeminterface.SystemStateInterface;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
@SmallTest
public class CarPowerManagerUnitTest extends AbstractExtendedMockitoTestCase {
private static final String TAG = CarPowerManagerUnitTest.class.getSimpleName();
private static final long WAIT_TIMEOUT_MS = 5_000;
private static final long WAIT_TIMEOUT_LONG_MS = 10_000;
// A shorter value for use when the test is expected to time out
private static final long WAIT_WHEN_TIMEOUT_EXPECTED_MS = 100;
private final MockDisplayInterface mDisplayInterface = new MockDisplayInterface();
private final MockSystemStateInterface mSystemStateInterface = new MockSystemStateInterface();
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private MockedPowerHalService mPowerHal;
private SystemInterface mSystemInterface;
private CarPowerManagementService mService;
private CarPowerManager mCarPowerManager;
@Mock
private Resources mResources;
@Mock
private Car mCar;
@Before
public void setUp() throws Exception {
mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/,
true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/);
mSystemInterface = SystemInterface.Builder.defaultSystemInterface(mContext)
.withDisplayInterface(mDisplayInterface)
.withSystemStateInterface(mSystemStateInterface)
.build();
setService();
mCarPowerManager = new CarPowerManager(mCar, mService);
}
@After
public void tearDown() throws Exception {
if (mService != null) {
mService.release();
}
}
@Test
public void testRequestShutdownOnNextSuspend_positive() throws Exception {
setPowerOn();
// Tell it to shutdown
mCarPowerManager.requestShutdownOnNextSuspend();
// Request suspend
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
// Verify shutdown
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START, 0);
}
@Test
public void testRequestShutdownOnNextSuspend_negative() throws Exception {
setPowerOn();
// Do not tell it to shutdown
// Request suspend
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
// Verify suspend
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
}
@Test
public void testScheduleNextWakeupTime() throws Exception {
setPowerOn();
int wakeTime = 1234;
mCarPowerManager.scheduleNextWakeupTime(wakeTime);
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
// Verify that we suspended with the requested wake-up time
assertStateReceivedForShutdownOrSleepWithPostpone(
PowerHalService.SET_DEEP_SLEEP_ENTRY, wakeTime);
}
@Test
public void testSetListener() throws Exception {
setPowerOn();
WaitablePowerStateListener listener = new WaitablePowerStateListener(2);
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
int finalState = listener.await();
assertThat(finalState).isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
}
@Test
public void testSetListenerWithCompletion() throws Exception {
setPowerOn();
WaitablePowerStateListenerWithCompletion listener =
new WaitablePowerStateListenerWithCompletion(2);
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
int finalState = listener.await();
assertThat(finalState).isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
}
@Test
public void testClearListener() throws Exception {
setPowerOn();
// Set a listener with a short timeout, because we expect the timeout to happen
WaitablePowerStateListener listener =
new WaitablePowerStateListener(1, WAIT_WHEN_TIMEOUT_EXPECTED_MS);
mCarPowerManager.clearListener();
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
// Verify that the listener didn't run
assertThrows(IllegalStateException.class, () -> listener.await());
}
@Test
public void testGetPowerState() throws Exception {
setPowerOn();
assertThat(mCarPowerManager.getPowerState()).isEqualTo(PowerHalService.SET_ON);
// Request suspend
setPowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE,
VehicleApPowerStateShutdownParam.CAN_SLEEP);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
assertThat(mCarPowerManager.getPowerState())
.isEqualTo(PowerHalService.SET_DEEP_SLEEP_ENTRY);
}
/**
* Helper method to create mService and initialize a test case
*/
private void setService() throws Exception {
Log.i(TAG, "setService(): overridden overlay properties: "
+ "config_disableUserSwitchDuringResume="
+ mResources.getBoolean(R.bool.config_disableUserSwitchDuringResume)
+ ", maxGarageModeRunningDurationInSecs="
+ mResources.getInteger(R.integer.maxGarageModeRunningDurationInSecs));
mService = new CarPowerManagementService(mContext, mResources, mPowerHal,
mSystemInterface, null, null, null);
mService.init();
mService.setShutdownTimersForTest(0, 0);
assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0);
}
private void assertStateReceived(int expectedState, int expectedParam) throws Exception {
int[] state = mPowerHal.waitForSend(WAIT_TIMEOUT_MS);
assertThat(state).asList().containsExactly(expectedState, expectedParam).inOrder();
}
/**
* Helper method to get the system into ON
*/
private void setPowerOn() throws Exception {
setPowerState(VehicleApPowerStateReq.ON, 0);
assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isTrue();
}
/**
* Helper to set the PowerHal state
*
* @param stateEnum Requested state enum
* @param stateParam Addition state parameter
*/
private void setPowerState(int stateEnum, int stateParam) {
mPowerHal.setCurrentPowerState(new PowerState(stateEnum, stateParam));
}
private void assertStateReceivedForShutdownOrSleepWithPostpone(
int lastState, int stateParameter) throws Exception {
long startTime = System.currentTimeMillis();
while (true) {
int[] state = mPowerHal.waitForSend(WAIT_TIMEOUT_LONG_MS);
if (state[0] == lastState) {
assertThat(state[1]).isEqualTo(stateParameter);
return;
}
assertThat(state[0]).isEqualTo(PowerHalService.SET_SHUTDOWN_POSTPONE);
assertThat(System.currentTimeMillis() - startTime).isLessThan(WAIT_TIMEOUT_LONG_MS);
}
}
private static final class MockDisplayInterface implements DisplayInterface {
private boolean mDisplayOn = true;
private final Semaphore mDisplayStateWait = new Semaphore(0);
@Override
public void setDisplayBrightness(int brightness) {}
@Override
public synchronized void setDisplayState(boolean on) {
mDisplayOn = on;
mDisplayStateWait.release();
}
public synchronized boolean getDisplayState() {
return mDisplayOn;
}
public boolean waitForDisplayStateChange(long timeoutMs) throws Exception {
JavaMockitoHelper.await(mDisplayStateWait, timeoutMs);
return mDisplayOn;
}
@Override
public void startDisplayStateMonitoring(CarPowerManagementService service) {}
@Override
public void stopDisplayStateMonitoring() {}
@Override
public void refreshDisplayBrightness() {}
}
/**
* Helper class to set a power-state listener,
* verify that the listener gets called the
* right number of times, and return the final
* power state.
*/
private final class WaitablePowerStateListener {
private final CountDownLatch mLatch;
private int mListenedState = -1;
private long mTimeoutValue = WAIT_TIMEOUT_MS;
WaitablePowerStateListener(int initialCount, long customTimeout) {
this(initialCount);
mTimeoutValue = customTimeout;
}
WaitablePowerStateListener(int initialCount) {
mLatch = new CountDownLatch(initialCount);
mCarPowerManager.setListener(
(state) -> {
mListenedState = state;
mLatch.countDown();
});
}
int await() throws Exception {
JavaMockitoHelper.await(mLatch, WAIT_TIMEOUT_MS);
return mListenedState;
}
}
/**
* Helper class to set a power-state listener with completion,
* verify that the listener gets called the right number of times,
* verify that the CompletableFuture is provided, complete the
* CompletableFuture, and return the final power state.
*/
private final class WaitablePowerStateListenerWithCompletion {
private final CountDownLatch mLatch;
private int mListenedState = -1;
WaitablePowerStateListenerWithCompletion(int initialCount) {
mLatch = new CountDownLatch(initialCount);
mCarPowerManager.setListenerWithCompletion(
(state, future) -> {
mListenedState = state;
if (state == PowerHalService.SET_SHUTDOWN_PREPARE) {
assertThat(future).isNotNull();
future.complete(null);
} else {
assertThat(future).isNull();
}
mLatch.countDown();
});
}
int await() throws Exception {
JavaMockitoHelper.await(mLatch, WAIT_TIMEOUT_MS);
return mListenedState;
}
}
private static final class MockSystemStateInterface implements SystemStateInterface {
private final Semaphore mShutdownWait = new Semaphore(0);
private final Semaphore mSleepWait = new Semaphore(0);
private final Semaphore mSleepExitWait = new Semaphore(0);
private boolean mWakeupCausedByTimer = false;
@Override
public void shutdown() {
mShutdownWait.release();
}
public void waitForShutdown(long timeoutMs) throws Exception {
JavaMockitoHelper.await(mShutdownWait, timeoutMs);
}
@Override
public boolean enterDeepSleep() {
mSleepWait.release();
try {
mSleepExitWait.acquire();
} catch (InterruptedException e) {
}
return true;
}
public void waitForSleepEntryAndWakeup(long timeoutMs) throws Exception {
JavaMockitoHelper.await(mSleepWait, timeoutMs);
mSleepExitWait.release();
}
@Override
public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
@Override
public boolean isWakeupCausedByTimer() {
Log.i(TAG, "isWakeupCausedByTimer:" + mWakeupCausedByTimer);
return mWakeupCausedByTimer;
}
public synchronized void setWakeupCausedByTimer(boolean set) {
mWakeupCausedByTimer = set;
}
@Override
public boolean isSystemSupportingDeepSleep() {
return true;
}
}
}