summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/AlarmManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java11
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java185
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java230
4 files changed, 340 insertions, 88 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index 275661084aa3..5b0de5e2aae0 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -26,6 +26,8 @@ public interface AlarmManagerInternal {
void broadcastAlarmComplete(int recipientUid);
}
+ /** Returns true if AlarmManager is delaying alarms due to device idle. */
+ boolean isIdling();
public void removeAlarmsForUid(int uid);
public void registerInFlightListener(InFlightListener callback);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 10b532700d1b..a400cc384a58 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1991,6 +1991,11 @@ class AlarmManagerService extends SystemService {
*/
private final class LocalService implements AlarmManagerInternal {
@Override
+ public boolean isIdling() {
+ return isIdlingImpl();
+ }
+
+ @Override
public void removeAlarmsForUid(int uid) {
synchronized (mLock) {
removeLocked(uid);
@@ -2823,6 +2828,12 @@ class AlarmManagerService extends SystemService {
}
}
+ private boolean isIdlingImpl() {
+ synchronized (mLock) {
+ return mPendingIdleUntil != null;
+ }
+ }
+
AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
synchronized (mLock) {
return mNextAlarmClockForUser.get(userId);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 39030aaf3eb4..6b663941f8fb 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -271,6 +271,7 @@ public class DeviceIdleController extends SystemService
private static final int EVENT_BUFFER_SIZE = 100;
private AlarmManager mAlarmManager;
+ private AlarmManagerInternal mLocalAlarmManager;
private IBatteryStats mBatteryStats;
private ActivityManagerInternal mLocalActivityManager;
private ActivityTaskManagerInternal mLocalActivityTaskManager;
@@ -616,7 +617,8 @@ public class DeviceIdleController extends SystemService
}
};
- private final AlarmManager.OnAlarmListener mDeepAlarmListener
+ @VisibleForTesting
+ final AlarmManager.OnAlarmListener mDeepAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
public void onAlarm() {
@@ -1874,6 +1876,7 @@ public class DeviceIdleController extends SystemService
if (phase == PHASE_SYSTEM_SERVICES_READY) {
synchronized (this) {
mAlarmManager = mInjector.getAlarmManager();
+ mLocalAlarmManager = getLocalService(AlarmManagerInternal.class);
mBatteryStats = BatteryStatsService.getService();
mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
@@ -2605,6 +2608,16 @@ public class DeviceIdleController extends SystemService
// next natural time to come out of it.
}
+
+ /** Returns true if the screen is locked. */
+ @VisibleForTesting
+ boolean isKeyguardShowing() {
+ synchronized (this) {
+ return mScreenLocked;
+ }
+ }
+
+ @VisibleForTesting
void keyguardShowingLocked(boolean showing) {
if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
if (mScreenLocked != showing) {
@@ -2616,25 +2629,38 @@ public class DeviceIdleController extends SystemService
}
}
+ @VisibleForTesting
void scheduleReportActiveLocked(String activeReason, int activeUid) {
Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
mHandler.sendMessage(msg);
}
void becomeActiveLocked(String activeReason, int activeUid) {
- if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + activeReason);
+ becomeActiveLocked(activeReason, activeUid, mConstants.INACTIVE_TIMEOUT, true);
+ }
+
+ private void becomeActiveLocked(String activeReason, int activeUid,
+ long newInactiveTimeout, boolean changeLightIdle) {
+ if (DEBUG) {
+ Slog.i(TAG, "becomeActiveLocked, reason=" + activeReason
+ + ", changeLightIdle=" + changeLightIdle);
+ }
if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason);
- EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason);
- scheduleReportActiveLocked(activeReason, activeUid);
mState = STATE_ACTIVE;
- mLightState = LIGHT_STATE_ACTIVE;
- mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
+ mInactiveTimeout = newInactiveTimeout;
mCurIdleBudget = 0;
mMaintenanceStartTime = 0;
resetIdleManagementLocked();
- resetLightIdleManagementLocked();
- addEvent(EVENT_NORMAL, activeReason);
+
+ if (changeLightIdle) {
+ EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason);
+ mLightState = LIGHT_STATE_ACTIVE;
+ resetLightIdleManagementLocked();
+ // Only report active if light is also ACTIVE.
+ scheduleReportActiveLocked(activeReason, activeUid);
+ addEvent(EVENT_NORMAL, activeReason);
+ }
}
}
@@ -2654,50 +2680,82 @@ public class DeviceIdleController extends SystemService
}
}
+ /** Sanity check to make sure DeviceIdleController and AlarmManager are on the same page. */
+ private void verifyAlarmStateLocked() {
+ if (mState == STATE_ACTIVE && mNextAlarmTime != 0) {
+ Slog.wtf(TAG, "mState=ACTIVE but mNextAlarmTime=" + mNextAlarmTime);
+ }
+ if (mState != STATE_IDLE && mLocalAlarmManager.isIdling()) {
+ Slog.wtf(TAG, "mState=" + stateToString(mState) + " but AlarmManager is idling");
+ }
+ if (mState == STATE_IDLE && !mLocalAlarmManager.isIdling()) {
+ Slog.wtf(TAG, "mState=IDLE but AlarmManager is not idling");
+ }
+ if (mLightState == LIGHT_STATE_ACTIVE && mNextLightAlarmTime != 0) {
+ Slog.wtf(TAG, "mLightState=ACTIVE but mNextLightAlarmTime is "
+ + TimeUtils.formatDuration(mNextLightAlarmTime - SystemClock.elapsedRealtime())
+ + " from now");
+ }
+ }
+
void becomeInactiveIfAppropriateLocked() {
- if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
- if ((!mScreenOn && !mCharging) || mForceIdle) {
- // Become inactive and determine if we will ultimately go idle.
- if (mDeepEnabled) {
- if (mQuickDozeActivated) {
- if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
- || mState == STATE_IDLE_MAINTENANCE) {
- // Already "idling". Don't want to restart the process.
- // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3
- // values, so returning here is safe.
- return;
- }
- if (DEBUG) {
- Slog.d(TAG, "Moved from "
- + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");
- }
- mState = STATE_QUICK_DOZE_DELAY;
- // Make sure any motion sensing or locating is stopped.
- resetIdleManagementLocked();
- // Wait a small amount of time in case something (eg: background service from
- // recently closed app) needs to finish running.
- scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
- EventLogTags.writeDeviceIdle(mState, "no activity");
- } else if (mState == STATE_ACTIVE) {
- mState = STATE_INACTIVE;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
- resetIdleManagementLocked();
- long delay = mInactiveTimeout;
- if (shouldUseIdleTimeoutFactorLocked()) {
- delay = (long) (mPreIdleFactor * delay);
- }
- scheduleAlarmLocked(delay, false);
- EventLogTags.writeDeviceIdle(mState, "no activity");
+ verifyAlarmStateLocked();
+
+ final boolean isScreenBlockingInactive =
+ mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);
+ if (DEBUG) {
+ Slog.d(TAG, "becomeInactiveIfAppropriateLocked():"
+ + " isScreenBlockingInactive=" + isScreenBlockingInactive
+ + " (mScreenOn=" + mScreenOn
+ + ", WAIT_FOR_UNLOCK=" + mConstants.WAIT_FOR_UNLOCK
+ + ", mScreenLocked=" + mScreenLocked + ")"
+ + " mCharging=" + mCharging
+ + " mForceIdle=" + mForceIdle
+ );
+ }
+ if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
+ return;
+ }
+ // Become inactive and determine if we will ultimately go idle.
+ if (mDeepEnabled) {
+ if (mQuickDozeActivated) {
+ if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
+ || mState == STATE_IDLE_MAINTENANCE) {
+ // Already "idling". Don't want to restart the process.
+ // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3
+ // values, so returning here is safe.
+ return;
}
- }
- if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
- mLightState = LIGHT_STATE_INACTIVE;
- if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
- resetLightIdleManagementLocked();
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
- EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
+ if (DEBUG) {
+ Slog.d(TAG, "Moved from "
+ + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");
+ }
+ mState = STATE_QUICK_DOZE_DELAY;
+ // Make sure any motion sensing or locating is stopped.
+ resetIdleManagementLocked();
+ // Wait a small amount of time in case something (eg: background service from
+ // recently closed app) needs to finish running.
+ scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+ EventLogTags.writeDeviceIdle(mState, "no activity");
+ } else if (mState == STATE_ACTIVE) {
+ mState = STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
+ resetIdleManagementLocked();
+ long delay = mInactiveTimeout;
+ if (shouldUseIdleTimeoutFactorLocked()) {
+ delay = (long) (mPreIdleFactor * delay);
+ }
+ scheduleAlarmLocked(delay, false);
+ EventLogTags.writeDeviceIdle(mState, "no activity");
}
}
+ if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
+ mLightState = LIGHT_STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
+ resetLightIdleManagementLocked();
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+ EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
+ }
}
private void resetIdleManagementLocked() {
@@ -3216,33 +3274,10 @@ public class DeviceIdleController extends SystemService
// The device is not yet active, so we want to go back to the pending idle
// state to wait again for no motion. Note that we only monitor for motion
// after moving out of the inactive state, so no need to worry about that.
- boolean becomeInactive = false;
- if (mState != STATE_ACTIVE) {
- // Motion shouldn't affect light state, if it's already in doze-light or maintenance
- boolean lightIdle = mLightState == LIGHT_STATE_IDLE
- || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK
- || mLightState == LIGHT_STATE_IDLE_MAINTENANCE;
- if (!lightIdle) {
- // Only switch to active state if we're not in either idle state
- scheduleReportActiveLocked(type, Process.myUid());
- addEvent(EVENT_NORMAL, type);
- }
- mActiveReason = ACTIVE_REASON_MOTION;
- mState = STATE_ACTIVE;
- mInactiveTimeout = timeout;
- mCurIdleBudget = 0;
- mMaintenanceStartTime = 0;
- EventLogTags.writeDeviceIdle(mState, type);
- becomeInactive = true;
- updateActiveConstraintsLocked();
- }
- if (mLightState == LIGHT_STATE_OVERRIDE) {
- // We went out of light idle mode because we had started deep idle mode... let's
- // now go back and reset things so we resume light idling if appropriate.
- mLightState = LIGHT_STATE_ACTIVE;
- EventLogTags.writeDeviceIdleLight(mLightState, type);
- becomeInactive = true;
- }
+ final boolean becomeInactive = mState != STATE_ACTIVE
+ || mLightState == LIGHT_STATE_OVERRIDE;
+ // We only want to change the IDLE state if it's OVERRIDE.
+ becomeActiveLocked(type, Process.myUid(), timeout, mLightState == LIGHT_STATE_OVERRIDE);
if (becomeInactive) {
becomeInactiveIfAppropriateLocked();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 53d72bb9a415..e51ee947cba1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -19,6 +19,7 @@ import static androidx.test.InstrumentationRegistry.getContext;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -51,6 +52,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -82,6 +86,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -105,6 +110,8 @@ public class DeviceIdleControllerTest {
@Mock
private ContentResolver mContentResolver;
@Mock
+ private DeviceIdleController.MyHandler mHandler;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -154,7 +161,7 @@ public class DeviceIdleControllerTest {
@Override
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
- return mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
+ return mHandler;
}
@Override
@@ -232,10 +239,12 @@ public class DeviceIdleControllerTest {
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(ActivityTaskManagerInternal.class))
.when(() -> LocalServices.getService(ActivityTaskManagerInternal.class));
+ doReturn(mock(AlarmManagerInternal.class))
+ .when(() -> LocalServices.getService(AlarmManagerInternal.class));
doReturn(mPowerManagerInternal)
.when(() -> LocalServices.getService(PowerManagerInternal.class));
- when(mPowerManagerInternal.getLowPowerState(anyInt())).thenReturn(
- mock(PowerSaveState.class));
+ when(mPowerManagerInternal.getLowPowerState(anyInt()))
+ .thenReturn(mock(PowerSaveState.class));
doReturn(mock(NetworkPolicyManagerInternal.class))
.when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
@@ -246,8 +255,11 @@ public class DeviceIdleControllerTest {
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
+ mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
+ doNothing().when(mHandler).handleMessage(any());
mInjector = new InjectorForTest(getContext());
doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
+
mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
spyOn(mDeviceIdleController);
doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
@@ -423,6 +435,29 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
verifyStateConditions(STATE_ACTIVE);
+
+ mConstants.WAIT_FOR_UNLOCK = false;
+ setScreenLocked(true);
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ setScreenLocked(false);
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
+
+ mConstants.WAIT_FOR_UNLOCK = true;
+ setScreenLocked(false);
+ setScreenOn(true);
+ setChargingOn(false);
+
+ mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+ verifyStateConditions(STATE_ACTIVE);
}
@Test
@@ -1307,7 +1342,7 @@ public class DeviceIdleControllerTest {
}
@Test
- public void testbecomeActiveLocked_deep() {
+ public void testBecomeActiveLocked_deep() {
// becomeActiveLocked should put everything into ACTIVE.
enterDeepState(STATE_ACTIVE);
@@ -1344,7 +1379,7 @@ public class DeviceIdleControllerTest {
}
@Test
- public void testbecomeActiveLocked_light() {
+ public void testBecomeActiveLocked_light() {
// becomeActiveLocked should put everything into ACTIVE.
enterLightState(LIGHT_STATE_ACTIVE);
@@ -1376,6 +1411,163 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
}
+ /** Test based on b/119058625. */
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_ScreenThenMotion() {
+ mConstants.WAIT_FOR_UNLOCK = true;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ mDeviceIdleController.keyguardShowingLocked(true);
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself
+ // shouldn't bring the device out of deep IDLE.
+ verifyStateConditions(STATE_IDLE);
+ mDeviceIdleController.handleMotionDetectedLocked(1000, "test");
+ // Motion should bring the device out of Doze. Since the screen is still locked (albeit
+ // on), the states should go back into INACTIVE.
+ verifyStateConditions(STATE_INACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
+ /** Test based on b/119058625. */
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_ScreenThenMotion() {
+ mConstants.WAIT_FOR_UNLOCK = true;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ mDeviceIdleController.keyguardShowingLocked(false);
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself
+ // should bring the device out of deep IDLE.
+ verifyStateConditions(STATE_ACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
+ /** Test based on b/119058625. */
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOn_MotionThenScreen() {
+ mConstants.WAIT_FOR_UNLOCK = true;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+ InOrder controllerInOrder = inOrder(mDeviceIdleController);
+
+ mDeviceIdleController.keyguardShowingLocked(true);
+ mDeviceIdleController.handleMotionDetectedLocked(1000, "test");
+ // The screen is still off, so motion should result in the INACTIVE state.
+ verifyStateConditions(STATE_INACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = true and the screen locked, turning the screen on by itself
+ // shouldn't bring the device all the way to ACTIVE.
+ verifyStateConditions(STATE_INACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager, never()).cancel(
+ eq(mDeviceIdleController.mDeepAlarmListener));
+
+ // User finally unlocks the device. Device should be fully active.
+ mDeviceIdleController.keyguardShowingLocked(false);
+ verifyStateConditions(STATE_ACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
+ /** Test based on b/119058625. */
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOn_KeyguardOff_MotionThenScreen() {
+ mConstants.WAIT_FOR_UNLOCK = true;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+ InOrder controllerInOrder = inOrder(mDeviceIdleController);
+
+ mDeviceIdleController.keyguardShowingLocked(false);
+ mDeviceIdleController.handleMotionDetectedLocked(1000, "test");
+ // The screen is still off, so motion should result in the INACTIVE state.
+ verifyStateConditions(STATE_INACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = true and the screen unlocked, turning the screen on by itself
+ // should bring the device out of deep IDLE.
+ verifyStateConditions(STATE_ACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOff_Screen() {
+ mConstants.WAIT_FOR_UNLOCK = false;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself
+ // should bring the device out of deep IDLE.
+ verifyStateConditions(STATE_ACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ verify(mAlarmManager).cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ verify(mDeviceIdleController).scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
+ @Test
+ public void testExitNotifiesDependencies_WaitForUnlockOff_MotionThenScreen() {
+ mConstants.WAIT_FOR_UNLOCK = false;
+ enterDeepState(STATE_IDLE);
+ reset(mAlarmManager);
+ spyOn(mDeviceIdleController);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+ InOrder controllerInOrder = inOrder(mDeviceIdleController);
+
+ mDeviceIdleController.handleMotionDetectedLocked(1000, "test");
+ // The screen is still off, so motion should result in the INACTIVE state.
+ verifyStateConditions(STATE_INACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+
+ setScreenOn(true);
+ // With WAIT_FOR_UNLOCK = false and the screen locked, turning the screen on by itself
+ // should bring the device out of deep IDLE.
+ verifyStateConditions(STATE_ACTIVE);
+ verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+ alarmManagerInOrder.verify(mAlarmManager)
+ .cancel(eq(mDeviceIdleController.mDeepAlarmListener));
+ controllerInOrder.verify(mDeviceIdleController)
+ .scheduleReportActiveLocked(anyString(), anyInt());
+ }
+
@Test
public void testStepToIdleMode() {
float delta = mDeviceIdleController.MIN_PRE_IDLE_FACTOR_CHANGE;
@@ -1508,6 +1700,10 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.updateChargingLocked(on);
}
+ private void setScreenLocked(boolean locked) {
+ mDeviceIdleController.keyguardShowingLocked(locked);
+ }
+
private void setScreenOn(boolean on) {
doReturn(on).when(mPowerManager).isInteractive();
mDeviceIdleController.updateInteractivityLocked();
@@ -1549,7 +1745,8 @@ public class DeviceIdleControllerTest {
assertFalse(mDeviceIdleController.mMotionListener.isActive());
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
case STATE_IDLE_PENDING:
assertEquals(
@@ -1557,7 +1754,8 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.mMotionListener.isActive());
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
case STATE_SENSING:
assertEquals(
@@ -1567,14 +1765,16 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.hasMotionSensor(),
mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
case STATE_LOCATING:
assertEquals(
mDeviceIdleController.hasMotionSensor(),
mDeviceIdleController.mMotionListener.isActive());
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
case STATE_IDLE:
if (mDeviceIdleController.hasMotionSensor()) {
@@ -1584,7 +1784,8 @@ public class DeviceIdleControllerTest {
}
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
// Light state should be OVERRIDE at this point.
verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
break;
@@ -1596,14 +1797,16 @@ public class DeviceIdleControllerTest {
}
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
case STATE_QUICK_DOZE_DELAY:
// If quick doze is enabled, the motion listener should NOT be active.
assertFalse(mDeviceIdleController.mMotionListener.isActive());
assertFalse(mAnyMotionDetector.isMonitoring);
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
default:
fail("Conditions for " + stateToString(expectedState) + " unknown.");
@@ -1632,7 +1835,8 @@ public class DeviceIdleControllerTest {
case LIGHT_STATE_IDLE_MAINTENANCE:
case LIGHT_STATE_OVERRIDE:
assertFalse(mDeviceIdleController.isCharging());
- assertFalse(mDeviceIdleController.isScreenOn());
+ assertFalse(mDeviceIdleController.isScreenOn()
+ && !mDeviceIdleController.isKeyguardShowing());
break;
default:
fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");