Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26987e03 authored by Kweku Adams's avatar Kweku Adams
Browse files

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
parent bf1ccb78
Loading
Loading
Loading
Loading
+2 −3
Original line number Original line Diff line number Diff line
@@ -2402,7 +2402,6 @@ public class DeviceIdleController extends SystemService
            return mConstants;
            return mConstants;
        }
        }



        /** Returns the current elapsed realtime in milliseconds. */
        /** Returns the current elapsed realtime in milliseconds. */
        long getElapsedRealtime() {
        long getElapsedRealtime() {
            return SystemClock.elapsedRealtime();
            return SystemClock.elapsedRealtime();
@@ -3819,6 +3818,7 @@ public class DeviceIdleController extends SystemService


                // Everything is in place to go into IDLE state.
                // Everything is in place to go into IDLE state.
            case STATE_IDLE_MAINTENANCE:
            case STATE_IDLE_MAINTENANCE:
                moveToStateLocked(STATE_IDLE, reason);
                scheduleAlarmLocked(mNextIdleDelay, true);
                scheduleAlarmLocked(mNextIdleDelay, true);
                if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
                if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
                        " ms.");
                        " ms.");
@@ -3829,7 +3829,6 @@ public class DeviceIdleController extends SystemService
                if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                    mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                    mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                }
                }
                moveToStateLocked(STATE_IDLE, reason);
                if (mLightState != LIGHT_STATE_OVERRIDE) {
                if (mLightState != LIGHT_STATE_OVERRIDE) {
                    moveToLightStateLocked(LIGHT_STATE_OVERRIDE, "deep");
                    moveToLightStateLocked(LIGHT_STATE_OVERRIDE, "deep");
                    cancelLightAlarmLocked();
                    cancelLightAlarmLocked();
@@ -3842,6 +3841,7 @@ public class DeviceIdleController extends SystemService
                // We have been idling long enough, now it is time to do some work.
                // We have been idling long enough, now it is time to do some work.
                mActiveIdleOpCount = 1;
                mActiveIdleOpCount = 1;
                mActiveIdleWakeLock.acquire();
                mActiveIdleWakeLock.acquire();
                moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
                scheduleAlarmLocked(mNextIdlePendingDelay, false);
                scheduleAlarmLocked(mNextIdlePendingDelay, false);
                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
                        "Next alarm in " + mNextIdlePendingDelay + " ms.");
                        "Next alarm in " + mNextIdlePendingDelay + " ms.");
@@ -3851,7 +3851,6 @@ public class DeviceIdleController extends SystemService
                if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
                if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
                    mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                    mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                }
                }
                moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
                addEvent(EVENT_DEEP_MAINTENANCE, null);
                addEvent(EVENT_DEEP_MAINTENANCE, null);
                mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                break;
                break;
+154 −1
Original line number Original line Diff line number Diff line
@@ -154,6 +154,7 @@ public class DeviceIdleControllerTest {
        ConstraintController constraintController;
        ConstraintController constraintController;
        // Freeze time for testing.
        // Freeze time for testing.
        long nowElapsed;
        long nowElapsed;
        boolean useMotionSensor = true;


        InjectorForTest(Context ctx) {
        InjectorForTest(Context ctx) {
            super(ctx);
            super(ctx);
@@ -245,7 +246,7 @@ public class DeviceIdleControllerTest {


        @Override
        @Override
        boolean useMotionSensor() {
        boolean useMotionSensor() {
            return true;
            return useMotionSensor;
        }
        }
    }
    }


@@ -345,6 +346,12 @@ public class DeviceIdleControllerTest {
        mAnyMotionDetector = new AnyMotionDetectorForTest();
        mAnyMotionDetector = new AnyMotionDetectorForTest();
        mInjector = new InjectorForTest(getContext());
        mInjector = new InjectorForTest(getContext());


        setupDeviceIdleController();
    }

    private void setupDeviceIdleController() {
        reset(mTelephonyManager);

        mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
        mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
        spyOn(mDeviceIdleController);
        spyOn(mDeviceIdleController);
        doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
        doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
@@ -371,6 +378,10 @@ public class DeviceIdleControllerTest {
        if (mMockingSession != null) {
        if (mMockingSession != null) {
            mMockingSession.finishMocking();
            mMockingSession.finishMocking();
        }
        }
    }

    @After
    public void cleanupDeviceIdleController() {
        // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
        // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
        // them after each test, otherwise, subsequent tests will fail.
        // them after each test, otherwise, subsequent tests will fail.
        LocalServices.removeServiceForTest(AppStateTracker.class);
        LocalServices.removeServiceForTest(AppStateTracker.class);
@@ -617,6 +628,60 @@ public class DeviceIdleControllerTest {
                .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
                .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
    @Test
    public void testStateActiveToStateInactive_UpcomingAlarm() {
    public void testStateActiveToStateInactive_UpcomingAlarm() {
        final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2;
        final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2;
@@ -756,6 +821,94 @@ public class DeviceIdleControllerTest {
        verifyStateConditions(STATE_IDLE_MAINTENANCE);
        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
    @Test
    public void testStepIdleStateLocked_ValidStates_WithWakeFromIdleAlarmSoon() {
    public void testStepIdleStateLocked_ValidStates_WithWakeFromIdleAlarmSoon() {
        enterDeepState(STATE_ACTIVE);
        enterDeepState(STATE_ACTIVE);