Merge "Re-register SMD after motion detected." into qt-qpr1-dev am: 23e867d5de

Change-Id: I22d0ee2693590a13e99cff140b707c3af0a0aac7
This commit is contained in:
Automerger Merge Worker
2019-12-20 18:52:29 +00:00
2 changed files with 194 additions and 27 deletions

View File

@@ -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 = () -> { private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
synchronized (DeviceIdleController.this) { synchronized (DeviceIdleController.this) {
if (!isStationaryLocked()) { if (!isStationaryLocked()) {
@@ -748,6 +757,7 @@ public class DeviceIdleController extends SystemService
@Override @Override
public void onTrigger(TriggerEvent event) { public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) { synchronized (DeviceIdleController.this) {
active = false;
motionLocked(); motionLocked();
} }
} }
@@ -755,6 +765,8 @@ public class DeviceIdleController extends SystemService
@Override @Override
public void onSensorChanged(SensorEvent event) { public void onSensorChanged(SensorEvent event) {
synchronized (DeviceIdleController.this) { synchronized (DeviceIdleController.this) {
mSensorManager.unregisterListener(this, mMotionSensor);
active = false;
motionLocked(); motionLocked();
} }
} }
@@ -1886,6 +1898,27 @@ public class DeviceIdleController extends SystemService
return controller.new MyHandler(BackgroundThread.getHandler().getLooper()); 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() { PowerManager getPowerManager() {
return mContext.getSystemService(PowerManager.class); return mContext.getSystemService(PowerManager.class);
} }
@@ -2037,21 +2070,7 @@ public class DeviceIdleController extends SystemService
mSensorManager = mInjector.getSensorManager(); mSensorManager = mInjector.getSensorManager();
if (mUseMotionSensor) { if (mUseMotionSensor) {
int sigMotionSensorId = getContext().getResources().getInteger( mMotionSensor = mInjector.getMotionSensor();
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);
}
} }
if (getContext().getResources().getBoolean( if (getContext().getResources().getBoolean(
@@ -3422,6 +3441,10 @@ public class DeviceIdleController extends SystemService
if (mStationaryListeners.size() > 0) { if (mStationaryListeners.size() > 0) {
postStationaryStatusUpdated(); postStationaryStatusUpdated();
scheduleMotionTimeoutAlarmLocked(); 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) { if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
// Don't exit idle due to motion if quick doze is enabled. // Don't exit idle due to motion if quick doze is enabled.
@@ -3488,9 +3511,12 @@ public class DeviceIdleController extends SystemService
*/ */
private void maybeStopMonitoringMotionLocked() { private void maybeStopMonitoringMotionLocked() {
if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) { if (mMotionSensor != null && mStationaryListeners.size() == 0) {
mMotionListener.unregisterLocked(); if (mMotionListener.active) {
cancelMotionTimeoutAlarmLocked(); mMotionListener.unregisterLocked();
cancelMotionTimeoutAlarmLocked();
}
cancelMotionRegistrationAlarmLocked();
} }
} }
@@ -3521,6 +3547,10 @@ public class DeviceIdleController extends SystemService
mAlarmManager.cancel(mMotionTimeoutAlarmListener); mAlarmManager.cancel(mMotionTimeoutAlarmListener);
} }
private void cancelMotionRegistrationAlarmLocked() {
mAlarmManager.cancel(mMotionRegistrationAlarmListener);
}
void cancelSensingTimeoutAlarmLocked() { void cancelSensingTimeoutAlarmLocked() {
if (mNextSensingTimeoutAlarmTime != 0) { if (mNextSensingTimeoutAlarmTime != 0) {
mNextSensingTimeoutAlarmTime = 0; mNextSensingTimeoutAlarmTime = 0;
@@ -3567,6 +3597,15 @@ public class DeviceIdleController extends SystemService
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); 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() { private void scheduleMotionTimeoutAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked"); if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
long nextMotionTimeoutAlarmTime = long nextMotionTimeoutAlarmTime =

View File

@@ -57,6 +57,7 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal;
@@ -66,7 +67,11 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.location.LocationManager; import android.location.LocationManager;
import android.location.LocationProvider; import android.location.LocationProvider;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -127,6 +132,8 @@ public class DeviceIdleControllerTest {
@Mock @Mock
private PowerManagerInternal mPowerManagerInternal; private PowerManagerInternal mPowerManagerInternal;
@Mock @Mock
private Sensor mMotionSensor;
@Mock
private SensorManager mSensorManager; private SensorManager mSensorManager;
class InjectorForTest extends DeviceIdleController.Injector { class InjectorForTest extends DeviceIdleController.Injector {
@@ -193,6 +200,11 @@ public class DeviceIdleControllerTest {
return mHandler; return mHandler;
} }
@Override
Sensor getMotionSensor() {
return mMotionSensor;
}
@Override @Override
PowerManager getPowerManager() { PowerManager getPowerManager() {
return mPowerManager; return mPowerManager;
@@ -1673,22 +1685,36 @@ public class DeviceIdleControllerTest {
} }
@Test @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); setAlarmSoon(false);
enterDeepState(STATE_QUICK_DOZE_DELAY); enterDeepState(STATE_QUICK_DOZE_DELAY);
mDeviceIdleController.stepIdleStateLocked("testing"); mDeviceIdleController.stepIdleStateLocked("testing");
verifyStateConditions(STATE_IDLE); verifyStateConditions(STATE_IDLE);
// Quick doze progression through states, so time should have increased appropriately. // Quick doze progression through states, so time should have increased appropriately.
mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor
.forClass(AlarmManager.OnAlarmListener.class); .forClass(AlarmManager.OnAlarmListener.class);
final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener =
ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), 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(); StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
spyOn(stationaryListener);
InOrder inOrder = inOrder(stationaryListener);
stationaryListener.motionExpected = true; stationaryListener.motionExpected = true;
mDeviceIdleController.registerStationaryListener(stationaryListener); mDeviceIdleController.registerStationaryListener(stationaryListener);
inOrder.verify(stationaryListener, timeout(1000L).times(1))
.onDeviceStationaryChanged(eq(false));
assertFalse(stationaryListener.isStationary); assertFalse(stationaryListener.isStationary);
// Go to IDLE_MAINTENANCE // Go to IDLE_MAINTENANCE
@@ -1700,13 +1726,17 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.stepIdleStateLocked("testing"); mDeviceIdleController.stepIdleStateLocked("testing");
// Now enough time has passed. // Now enough time has passed.
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT;
stationaryListener.motionExpected = false; stationaryListener.motionExpected = false;
alarmListener.getValue().onAlarm(); motionAlarmListener.getValue().onAlarm();
inOrder.verify(stationaryListener, timeout(1000L).times(1))
.onDeviceStationaryChanged(eq(true));
assertTrue(stationaryListener.isStationary); assertTrue(stationaryListener.isStationary);
stationaryListener.motionExpected = true; stationaryListener.motionExpected = true;
mDeviceIdleController.mMotionListener.onSensorChanged(null); mDeviceIdleController.mMotionListener.onTrigger(null);
inOrder.verify(stationaryListener, timeout(1000L).times(1))
.onDeviceStationaryChanged(eq(false));
assertFalse(stationaryListener.isStationary); assertFalse(stationaryListener.isStationary);
// Since we're in quick doze, the device shouldn't stop idling. // Since we're in quick doze, the device shouldn't stop idling.
@@ -1715,18 +1745,116 @@ public class DeviceIdleControllerTest {
// Go to IDLE_MAINTENANCE // Go to IDLE_MAINTENANCE
mDeviceIdleController.stepIdleStateLocked("testing"); mDeviceIdleController.stepIdleStateLocked("testing");
motionRegistrationAlarmListener.getValue().onAlarm();
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
// Back to IDLE // Back to IDLE
stationaryListener.motionExpected = false;
mDeviceIdleController.stepIdleStateLocked("testing"); mDeviceIdleController.stepIdleStateLocked("testing");
verify(mSensorManager,
timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(2))
.requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor));
// Now enough time has passed. // Now enough time has passed.
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT;
stationaryListener.motionExpected = false; motionAlarmListener.getValue().onAlarm();
alarmListener.getValue().onAlarm(); inOrder.verify(stationaryListener,
timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(1))
.onDeviceStationaryChanged(eq(true));
assertTrue(stationaryListener.isStationary); 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) { private void enterDeepState(int state) {
switch (state) { switch (state) {
case STATE_ACTIVE: case STATE_ACTIVE: