Merge "Don't disable GPS in Doze until the device is stationary." into qt-qpr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
858b68330d
@@ -291,6 +291,7 @@ public class DeviceIdleController extends SystemService
|
||||
private boolean mLightEnabled;
|
||||
private boolean mDeepEnabled;
|
||||
private boolean mQuickDozeActivated;
|
||||
private boolean mQuickDozeActivatedWhileIdling;
|
||||
private boolean mForceIdle;
|
||||
private boolean mNetworkConnected;
|
||||
private boolean mScreenOn;
|
||||
@@ -302,6 +303,10 @@ public class DeviceIdleController extends SystemService
|
||||
private boolean mHasNetworkLocation;
|
||||
private Location mLastGenericLocation;
|
||||
private Location mLastGpsLocation;
|
||||
|
||||
/** Time in the elapsed realtime timebase when this listener last received a motion event. */
|
||||
private long mLastMotionEventElapsed;
|
||||
|
||||
// Current locked state of the screen
|
||||
private boolean mScreenLocked;
|
||||
private int mNumBlockingConstraints = 0;
|
||||
@@ -547,6 +552,9 @@ public class DeviceIdleController extends SystemService
|
||||
*/
|
||||
private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
|
||||
|
||||
private final ArraySet<StationaryListener> mStationaryListeners =
|
||||
new ArraySet<>();
|
||||
|
||||
private static final int EVENT_NULL = 0;
|
||||
private static final int EVENT_NORMAL = 1;
|
||||
private static final int EVENT_LIGHT_IDLE = 2;
|
||||
@@ -605,6 +613,21 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
};
|
||||
|
||||
private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
|
||||
synchronized (DeviceIdleController.this) {
|
||||
if (!isStationaryLocked()) {
|
||||
// If the device keeps registering motion, then the alarm should be
|
||||
// rescheduled, so this shouldn't go off until the device is stationary.
|
||||
// This case may happen in a race condition (alarm goes off right before
|
||||
// motion is detected, but handleMotionDetectedLocked is called before
|
||||
// we enter this block).
|
||||
Slog.w(TAG, "motion timeout went off and device isn't stationary");
|
||||
return;
|
||||
}
|
||||
}
|
||||
postStationaryStatusUpdated();
|
||||
};
|
||||
|
||||
private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
|
||||
= new AlarmManager.OnAlarmListener() {
|
||||
@Override
|
||||
@@ -654,12 +677,70 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
};
|
||||
|
||||
/** Post stationary status only to this listener. */
|
||||
private void postStationaryStatus(StationaryListener listener) {
|
||||
mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
|
||||
}
|
||||
|
||||
/** Post stationary status to all registered listeners. */
|
||||
private void postStationaryStatusUpdated() {
|
||||
mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
|
||||
}
|
||||
|
||||
private boolean isStationaryLocked() {
|
||||
final long now = mInjector.getElapsedRealtime();
|
||||
return mMotionListener.active
|
||||
// Listening for motion for long enough and last motion was long enough ago.
|
||||
&& now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
|
||||
>= mConstants.MOTION_INACTIVE_TIMEOUT;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void registerStationaryListener(StationaryListener listener) {
|
||||
synchronized (this) {
|
||||
if (!mStationaryListeners.add(listener)) {
|
||||
// Listener already registered.
|
||||
return;
|
||||
}
|
||||
postStationaryStatus(listener);
|
||||
if (mMotionListener.active) {
|
||||
if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
|
||||
// First listener to be registered and the device isn't stationary, so we
|
||||
// need to register the alarm to report the device is stationary.
|
||||
scheduleMotionTimeoutAlarmLocked();
|
||||
}
|
||||
} else {
|
||||
startMonitoringMotionLocked();
|
||||
scheduleMotionTimeoutAlarmLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterStationaryListener(StationaryListener listener) {
|
||||
synchronized (this) {
|
||||
if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
|
||||
// Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
|
||||
// and so doesn't need to be on for ACTIVE or INACTIVE states.
|
||||
// Motion detection isn't needed when idling due to Quick Doze.
|
||||
&& (mState == STATE_ACTIVE || mState == STATE_INACTIVE
|
||||
|| mQuickDozeActivated)) {
|
||||
maybeStopMonitoringMotionLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final class MotionListener extends TriggerEventListener
|
||||
implements SensorEventListener {
|
||||
|
||||
boolean active = false;
|
||||
|
||||
/**
|
||||
* Time in the elapsed realtime timebase when this listener was activated. Only valid if
|
||||
* {@link #active} is true.
|
||||
*/
|
||||
long activatedTimeElapsed;
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
@@ -667,7 +748,6 @@ public class DeviceIdleController extends SystemService
|
||||
@Override
|
||||
public void onTrigger(TriggerEvent event) {
|
||||
synchronized (DeviceIdleController.this) {
|
||||
active = false;
|
||||
motionLocked();
|
||||
}
|
||||
}
|
||||
@@ -675,8 +755,6 @@ public class DeviceIdleController extends SystemService
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
synchronized (DeviceIdleController.this) {
|
||||
mSensorManager.unregisterListener(this, mMotionSensor);
|
||||
active = false;
|
||||
motionLocked();
|
||||
}
|
||||
}
|
||||
@@ -694,6 +772,7 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
if (success) {
|
||||
active = true;
|
||||
activatedTimeElapsed = mInjector.getElapsedRealtime();
|
||||
} else {
|
||||
Slog.e(TAG, "Unable to register for " + mMotionSensor);
|
||||
}
|
||||
@@ -1307,6 +1386,8 @@ public class DeviceIdleController extends SystemService
|
||||
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
|
||||
private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
|
||||
private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
|
||||
@VisibleForTesting
|
||||
static final int MSG_REPORT_STATIONARY_STATUS = 13;
|
||||
|
||||
final class MyHandler extends Handler {
|
||||
MyHandler(Looper looper) {
|
||||
@@ -1443,6 +1524,30 @@ public class DeviceIdleController extends SystemService
|
||||
updatePreIdleFactor();
|
||||
maybeDoImmediateMaintenance();
|
||||
} break;
|
||||
case MSG_REPORT_STATIONARY_STATUS: {
|
||||
final StationaryListener newListener = (StationaryListener) msg.obj;
|
||||
final StationaryListener[] listeners;
|
||||
final boolean isStationary;
|
||||
synchronized (DeviceIdleController.this) {
|
||||
isStationary = isStationaryLocked();
|
||||
if (newListener == null) {
|
||||
// Only notify all listeners if we aren't directing to one listener.
|
||||
listeners = mStationaryListeners.toArray(
|
||||
new StationaryListener[mStationaryListeners.size()]);
|
||||
} else {
|
||||
listeners = null;
|
||||
}
|
||||
}
|
||||
if (listeners != null) {
|
||||
for (StationaryListener listener : listeners) {
|
||||
listener.onDeviceStationaryChanged(isStationary);
|
||||
}
|
||||
}
|
||||
if (newListener != null) {
|
||||
newListener.onDeviceStationaryChanged(isStationary);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1628,6 +1733,19 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to be notified when DeviceIdleController determines that the device has
|
||||
* moved or is stationary.
|
||||
*/
|
||||
public interface StationaryListener {
|
||||
/**
|
||||
* Called when DeviceIdleController has determined that the device is stationary or moving.
|
||||
*
|
||||
* @param isStationary true if the device is stationary, false otherwise
|
||||
*/
|
||||
void onDeviceStationaryChanged(boolean isStationary);
|
||||
}
|
||||
|
||||
public class LocalService {
|
||||
public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
|
||||
synchronized (DeviceIdleController.this) {
|
||||
@@ -1693,6 +1811,24 @@ public class DeviceIdleController extends SystemService
|
||||
public int[] getPowerSaveTempWhitelistAppIds() {
|
||||
return DeviceIdleController.this.getAppIdTempWhitelistInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener that will be notified when the system has detected that the device
|
||||
* is
|
||||
* stationary or in motion.
|
||||
*/
|
||||
public void registerStationaryListener(StationaryListener listener) {
|
||||
DeviceIdleController.this.registerStationaryListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a registered stationary listener from being notified when the system has
|
||||
* detected
|
||||
* that the device is stationary or in motion.
|
||||
*/
|
||||
public void unregisterStationaryListener(StationaryListener listener) {
|
||||
DeviceIdleController.this.unregisterStationaryListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
static class Injector {
|
||||
@@ -1734,6 +1870,11 @@ public class DeviceIdleController extends SystemService
|
||||
return mConstants;
|
||||
}
|
||||
|
||||
/** Returns the current elapsed realtime in milliseconds. */
|
||||
long getElapsedRealtime() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
LocationManager getLocationManager() {
|
||||
if (mLocationManager == null) {
|
||||
mLocationManager = mContext.getSystemService(LocationManager.class);
|
||||
@@ -2601,6 +2742,8 @@ public class DeviceIdleController extends SystemService
|
||||
void updateQuickDozeFlagLocked(boolean enabled) {
|
||||
if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
|
||||
mQuickDozeActivated = enabled;
|
||||
mQuickDozeActivatedWhileIdling =
|
||||
mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
|
||||
if (enabled) {
|
||||
// If Quick Doze is enabled, see if we should go straight into it.
|
||||
becomeInactiveIfAppropriateLocked();
|
||||
@@ -2767,10 +2910,11 @@ public class DeviceIdleController extends SystemService
|
||||
mNextIdleDelay = 0;
|
||||
mNextLightIdleDelay = 0;
|
||||
mIdleStartTime = 0;
|
||||
mQuickDozeActivatedWhileIdling = false;
|
||||
cancelAlarmLocked();
|
||||
cancelSensingTimeoutAlarmLocked();
|
||||
cancelLocatingLocked();
|
||||
stopMonitoringMotionLocked();
|
||||
maybeStopMonitoringMotionLocked();
|
||||
mAnyMotionDetector.stop();
|
||||
updateActiveConstraintsLocked();
|
||||
}
|
||||
@@ -3270,11 +3414,23 @@ public class DeviceIdleController extends SystemService
|
||||
|
||||
void motionLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "motionLocked()");
|
||||
// The motion sensor will have been disabled at this point
|
||||
mLastMotionEventElapsed = mInjector.getElapsedRealtime();
|
||||
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
|
||||
}
|
||||
|
||||
void handleMotionDetectedLocked(long timeout, String type) {
|
||||
if (mStationaryListeners.size() > 0) {
|
||||
postStationaryStatusUpdated();
|
||||
scheduleMotionTimeoutAlarmLocked();
|
||||
}
|
||||
if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
|
||||
// Don't exit idle due to motion if quick doze is enabled.
|
||||
// However, if the device started idling due to the normal progression (going through
|
||||
// all the states) and then had quick doze activated, come out briefly on motion so the
|
||||
// user can get slightly fresher content.
|
||||
return;
|
||||
}
|
||||
maybeStopMonitoringMotionLocked();
|
||||
// The device is not yet active, so we want to go back to the pending idle
|
||||
// state to wait again for no motion. Note that we only monitor for motion
|
||||
// after moving out of the inactive state, so no need to worry about that.
|
||||
@@ -3326,10 +3482,15 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
void stopMonitoringMotionLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
|
||||
if (mMotionSensor != null && mMotionListener.active) {
|
||||
/**
|
||||
* Stops motion monitoring. Will not stop monitoring if there are registered stationary
|
||||
* listeners.
|
||||
*/
|
||||
private void maybeStopMonitoringMotionLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
|
||||
if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
|
||||
mMotionListener.unregisterLocked();
|
||||
cancelMotionTimeoutAlarmLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3356,6 +3517,10 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelMotionTimeoutAlarmLocked() {
|
||||
mAlarmManager.cancel(mMotionTimeoutAlarmListener);
|
||||
}
|
||||
|
||||
void cancelSensingTimeoutAlarmLocked() {
|
||||
if (mNextSensingTimeoutAlarmTime != 0) {
|
||||
mNextSensingTimeoutAlarmTime = 0;
|
||||
@@ -3402,6 +3567,14 @@ public class DeviceIdleController extends SystemService
|
||||
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
|
||||
}
|
||||
|
||||
private void scheduleMotionTimeoutAlarmLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
|
||||
long nextMotionTimeoutAlarmTime =
|
||||
mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
|
||||
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
|
||||
"DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
|
||||
}
|
||||
|
||||
void scheduleSensingTimeoutAlarmLocked(long delay) {
|
||||
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
|
||||
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
|
||||
@@ -4322,9 +4495,14 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
pw.println(" }");
|
||||
}
|
||||
if (mUseMotionSensor) {
|
||||
if (mUseMotionSensor || mStationaryListeners.size() > 0) {
|
||||
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
|
||||
pw.print(" mNotMoving="); pw.println(mNotMoving);
|
||||
pw.print(" mMotionListener.activatedTimeElapsed=");
|
||||
pw.println(mMotionListener.activatedTimeElapsed);
|
||||
pw.print(" mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
|
||||
pw.print(" "); pw.print(mStationaryListeners.size());
|
||||
pw.println(" stationary listeners registered");
|
||||
}
|
||||
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
|
||||
pw.print(mHasGps); pw.print(" mHasNetwork=");
|
||||
|
||||
@@ -75,6 +75,8 @@ import com.android.internal.location.ProviderProperties;
|
||||
import com.android.internal.location.ProviderRequest;
|
||||
import com.android.internal.location.gnssmetrics.GnssMetrics;
|
||||
import com.android.internal.telephony.TelephonyIntents;
|
||||
import com.android.server.DeviceIdleController;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
|
||||
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
|
||||
|
||||
@@ -178,6 +180,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
private static final int AGPS_SUPL_MODE_MSA = 0x02;
|
||||
private static final int AGPS_SUPL_MODE_MSB = 0x01;
|
||||
|
||||
private static final int UPDATE_LOW_POWER_MODE = 1;
|
||||
private static final int SET_REQUEST = 3;
|
||||
private static final int INJECT_NTP_TIME = 5;
|
||||
// PSDS stands for Predicted Satellite Data Service
|
||||
@@ -370,6 +373,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
// True if gps should be disabled because of PowerManager controls
|
||||
private boolean mDisableGpsForPowerManager = false;
|
||||
|
||||
/**
|
||||
* True if the device idle controller has determined that the device is stationary. This is only
|
||||
* updated when the device enters idle mode.
|
||||
*/
|
||||
private volatile boolean mIsDeviceStationary = false;
|
||||
|
||||
/**
|
||||
* Properties loaded from PROPERTIES_FILE.
|
||||
* It must be accessed only inside {@link #mHandler}.
|
||||
@@ -462,6 +471,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
|
||||
return mGnssNavigationMessageProvider;
|
||||
}
|
||||
|
||||
private final DeviceIdleController.StationaryListener mDeviceIdleStationaryListener =
|
||||
isStationary -> {
|
||||
mIsDeviceStationary = isStationary;
|
||||
// Call updateLowPowerMode on handler thread so it's always called from the same
|
||||
// thread.
|
||||
mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -478,11 +496,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
case ALARM_TIMEOUT:
|
||||
hibernate();
|
||||
break;
|
||||
case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
|
||||
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
|
||||
DeviceIdleController.LocalService deviceIdleService = LocalServices.getService(
|
||||
DeviceIdleController.LocalService.class);
|
||||
if (mPowerManager.isDeviceIdleMode()) {
|
||||
deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
|
||||
} else {
|
||||
deviceIdleService.unregisterStationaryListener(
|
||||
mDeviceIdleStationaryListener);
|
||||
}
|
||||
// Intentional fall-through.
|
||||
case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
|
||||
case Intent.ACTION_SCREEN_OFF:
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
updateLowPowerMode();
|
||||
// Call updateLowPowerMode on handler thread so it's always called from the
|
||||
// same thread.
|
||||
mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
|
||||
break;
|
||||
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
|
||||
case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
|
||||
@@ -540,10 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
}
|
||||
|
||||
private void updateLowPowerMode() {
|
||||
// Disable GPS if we are in device idle mode.
|
||||
boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
|
||||
final PowerSaveState result =
|
||||
mPowerManager.getPowerSaveState(ServiceType.LOCATION);
|
||||
// Disable GPS if we are in device idle mode and the device is stationary.
|
||||
boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
|
||||
final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
|
||||
switch (result.locationMode) {
|
||||
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
|
||||
case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
|
||||
@@ -2048,6 +2076,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
|
||||
case REPORT_SV_STATUS:
|
||||
handleReportSvStatus((SvStatusInfo) msg.obj);
|
||||
break;
|
||||
case UPDATE_LOW_POWER_MODE:
|
||||
updateLowPowerMode();
|
||||
break;
|
||||
}
|
||||
if (msg.arg2 == 1) {
|
||||
// wakelock was taken for this message, release it
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.server;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getContext;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
|
||||
@@ -31,6 +32,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
|
||||
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
|
||||
import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
|
||||
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
|
||||
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
|
||||
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
|
||||
import static com.android.server.DeviceIdleController.STATE_IDLE;
|
||||
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
|
||||
@@ -51,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
@@ -70,6 +73,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
@@ -85,11 +89,13 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoSession;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
/**
|
||||
* Tests for {@link com.android.server.DeviceIdleController}.
|
||||
@@ -97,6 +103,7 @@ import org.mockito.quality.Strictness;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DeviceIdleControllerTest {
|
||||
private DeviceIdleController mDeviceIdleController;
|
||||
private DeviceIdleController.MyHandler mHandler;
|
||||
private AnyMotionDetectorForTest mAnyMotionDetector;
|
||||
private AppStateTrackerForTest mAppStateTracker;
|
||||
private DeviceIdleController.Constants mConstants;
|
||||
@@ -110,8 +117,6 @@ public class DeviceIdleControllerTest {
|
||||
@Mock
|
||||
private ContentResolver mContentResolver;
|
||||
@Mock
|
||||
private DeviceIdleController.MyHandler mHandler;
|
||||
@Mock
|
||||
private IActivityManager mIActivityManager;
|
||||
@Mock
|
||||
private LocationManager mLocationManager;
|
||||
@@ -128,6 +133,8 @@ public class DeviceIdleControllerTest {
|
||||
ConnectivityService connectivityService;
|
||||
LocationManager locationManager;
|
||||
ConstraintController constraintController;
|
||||
// Freeze time for testing.
|
||||
long nowElapsed;
|
||||
|
||||
InjectorForTest(Context ctx) {
|
||||
super(ctx);
|
||||
@@ -154,6 +161,11 @@ public class DeviceIdleControllerTest {
|
||||
return connectivityService;
|
||||
}
|
||||
|
||||
@Override
|
||||
long getElapsedRealtime() {
|
||||
return nowElapsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
LocationManager getLocationManager() {
|
||||
return locationManager;
|
||||
@@ -161,6 +173,23 @@ public class DeviceIdleControllerTest {
|
||||
|
||||
@Override
|
||||
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
|
||||
if (mHandler == null) {
|
||||
mHandler = controller.new MyHandler(getContext().getMainLooper());
|
||||
spyOn(mHandler);
|
||||
doNothing().when(mHandler).handleMessage(argThat((message) ->
|
||||
message.what != MSG_REPORT_STATIONARY_STATUS));
|
||||
doAnswer(new Answer<Boolean>() {
|
||||
@Override
|
||||
public Boolean answer(InvocationOnMock invocation) throws Throwable {
|
||||
Message msg = invocation.getArgument(0);
|
||||
mHandler.handleMessage(msg);
|
||||
return true;
|
||||
}
|
||||
}).when(mHandler).sendMessageDelayed(
|
||||
argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
|
||||
anyLong());
|
||||
}
|
||||
|
||||
return mHandler;
|
||||
}
|
||||
|
||||
@@ -226,6 +255,19 @@ public class DeviceIdleControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private class StationaryListenerForTest implements DeviceIdleController.StationaryListener {
|
||||
boolean motionExpected = false;
|
||||
boolean isStationary = false;
|
||||
|
||||
@Override
|
||||
public void onDeviceStationaryChanged(boolean isStationary) {
|
||||
if (isStationary == motionExpected) {
|
||||
fail("Unexpected device stationary status: " + isStationary);
|
||||
}
|
||||
this.isStationary = isStationary;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMockingSession = mockitoSession()
|
||||
@@ -255,8 +297,6 @@ public class DeviceIdleControllerTest {
|
||||
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
|
||||
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
|
||||
mAnyMotionDetector = new AnyMotionDetectorForTest();
|
||||
mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
|
||||
doNothing().when(mHandler).handleMessage(any());
|
||||
mInjector = new InjectorForTest(getContext());
|
||||
doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
|
||||
|
||||
@@ -1607,6 +1647,86 @@ public class DeviceIdleControllerTest {
|
||||
1.0f, curfactor, delta);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStationaryDetection_QuickDozeOff() {
|
||||
setQuickDozeEnabled(false);
|
||||
enterDeepState(STATE_IDLE);
|
||||
// Regular progression through states, so time should have increased appropriately.
|
||||
mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
|
||||
+ mConstants.LOCATING_TIMEOUT;
|
||||
|
||||
StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
|
||||
|
||||
mDeviceIdleController.registerStationaryListener(stationaryListener);
|
||||
|
||||
// Go to IDLE_MAINTENANCE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
|
||||
// Back to IDLE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
assertTrue(stationaryListener.isStationary);
|
||||
|
||||
// Test motion
|
||||
stationaryListener.motionExpected = true;
|
||||
mDeviceIdleController.mMotionListener.onTrigger(null);
|
||||
assertFalse(stationaryListener.isStationary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStationaryDetection_QuickDozeOn() {
|
||||
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"),
|
||||
alarmListener.capture(), any());
|
||||
|
||||
StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
|
||||
|
||||
stationaryListener.motionExpected = true;
|
||||
mDeviceIdleController.registerStationaryListener(stationaryListener);
|
||||
assertFalse(stationaryListener.isStationary);
|
||||
|
||||
// Go to IDLE_MAINTENANCE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
|
||||
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
|
||||
|
||||
// Back to IDLE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
|
||||
// Now enough time has passed.
|
||||
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
|
||||
stationaryListener.motionExpected = false;
|
||||
alarmListener.getValue().onAlarm();
|
||||
assertTrue(stationaryListener.isStationary);
|
||||
|
||||
stationaryListener.motionExpected = true;
|
||||
mDeviceIdleController.mMotionListener.onSensorChanged(null);
|
||||
assertFalse(stationaryListener.isStationary);
|
||||
|
||||
// Since we're in quick doze, the device shouldn't stop idling.
|
||||
verifyStateConditions(STATE_IDLE);
|
||||
|
||||
// Go to IDLE_MAINTENANCE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
|
||||
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
|
||||
|
||||
// Back to IDLE
|
||||
mDeviceIdleController.stepIdleStateLocked("testing");
|
||||
|
||||
// Now enough time has passed.
|
||||
mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
|
||||
stationaryListener.motionExpected = false;
|
||||
alarmListener.getValue().onAlarm();
|
||||
assertTrue(stationaryListener.isStationary);
|
||||
}
|
||||
|
||||
private void enterDeepState(int state) {
|
||||
switch (state) {
|
||||
case STATE_ACTIVE:
|
||||
|
||||
Reference in New Issue
Block a user