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
This commit is contained in:
Kweku Adams
2022-05-20 17:56:06 +00:00
parent b6da699d30
commit b88801a2bd
2 changed files with 67 additions and 2 deletions

View File

@@ -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.

View File

@@ -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: