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

Commit b88801a2 authored by Kweku Adams's avatar Kweku Adams
Browse files

Fix StationaryListener updating.

Quick Doze bypasses motion detection, so if the device moves, the device
will stay in Doze without resetting the motion sensor registration
*for Doze*. If there's a StationaryListener registered,
DeviceIdleController will set a new motion sensor registration ~5
minutes after motion is detected. However, DIC doesn't consider the
device stationary unless there's been no motion for at least 10 minutes
(and the motion sensor registration has been active for the past 10
minutes), so setting the motion timeout alarm to be 10 minutes after the
motion recent motion event (which ends up being 5 minutes after DIC
re-registers the motion sensor) will result in StationaryListeners being
told the device is not stationary if the device is stationary long
enough after a previous motion event.

Bug: 231779749
Test: atest FrameworksMockingServicesTests:DeviceIdleControllerTest
Change-Id: I452e76a4fd5513ff290f89e87182016fa93c1307
parent b6da699d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -693,6 +693,7 @@ public class DeviceIdleController extends SystemService
        synchronized (DeviceIdleController.this) {
            if (mStationaryListeners.size() > 0) {
                startMonitoringMotionLocked();
                scheduleMotionTimeoutAlarmLocked();
            }
        }
    };
@@ -3859,7 +3860,7 @@ public class DeviceIdleController extends SystemService
    void handleMotionDetectedLocked(long timeout, String type) {
        if (mStationaryListeners.size() > 0) {
            postStationaryStatusUpdated();
            scheduleMotionTimeoutAlarmLocked();
            cancelMotionTimeoutAlarmLocked();
            // We need to re-register the motion listener, but we don't want the sensors to be
            // constantly active or to churn the CPU by registering too early, register after some
            // delay.
+65 −1
Original line number Diff line number Diff line
@@ -284,7 +284,7 @@ public class DeviceIdleControllerTest {
        @Override
        public void onDeviceStationaryChanged(boolean isStationary) {
            if (isStationary == motionExpected) {
                fail("Unexpected device stationary status: " + isStationary);
                fail("Got unexpected device stationary status: " + isStationary);
            }
            this.isStationary = isStationary;
        }
@@ -2096,6 +2096,70 @@ public class DeviceIdleControllerTest {
                        eq(SensorManager.SENSOR_DELAY_NORMAL));
    }

    @Test
    public void testStationaryDetection_NoDoze_AfterMotion() {
        // Short timeout for testing.
        mConstants.MOTION_INACTIVE_TIMEOUT = 6000L;
        doReturn(Sensor.REPORTING_MODE_CONTINUOUS).when(mMotionSensor).getReportingMode();
        setAlarmSoon(true);

        final ArgumentCaptor<AlarmManager.OnAlarmListener> regAlarmListener = ArgumentCaptor
                .forClass(AlarmManager.OnAlarmListener.class);
        final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor
                .forClass(AlarmManager.OnAlarmListener.class);
        doNothing().when(mAlarmManager).setWindow(
                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"),
                motionAlarmListener.capture(), any());
        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                eq("DeviceIdleController.motion_registration"),
                regAlarmListener.capture(), any());
        ArgumentCaptor<SensorEventListener> listenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);

        StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
        spyOn(stationaryListener);
        InOrder inOrder = inOrder(stationaryListener, mSensorManager, mAlarmManager);

        stationaryListener.motionExpected = true;
        mDeviceIdleController.registerStationaryListener(stationaryListener);
        inOrder.verify(stationaryListener, timeout(1000L).times(1))
                .onDeviceStationaryChanged(eq(false));
        assertFalse(stationaryListener.isStationary);
        inOrder.verify(mSensorManager)
                .registerListener(listenerCaptor.capture(), eq(mMotionSensor),
                        eq(SensorManager.SENSOR_DELAY_NORMAL));
        inOrder.verify(mAlarmManager).setWindow(
                anyInt(), eq(mInjector.nowElapsed + mConstants.MOTION_INACTIVE_TIMEOUT), anyLong(),
                eq("DeviceIdleController.motion"), any(), any());
        final SensorEventListener listener = listenerCaptor.getValue();

        // Trigger motion
        listener.onSensorChanged(mock(SensorEvent.class));
        inOrder.verify(stationaryListener, timeout(1000L).times(1))
                .onDeviceStationaryChanged(eq(false));
        final ArgumentCaptor<Long> registrationTimeCaptor = ArgumentCaptor.forClass(Long.class);
        inOrder.verify(mAlarmManager).setWindow(
                anyInt(), registrationTimeCaptor.capture(), anyLong(),
                eq("DeviceIdleController.motion_registration"), any(), any());

        // Make sure the listener is re-registered.
        mInjector.nowElapsed = registrationTimeCaptor.getValue();
        regAlarmListener.getValue().onAlarm();
        inOrder.verify(mSensorManager)
                .registerListener(eq(listener), eq(mMotionSensor),
                        eq(SensorManager.SENSOR_DELAY_NORMAL));
        final ArgumentCaptor<Long> timeoutCaptor = ArgumentCaptor.forClass(Long.class);
        inOrder.verify(mAlarmManager).setWindow(anyInt(), timeoutCaptor.capture(), anyLong(),
                eq("DeviceIdleController.motion"), any(), any());

        // No motion before timeout
        stationaryListener.motionExpected = false;
        mInjector.nowElapsed = timeoutCaptor.getValue();
        motionAlarmListener.getValue().onAlarm();
        inOrder.verify(stationaryListener, timeout(1000L).times(1))
                .onDeviceStationaryChanged(eq(true));
    }

    private void enterDeepState(int state) {
        switch (state) {
            case STATE_ACTIVE: