Ensure AlarmManager knows of IDLE state.
AlarmManager relies on AlarmManager.setIdleUntil to know when the device is entering the IDLE state. Devices that want to use a motion sensor but don't have one normally won't proceed to the IDLE state, but someone could force the device into the IDLE state via adb. In that situation, AlarmManager should still be told that the device entered the IDLE state. The alarm scheduling method expects mState to be updated before its called, so if the method is called before the state is updated, it will do the wrong thing. Bug: 270672228 Test: atest DeviceIdleTest Test: atest FrameworksMockingServicesTests:DeviceIdleControllerTest Change-Id: I95a06e37f218bfebc86ffa6a984b50b60013af7d
This commit is contained in:
@@ -2402,7 +2402,6 @@ public class DeviceIdleController extends SystemService
|
||||
return mConstants;
|
||||
}
|
||||
|
||||
|
||||
/** Returns the current elapsed realtime in milliseconds. */
|
||||
long getElapsedRealtime() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
@@ -3819,6 +3818,7 @@ public class DeviceIdleController extends SystemService
|
||||
|
||||
// Everything is in place to go into IDLE state.
|
||||
case STATE_IDLE_MAINTENANCE:
|
||||
moveToStateLocked(STATE_IDLE, reason);
|
||||
scheduleAlarmLocked(mNextIdleDelay, true);
|
||||
if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
|
||||
" ms.");
|
||||
@@ -3829,7 +3829,6 @@ public class DeviceIdleController extends SystemService
|
||||
if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
|
||||
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
|
||||
}
|
||||
moveToStateLocked(STATE_IDLE, reason);
|
||||
if (mLightState != LIGHT_STATE_OVERRIDE) {
|
||||
moveToLightStateLocked(LIGHT_STATE_OVERRIDE, "deep");
|
||||
cancelLightAlarmLocked();
|
||||
@@ -3842,6 +3841,7 @@ public class DeviceIdleController extends SystemService
|
||||
// We have been idling long enough, now it is time to do some work.
|
||||
mActiveIdleOpCount = 1;
|
||||
mActiveIdleWakeLock.acquire();
|
||||
moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
|
||||
scheduleAlarmLocked(mNextIdlePendingDelay, false);
|
||||
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
|
||||
"Next alarm in " + mNextIdlePendingDelay + " ms.");
|
||||
@@ -3851,7 +3851,6 @@ public class DeviceIdleController extends SystemService
|
||||
if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
|
||||
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
|
||||
}
|
||||
moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
|
||||
addEvent(EVENT_DEEP_MAINTENANCE, null);
|
||||
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
|
||||
break;
|
||||
|
||||
@@ -154,6 +154,7 @@ public class DeviceIdleControllerTest {
|
||||
ConstraintController constraintController;
|
||||
// Freeze time for testing.
|
||||
long nowElapsed;
|
||||
boolean useMotionSensor = true;
|
||||
|
||||
InjectorForTest(Context ctx) {
|
||||
super(ctx);
|
||||
@@ -245,7 +246,7 @@ public class DeviceIdleControllerTest {
|
||||
|
||||
@Override
|
||||
boolean useMotionSensor() {
|
||||
return true;
|
||||
return useMotionSensor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +346,12 @@ public class DeviceIdleControllerTest {
|
||||
mAnyMotionDetector = new AnyMotionDetectorForTest();
|
||||
mInjector = new InjectorForTest(getContext());
|
||||
|
||||
setupDeviceIdleController();
|
||||
}
|
||||
|
||||
private void setupDeviceIdleController() {
|
||||
reset(mTelephonyManager);
|
||||
|
||||
mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
|
||||
spyOn(mDeviceIdleController);
|
||||
doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
|
||||
@@ -371,6 +378,10 @@ public class DeviceIdleControllerTest {
|
||||
if (mMockingSession != null) {
|
||||
mMockingSession.finishMocking();
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupDeviceIdleController() {
|
||||
// DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
|
||||
// them after each test, otherwise, subsequent tests will fail.
|
||||
LocalServices.removeServiceForTest(AppStateTracker.class);
|
||||
@@ -617,6 +628,60 @@ public class DeviceIdleControllerTest {
|
||||
.scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateActiveToStateInactive_DoNotUseMotionSensor() {
|
||||
mInjector.useMotionSensor = false;
|
||||
cleanupDeviceIdleController();
|
||||
setupDeviceIdleController();
|
||||
mDeviceIdleController.becomeActiveLocked("testing", 0);
|
||||
verifyStateConditions(STATE_ACTIVE);
|
||||
|
||||
setAlarmSoon(false);
|
||||
setChargingOn(false);
|
||||
setScreenOn(false);
|
||||
setEmergencyCallActive(false);
|
||||
|
||||
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||
verifyStateConditions(STATE_INACTIVE);
|
||||
verify(mDeviceIdleController)
|
||||
.scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
|
||||
// The device configuration doesn't require a motion sensor to proceed with idling.
|
||||
// This should be the case on TVs or other such devices. We should set an alarm to move
|
||||
// forward if the motion sensor is missing in this case.
|
||||
verify(mAlarmManager).setWindow(
|
||||
anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateActiveToStateInactive_MissingMotionSensor() {
|
||||
mInjector.useMotionSensor = true;
|
||||
mMotionSensor = null;
|
||||
cleanupDeviceIdleController();
|
||||
setupDeviceIdleController();
|
||||
mDeviceIdleController.becomeActiveLocked("testing", 0);
|
||||
verifyStateConditions(STATE_ACTIVE);
|
||||
|
||||
setAlarmSoon(false);
|
||||
setChargingOn(false);
|
||||
setScreenOn(false);
|
||||
setEmergencyCallActive(false);
|
||||
|
||||
mDeviceIdleController.becomeInactiveIfAppropriateLocked();
|
||||
verifyStateConditions(STATE_INACTIVE);
|
||||
verify(mDeviceIdleController)
|
||||
.scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
|
||||
// The device configuration requires a motion sensor to proceed with idling,
|
||||
// so we should never set an alarm to move forward if the motion sensor is
|
||||
// missing in this case.
|
||||
verify(mAlarmManager, never()).setWindow(
|
||||
anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
verify(mAlarmManager, never()).set(
|
||||
anyInt(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStateActiveToStateInactive_UpcomingAlarm() {
|
||||
final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2;
|
||||
@@ -756,6 +821,94 @@ public class DeviceIdleControllerTest {
|
||||
verifyStateConditions(STATE_IDLE_MAINTENANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepIdleStateLocked_ValidStates_MissingMotionSensor() {
|
||||
mInjector.useMotionSensor = true;
|
||||
mMotionSensor = null;
|
||||
cleanupDeviceIdleController();
|
||||
setupDeviceIdleController();
|
||||
mInjector.locationManager = mLocationManager;
|
||||
doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
|
||||
// Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
|
||||
setAlarmSoon(false);
|
||||
|
||||
InOrder alarmManagerInOrder = inOrder(mAlarmManager);
|
||||
|
||||
// Set state to INACTIVE.
|
||||
mDeviceIdleController.becomeActiveLocked("testing", 0);
|
||||
setChargingOn(false);
|
||||
setScreenOn(false);
|
||||
verifyStateConditions(STATE_INACTIVE);
|
||||
|
||||
// The device configuration requires a motion sensor to proceed with idling,
|
||||
// so we should never set an alarm to move forward if the motion sensor is
|
||||
// missing in this case.
|
||||
alarmManagerInOrder.verify(mAlarmManager, never())
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
// Pretend that someone is forcing state stepping via adb
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
// verifyStateConditions knows this state typically shouldn't happen during normal
|
||||
// operation, so we can't use it directly here. For this test, all we care about
|
||||
// is that the state stepped forward.
|
||||
assertEquals(STATE_IDLE_PENDING, mDeviceIdleController.getState());
|
||||
// Still no alarm
|
||||
alarmManagerInOrder.verify(mAlarmManager, never())
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
// verifyStateConditions knows this state typically shouldn't happen during normal
|
||||
// operation, so we can't use it directly here. For this test, all we care about
|
||||
// is that the state stepped forward.
|
||||
assertEquals(STATE_SENSING, mDeviceIdleController.getState());
|
||||
// Still no alarm
|
||||
alarmManagerInOrder.verify(mAlarmManager, never())
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
// Location manager exists with a provider, so SENSING should go to LOCATING.
|
||||
// verifyStateConditions knows this state typically shouldn't happen during normal
|
||||
// operation, so we can't use it directly here. For this test, all we care about
|
||||
// is that the state stepped forward.
|
||||
assertEquals(STATE_LOCATING, mDeviceIdleController.getState());
|
||||
// Still no alarm
|
||||
alarmManagerInOrder.verify(mAlarmManager, never())
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
verifyStateConditions(STATE_IDLE);
|
||||
// The device was forced into IDLE. AlarmManager should be notified.
|
||||
alarmManagerInOrder.verify(mAlarmManager)
|
||||
.setIdleUntil(anyInt(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
// Should just alternate between IDLE and IDLE_MAINTENANCE now. Since we've gotten to this
|
||||
// point, alarms should be set on each transition.
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
verifyStateConditions(STATE_IDLE_MAINTENANCE);
|
||||
alarmManagerInOrder.verify(mAlarmManager)
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
verifyStateConditions(STATE_IDLE);
|
||||
alarmManagerInOrder.verify(mAlarmManager)
|
||||
.setIdleUntil(anyInt(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
verifyStateConditions(STATE_IDLE_MAINTENANCE);
|
||||
alarmManagerInOrder.verify(mAlarmManager)
|
||||
.setWindow(anyInt(), anyLong(), anyLong(),
|
||||
eq("DeviceIdleController.deep"), any(), any(Handler.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepIdleStateLocked_ValidStates_WithWakeFromIdleAlarmSoon() {
|
||||
enterDeepState(STATE_ACTIVE);
|
||||
|
||||
Reference in New Issue
Block a user