Loading services/core/java/com/android/server/DeviceIdleController.java +57 −18 Original line number Diff line number Diff line Loading @@ -613,6 +613,15 @@ public class DeviceIdleController extends SystemService } }; /** AlarmListener to start monitoring motion if there are registered stationary listeners. */ private final AlarmManager.OnAlarmListener mMotionRegistrationAlarmListener = () -> { synchronized (DeviceIdleController.this) { if (mStationaryListeners.size() > 0) { startMonitoringMotionLocked(); } } }; private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> { synchronized (DeviceIdleController.this) { if (!isStationaryLocked()) { Loading Loading @@ -748,6 +757,7 @@ public class DeviceIdleController extends SystemService @Override public void onTrigger(TriggerEvent event) { synchronized (DeviceIdleController.this) { active = false; motionLocked(); } } Loading @@ -755,6 +765,8 @@ public class DeviceIdleController extends SystemService @Override public void onSensorChanged(SensorEvent event) { synchronized (DeviceIdleController.this) { mSensorManager.unregisterListener(this, mMotionSensor); active = false; motionLocked(); } } Loading Loading @@ -1886,6 +1898,27 @@ public class DeviceIdleController extends SystemService return controller.new MyHandler(BackgroundThread.getHandler().getLooper()); } Sensor getMotionSensor() { final SensorManager sensorManager = getSensorManager(); Sensor motionSensor = null; int sigMotionSensorId = mContext.getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); if (sigMotionSensorId > 0) { motionSensor = sensorManager.getDefaultSensor(sigMotionSensorId, true); } if (motionSensor == null && mContext.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { motionSensor = sensorManager.getDefaultSensor( Sensor.TYPE_WRIST_TILT_GESTURE, true); } if (motionSensor == null) { // As a last ditch, fall back to SMD. motionSensor = sensorManager.getDefaultSensor( Sensor.TYPE_SIGNIFICANT_MOTION, true); } return motionSensor; } PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } Loading Loading @@ -2037,21 +2070,7 @@ public class DeviceIdleController extends SystemService mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { int sigMotionSensorId = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); if (sigMotionSensorId > 0) { mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true); } if (mMotionSensor == null && getContext().getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { mMotionSensor = mSensorManager.getDefaultSensor( Sensor.TYPE_WRIST_TILT_GESTURE, true); } if (mMotionSensor == null) { // As a last ditch, fall back to SMD. mMotionSensor = mSensorManager.getDefaultSensor( Sensor.TYPE_SIGNIFICANT_MOTION, true); } mMotionSensor = mInjector.getMotionSensor(); } if (getContext().getResources().getBoolean( Loading Loading @@ -3422,6 +3441,10 @@ public class DeviceIdleController extends SystemService if (mStationaryListeners.size() > 0) { postStationaryStatusUpdated(); scheduleMotionTimeoutAlarmLocked(); // 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. scheduleMotionRegistrationAlarmLocked(); } if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) { // Don't exit idle due to motion if quick doze is enabled. Loading Loading @@ -3488,10 +3511,13 @@ public class DeviceIdleController extends SystemService */ private void maybeStopMonitoringMotionLocked() { if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) { if (mMotionSensor != null && mStationaryListeners.size() == 0) { if (mMotionListener.active) { mMotionListener.unregisterLocked(); cancelMotionTimeoutAlarmLocked(); } cancelMotionRegistrationAlarmLocked(); } } void cancelAlarmLocked() { Loading Loading @@ -3521,6 +3547,10 @@ public class DeviceIdleController extends SystemService mAlarmManager.cancel(mMotionTimeoutAlarmListener); } private void cancelMotionRegistrationAlarmLocked() { mAlarmManager.cancel(mMotionRegistrationAlarmListener); } void cancelSensingTimeoutAlarmLocked() { if (mNextSensingTimeoutAlarmTime != 0) { mNextSensingTimeoutAlarmTime = 0; Loading Loading @@ -3567,6 +3597,15 @@ public class DeviceIdleController extends SystemService mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); } private void scheduleMotionRegistrationAlarmLocked() { if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked"); long nextMotionRegistrationAlarmTime = mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime, "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener, mHandler); } private void scheduleMotionTimeoutAlarmLocked() { if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked"); long nextMotionTimeoutAlarmTime = Loading services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +137 −9 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; Loading @@ -66,7 +67,11 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; Loading Loading @@ -127,6 +132,8 @@ public class DeviceIdleControllerTest { @Mock private PowerManagerInternal mPowerManagerInternal; @Mock private Sensor mMotionSensor; @Mock private SensorManager mSensorManager; class InjectorForTest extends DeviceIdleController.Injector { Loading Loading @@ -193,6 +200,11 @@ public class DeviceIdleControllerTest { return mHandler; } @Override Sensor getMotionSensor() { return mMotionSensor; } @Override PowerManager getPowerManager() { return mPowerManager; Loading Loading @@ -1673,22 +1685,36 @@ public class DeviceIdleControllerTest { } @Test public void testStationaryDetection_QuickDozeOn() { public void testStationaryDetection_QuickDozeOn_NoMotion() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); doReturn(true).when(mSensorManager) .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener = ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), alarmListener.capture(), any()); motionAlarmListener.capture(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), motionRegistrationAlarmListener.capture(), any()); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener); stationaryListener.motionExpected = true; mDeviceIdleController.registerStationaryListener(stationaryListener); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); // Go to IDLE_MAINTENANCE Loading @@ -1700,13 +1726,17 @@ public class DeviceIdleControllerTest { mDeviceIdleController.stepIdleStateLocked("testing"); // Now enough time has passed. mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; stationaryListener.motionExpected = false; alarmListener.getValue().onAlarm(); motionAlarmListener.getValue().onAlarm(); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(true)); assertTrue(stationaryListener.isStationary); stationaryListener.motionExpected = true; mDeviceIdleController.mMotionListener.onSensorChanged(null); mDeviceIdleController.mMotionListener.onTrigger(null); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); // Since we're in quick doze, the device shouldn't stop idling. Loading @@ -1715,18 +1745,116 @@ public class DeviceIdleControllerTest { // Go to IDLE_MAINTENANCE mDeviceIdleController.stepIdleStateLocked("testing"); motionRegistrationAlarmListener.getValue().onAlarm(); mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; // Back to IDLE stationaryListener.motionExpected = false; mDeviceIdleController.stepIdleStateLocked("testing"); verify(mSensorManager, timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(2)) .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); // Now enough time has passed. mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; stationaryListener.motionExpected = false; alarmListener.getValue().onAlarm(); mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; motionAlarmListener.getValue().onAlarm(); inOrder.verify(stationaryListener, timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(1)) .onDeviceStationaryChanged(eq(true)); assertTrue(stationaryListener.isStationary); } @Test public void testStationaryDetection_QuickDozeOn_OneShot() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager) .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), alarmListener.capture(), any()); ArgumentCaptor<TriggerEventListener> listenerCaptor = ArgumentCaptor.forClass(TriggerEventListener.class); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener, mSensorManager); stationaryListener.motionExpected = true; mDeviceIdleController.registerStationaryListener(stationaryListener); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); inOrder.verify(mSensorManager) .requestTriggerSensor(listenerCaptor.capture(), eq(mMotionSensor)); final TriggerEventListener listener = listenerCaptor.getValue(); // Trigger motion listener.onTrigger(mock(TriggerEvent.class)); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); // Make sure the listener is re-registered. alarmListener.getValue().onAlarm(); inOrder.verify(mSensorManager).requestTriggerSensor(eq(listener), eq(mMotionSensor)); } @Test public void testStationaryDetection_QuickDozeOn_MultiShot() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_CONTINUOUS).when(mMotionSensor).getReportingMode(); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager) .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), alarmListener.capture(), any()); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener, mSensorManager); 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)); final SensorEventListener listener = listenerCaptor.getValue(); // Trigger motion listener.onSensorChanged(mock(SensorEvent.class)); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); // Make sure the listener is re-registered. alarmListener.getValue().onAlarm(); inOrder.verify(mSensorManager) .registerListener(eq(listener), eq(mMotionSensor), eq(SensorManager.SENSOR_DELAY_NORMAL)); } private void enterDeepState(int state) { switch (state) { case STATE_ACTIVE: Loading Loading
services/core/java/com/android/server/DeviceIdleController.java +57 −18 Original line number Diff line number Diff line Loading @@ -613,6 +613,15 @@ public class DeviceIdleController extends SystemService } }; /** AlarmListener to start monitoring motion if there are registered stationary listeners. */ private final AlarmManager.OnAlarmListener mMotionRegistrationAlarmListener = () -> { synchronized (DeviceIdleController.this) { if (mStationaryListeners.size() > 0) { startMonitoringMotionLocked(); } } }; private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> { synchronized (DeviceIdleController.this) { if (!isStationaryLocked()) { Loading Loading @@ -748,6 +757,7 @@ public class DeviceIdleController extends SystemService @Override public void onTrigger(TriggerEvent event) { synchronized (DeviceIdleController.this) { active = false; motionLocked(); } } Loading @@ -755,6 +765,8 @@ public class DeviceIdleController extends SystemService @Override public void onSensorChanged(SensorEvent event) { synchronized (DeviceIdleController.this) { mSensorManager.unregisterListener(this, mMotionSensor); active = false; motionLocked(); } } Loading Loading @@ -1886,6 +1898,27 @@ public class DeviceIdleController extends SystemService return controller.new MyHandler(BackgroundThread.getHandler().getLooper()); } Sensor getMotionSensor() { final SensorManager sensorManager = getSensorManager(); Sensor motionSensor = null; int sigMotionSensorId = mContext.getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); if (sigMotionSensorId > 0) { motionSensor = sensorManager.getDefaultSensor(sigMotionSensorId, true); } if (motionSensor == null && mContext.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { motionSensor = sensorManager.getDefaultSensor( Sensor.TYPE_WRIST_TILT_GESTURE, true); } if (motionSensor == null) { // As a last ditch, fall back to SMD. motionSensor = sensorManager.getDefaultSensor( Sensor.TYPE_SIGNIFICANT_MOTION, true); } return motionSensor; } PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } Loading Loading @@ -2037,21 +2070,7 @@ public class DeviceIdleController extends SystemService mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { int sigMotionSensorId = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); if (sigMotionSensorId > 0) { mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true); } if (mMotionSensor == null && getContext().getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { mMotionSensor = mSensorManager.getDefaultSensor( Sensor.TYPE_WRIST_TILT_GESTURE, true); } if (mMotionSensor == null) { // As a last ditch, fall back to SMD. mMotionSensor = mSensorManager.getDefaultSensor( Sensor.TYPE_SIGNIFICANT_MOTION, true); } mMotionSensor = mInjector.getMotionSensor(); } if (getContext().getResources().getBoolean( Loading Loading @@ -3422,6 +3441,10 @@ public class DeviceIdleController extends SystemService if (mStationaryListeners.size() > 0) { postStationaryStatusUpdated(); scheduleMotionTimeoutAlarmLocked(); // 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. scheduleMotionRegistrationAlarmLocked(); } if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) { // Don't exit idle due to motion if quick doze is enabled. Loading Loading @@ -3488,10 +3511,13 @@ public class DeviceIdleController extends SystemService */ private void maybeStopMonitoringMotionLocked() { if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) { if (mMotionSensor != null && mStationaryListeners.size() == 0) { if (mMotionListener.active) { mMotionListener.unregisterLocked(); cancelMotionTimeoutAlarmLocked(); } cancelMotionRegistrationAlarmLocked(); } } void cancelAlarmLocked() { Loading Loading @@ -3521,6 +3547,10 @@ public class DeviceIdleController extends SystemService mAlarmManager.cancel(mMotionTimeoutAlarmListener); } private void cancelMotionRegistrationAlarmLocked() { mAlarmManager.cancel(mMotionRegistrationAlarmListener); } void cancelSensingTimeoutAlarmLocked() { if (mNextSensingTimeoutAlarmTime != 0) { mNextSensingTimeoutAlarmTime = 0; Loading Loading @@ -3567,6 +3597,15 @@ public class DeviceIdleController extends SystemService mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); } private void scheduleMotionRegistrationAlarmLocked() { if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked"); long nextMotionRegistrationAlarmTime = mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime, "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener, mHandler); } private void scheduleMotionTimeoutAlarmLocked() { if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked"); long nextMotionTimeoutAlarmTime = Loading
services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +137 −9 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; Loading @@ -66,7 +67,11 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; Loading Loading @@ -127,6 +132,8 @@ public class DeviceIdleControllerTest { @Mock private PowerManagerInternal mPowerManagerInternal; @Mock private Sensor mMotionSensor; @Mock private SensorManager mSensorManager; class InjectorForTest extends DeviceIdleController.Injector { Loading Loading @@ -193,6 +200,11 @@ public class DeviceIdleControllerTest { return mHandler; } @Override Sensor getMotionSensor() { return mMotionSensor; } @Override PowerManager getPowerManager() { return mPowerManager; Loading Loading @@ -1673,22 +1685,36 @@ public class DeviceIdleControllerTest { } @Test public void testStationaryDetection_QuickDozeOn() { public void testStationaryDetection_QuickDozeOn_NoMotion() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); doReturn(true).when(mSensorManager) .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener = ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), alarmListener.capture(), any()); motionAlarmListener.capture(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), motionRegistrationAlarmListener.capture(), any()); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener); stationaryListener.motionExpected = true; mDeviceIdleController.registerStationaryListener(stationaryListener); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); // Go to IDLE_MAINTENANCE Loading @@ -1700,13 +1726,17 @@ public class DeviceIdleControllerTest { mDeviceIdleController.stepIdleStateLocked("testing"); // Now enough time has passed. mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; stationaryListener.motionExpected = false; alarmListener.getValue().onAlarm(); motionAlarmListener.getValue().onAlarm(); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(true)); assertTrue(stationaryListener.isStationary); stationaryListener.motionExpected = true; mDeviceIdleController.mMotionListener.onSensorChanged(null); mDeviceIdleController.mMotionListener.onTrigger(null); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); // Since we're in quick doze, the device shouldn't stop idling. Loading @@ -1715,18 +1745,116 @@ public class DeviceIdleControllerTest { // Go to IDLE_MAINTENANCE mDeviceIdleController.stepIdleStateLocked("testing"); motionRegistrationAlarmListener.getValue().onAlarm(); mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; // Back to IDLE stationaryListener.motionExpected = false; mDeviceIdleController.stepIdleStateLocked("testing"); verify(mSensorManager, timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(2)) .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); // Now enough time has passed. mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; stationaryListener.motionExpected = false; alarmListener.getValue().onAlarm(); mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; motionAlarmListener.getValue().onAlarm(); inOrder.verify(stationaryListener, timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(1)) .onDeviceStationaryChanged(eq(true)); assertTrue(stationaryListener.isStationary); } @Test public void testStationaryDetection_QuickDozeOn_OneShot() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager) .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), alarmListener.capture(), any()); ArgumentCaptor<TriggerEventListener> listenerCaptor = ArgumentCaptor.forClass(TriggerEventListener.class); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener, mSensorManager); stationaryListener.motionExpected = true; mDeviceIdleController.registerStationaryListener(stationaryListener); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); assertFalse(stationaryListener.isStationary); inOrder.verify(mSensorManager) .requestTriggerSensor(listenerCaptor.capture(), eq(mMotionSensor)); final TriggerEventListener listener = listenerCaptor.getValue(); // Trigger motion listener.onTrigger(mock(TriggerEvent.class)); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); // Make sure the listener is re-registered. alarmListener.getValue().onAlarm(); inOrder.verify(mSensorManager).requestTriggerSensor(eq(listener), eq(mMotionSensor)); } @Test public void testStationaryDetection_QuickDozeOn_MultiShot() { // Short timeout for testing. mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; doReturn(Sensor.REPORTING_MODE_CONTINUOUS).when(mMotionSensor).getReportingMode(); setAlarmSoon(false); enterDeepState(STATE_QUICK_DOZE_DELAY); mDeviceIdleController.stepIdleStateLocked("testing"); verifyStateConditions(STATE_IDLE); // Quick doze progression through states, so time should have increased appropriately. mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor .forClass(AlarmManager.OnAlarmListener.class); doNothing().when(mAlarmManager) .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion_registration"), alarmListener.capture(), any()); ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); spyOn(stationaryListener); InOrder inOrder = inOrder(stationaryListener, mSensorManager); 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)); final SensorEventListener listener = listenerCaptor.getValue(); // Trigger motion listener.onSensorChanged(mock(SensorEvent.class)); inOrder.verify(stationaryListener, timeout(1000L).times(1)) .onDeviceStationaryChanged(eq(false)); // Make sure the listener is re-registered. alarmListener.getValue().onAlarm(); inOrder.verify(mSensorManager) .registerListener(eq(listener), eq(mMotionSensor), eq(SensorManager.SENSOR_DELAY_NORMAL)); } private void enterDeepState(int state) { switch (state) { case STATE_ACTIVE: Loading