Merge "Ensure AlarmManager knows of IDLE state." into udc-dev am: c95d515c63
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21932153
Change-Id: Ia392187c1b85990e40b93977e7262244e233cabb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 9e4321d..1d93eb3 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2402,7 +2402,6 @@
return mConstants;
}
-
/** Returns the current elapsed realtime in milliseconds. */
long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
@@ -3819,6 +3818,7 @@
// 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 @@
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 @@
// 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 @@
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;
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 8582012..5377ee7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -154,6 +154,7 @@
ConstraintController constraintController;
// Freeze time for testing.
long nowElapsed;
+ boolean useMotionSensor = true;
InjectorForTest(Context ctx) {
super(ctx);
@@ -245,7 +246,7 @@
@Override
boolean useMotionSensor() {
- return true;
+ return useMotionSensor;
}
}
@@ -345,6 +346,12 @@
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 @@
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);
@@ -618,6 +629,60 @@
}
@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;
// Set an upcoming alarm that will prevent full idle.
@@ -757,6 +822,94 @@
}
@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);
// Return that there's an alarm coming soon.