Loading services/core/java/com/android/server/DeviceIdleController.java +187 −9 Original line number Original line Diff line number Diff line Loading @@ -291,6 +291,7 @@ public class DeviceIdleController extends SystemService private boolean mLightEnabled; private boolean mLightEnabled; private boolean mDeepEnabled; private boolean mDeepEnabled; private boolean mQuickDozeActivated; private boolean mQuickDozeActivated; private boolean mQuickDozeActivatedWhileIdling; private boolean mForceIdle; private boolean mForceIdle; private boolean mNetworkConnected; private boolean mNetworkConnected; private boolean mScreenOn; private boolean mScreenOn; Loading @@ -302,6 +303,10 @@ public class DeviceIdleController extends SystemService private boolean mHasNetworkLocation; private boolean mHasNetworkLocation; private Location mLastGenericLocation; private Location mLastGenericLocation; private Location mLastGpsLocation; 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 // Current locked state of the screen private boolean mScreenLocked; private boolean mScreenLocked; private int mNumBlockingConstraints = 0; private int mNumBlockingConstraints = 0; Loading Loading @@ -547,6 +552,9 @@ public class DeviceIdleController extends SystemService */ */ private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>(); 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_NULL = 0; private static final int EVENT_NORMAL = 1; private static final int EVENT_NORMAL = 1; private static final int EVENT_LIGHT_IDLE = 2; private static final int EVENT_LIGHT_IDLE = 2; Loading Loading @@ -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 private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener = new AlarmManager.OnAlarmListener() { = new AlarmManager.OnAlarmListener() { @Override @Override Loading Loading @@ -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 @VisibleForTesting final class MotionListener extends TriggerEventListener final class MotionListener extends TriggerEventListener implements SensorEventListener { implements SensorEventListener { boolean active = false; 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() { public boolean isActive() { return active; return active; } } Loading @@ -667,7 +748,6 @@ 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(); } } } } Loading @@ -675,8 +755,6 @@ 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(); } } } } Loading @@ -694,6 +772,7 @@ public class DeviceIdleController extends SystemService } } if (success) { if (success) { active = true; active = true; activatedTimeElapsed = mInjector.getElapsedRealtime(); } else { } else { Slog.e(TAG, "Unable to register for " + mMotionSensor); Slog.e(TAG, "Unable to register for " + mMotionSensor); } } Loading Loading @@ -1307,6 +1386,8 @@ public class DeviceIdleController extends SystemService private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; 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_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11; private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12; 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 { final class MyHandler extends Handler { MyHandler(Looper looper) { MyHandler(Looper looper) { Loading Loading @@ -1443,6 +1524,30 @@ public class DeviceIdleController extends SystemService updatePreIdleFactor(); updatePreIdleFactor(); maybeDoImmediateMaintenance(); maybeDoImmediateMaintenance(); } break; } 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; } } } } } } Loading Loading @@ -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 class LocalService { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { synchronized (DeviceIdleController.this) { synchronized (DeviceIdleController.this) { Loading Loading @@ -1693,6 +1811,24 @@ public class DeviceIdleController extends SystemService public int[] getPowerSaveTempWhitelistAppIds() { public int[] getPowerSaveTempWhitelistAppIds() { return DeviceIdleController.this.getAppIdTempWhitelistInternal(); 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 { static class Injector { Loading Loading @@ -1734,6 +1870,11 @@ public class DeviceIdleController extends SystemService return mConstants; return mConstants; } } /** Returns the current elapsed realtime in milliseconds. */ long getElapsedRealtime() { return SystemClock.elapsedRealtime(); } LocationManager getLocationManager() { LocationManager getLocationManager() { if (mLocationManager == null) { if (mLocationManager == null) { mLocationManager = mContext.getSystemService(LocationManager.class); mLocationManager = mContext.getSystemService(LocationManager.class); Loading Loading @@ -2601,6 +2742,8 @@ public class DeviceIdleController extends SystemService void updateQuickDozeFlagLocked(boolean enabled) { void updateQuickDozeFlagLocked(boolean enabled) { if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled); if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled); mQuickDozeActivated = enabled; mQuickDozeActivated = enabled; mQuickDozeActivatedWhileIdling = mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE); if (enabled) { if (enabled) { // If Quick Doze is enabled, see if we should go straight into it. // If Quick Doze is enabled, see if we should go straight into it. becomeInactiveIfAppropriateLocked(); becomeInactiveIfAppropriateLocked(); Loading Loading @@ -2767,10 +2910,11 @@ public class DeviceIdleController extends SystemService mNextIdleDelay = 0; mNextIdleDelay = 0; mNextLightIdleDelay = 0; mNextLightIdleDelay = 0; mIdleStartTime = 0; mIdleStartTime = 0; mQuickDozeActivatedWhileIdling = false; cancelAlarmLocked(); cancelAlarmLocked(); cancelSensingTimeoutAlarmLocked(); cancelSensingTimeoutAlarmLocked(); cancelLocatingLocked(); cancelLocatingLocked(); stopMonitoringMotionLocked(); maybeStopMonitoringMotionLocked(); mAnyMotionDetector.stop(); mAnyMotionDetector.stop(); updateActiveConstraintsLocked(); updateActiveConstraintsLocked(); } } Loading Loading @@ -3270,11 +3414,23 @@ public class DeviceIdleController extends SystemService void motionLocked() { void motionLocked() { if (DEBUG) Slog.d(TAG, "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"); handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion"); } } void handleMotionDetectedLocked(long timeout, String type) { 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 // 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 // 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. // after moving out of the inactive state, so no need to worry about that. Loading Loading @@ -3326,10 +3482,15 @@ public class DeviceIdleController extends SystemService } } } } void stopMonitoringMotionLocked() { /** if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()"); * Stops motion monitoring. Will not stop monitoring if there are registered stationary if (mMotionSensor != null && mMotionListener.active) { * listeners. */ private void maybeStopMonitoringMotionLocked() { if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) { mMotionListener.unregisterLocked(); mMotionListener.unregisterLocked(); cancelMotionTimeoutAlarmLocked(); } } } } Loading @@ -3356,6 +3517,10 @@ public class DeviceIdleController extends SystemService } } } } private void cancelMotionTimeoutAlarmLocked() { mAlarmManager.cancel(mMotionTimeoutAlarmListener); } void cancelSensingTimeoutAlarmLocked() { void cancelSensingTimeoutAlarmLocked() { if (mNextSensingTimeoutAlarmTime != 0) { if (mNextSensingTimeoutAlarmTime != 0) { mNextSensingTimeoutAlarmTime = 0; mNextSensingTimeoutAlarmTime = 0; Loading Loading @@ -3402,6 +3567,14 @@ public class DeviceIdleController extends SystemService mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); 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) { void scheduleSensingTimeoutAlarmLocked(long delay) { if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay; mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay; Loading Loading @@ -4322,9 +4495,14 @@ public class DeviceIdleController extends SystemService } } pw.println(" }"); pw.println(" }"); } } if (mUseMotionSensor) { if (mUseMotionSensor || mStationaryListeners.size() > 0) { pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); 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(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); pw.print(mHasGps); pw.print(" mHasNetwork="); pw.print(mHasGps); pw.print(" mHasNetwork="); Loading services/core/java/com/android/server/location/GnssLocationProvider.java +37 −6 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,8 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.location.ProviderRequest; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.telephony.TelephonyIntents; 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.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; Loading Loading @@ -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_MSA = 0x02; private static final int AGPS_SUPL_MODE_MSB = 0x01; 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 SET_REQUEST = 3; private static final int INJECT_NTP_TIME = 5; private static final int INJECT_NTP_TIME = 5; // PSDS stands for Predicted Satellite Data Service // PSDS stands for Predicted Satellite Data Service Loading Loading @@ -370,6 +373,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // True if gps should be disabled because of PowerManager controls // True if gps should be disabled because of PowerManager controls private boolean mDisableGpsForPowerManager = false; 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. * Properties loaded from PROPERTIES_FILE. * It must be accessed only inside {@link #mHandler}. * It must be accessed only inside {@link #mHandler}. Loading Loading @@ -462,6 +471,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { return mGnssNavigationMessageProvider; 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() { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @Override public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { Loading @@ -478,11 +496,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case ALARM_TIMEOUT: case ALARM_TIMEOUT: hibernate(); hibernate(); break; break; case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED: case PowerManager.ACTION_DEVICE_IDLE_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_OFF: case Intent.ACTION_SCREEN_ON: 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; break; case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: Loading Loading @@ -540,10 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } private void updateLowPowerMode() { private void updateLowPowerMode() { // Disable GPS if we are in device idle mode. // Disable GPS if we are in device idle mode and the device is stationary. boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode(); boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary; final PowerSaveState result = final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION); mPowerManager.getPowerSaveState(ServiceType.LOCATION); switch (result.locationMode) { switch (result.locationMode) { case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: Loading Loading @@ -2048,6 +2076,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case REPORT_SV_STATUS: case REPORT_SV_STATUS: handleReportSvStatus((SvStatusInfo) msg.obj); handleReportSvStatus((SvStatusInfo) msg.obj); break; break; case UPDATE_LOW_POWER_MODE: updateLowPowerMode(); break; } } if (msg.arg2 == 1) { if (msg.arg2 == 1) { // wakelock was taken for this message, release it // wakelock was taken for this message, release it Loading services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +125 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/DeviceIdleController.java +187 −9 Original line number Original line Diff line number Diff line Loading @@ -291,6 +291,7 @@ public class DeviceIdleController extends SystemService private boolean mLightEnabled; private boolean mLightEnabled; private boolean mDeepEnabled; private boolean mDeepEnabled; private boolean mQuickDozeActivated; private boolean mQuickDozeActivated; private boolean mQuickDozeActivatedWhileIdling; private boolean mForceIdle; private boolean mForceIdle; private boolean mNetworkConnected; private boolean mNetworkConnected; private boolean mScreenOn; private boolean mScreenOn; Loading @@ -302,6 +303,10 @@ public class DeviceIdleController extends SystemService private boolean mHasNetworkLocation; private boolean mHasNetworkLocation; private Location mLastGenericLocation; private Location mLastGenericLocation; private Location mLastGpsLocation; 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 // Current locked state of the screen private boolean mScreenLocked; private boolean mScreenLocked; private int mNumBlockingConstraints = 0; private int mNumBlockingConstraints = 0; Loading Loading @@ -547,6 +552,9 @@ public class DeviceIdleController extends SystemService */ */ private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>(); 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_NULL = 0; private static final int EVENT_NORMAL = 1; private static final int EVENT_NORMAL = 1; private static final int EVENT_LIGHT_IDLE = 2; private static final int EVENT_LIGHT_IDLE = 2; Loading Loading @@ -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 private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener = new AlarmManager.OnAlarmListener() { = new AlarmManager.OnAlarmListener() { @Override @Override Loading Loading @@ -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 @VisibleForTesting final class MotionListener extends TriggerEventListener final class MotionListener extends TriggerEventListener implements SensorEventListener { implements SensorEventListener { boolean active = false; 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() { public boolean isActive() { return active; return active; } } Loading @@ -667,7 +748,6 @@ 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(); } } } } Loading @@ -675,8 +755,6 @@ 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(); } } } } Loading @@ -694,6 +772,7 @@ public class DeviceIdleController extends SystemService } } if (success) { if (success) { active = true; active = true; activatedTimeElapsed = mInjector.getElapsedRealtime(); } else { } else { Slog.e(TAG, "Unable to register for " + mMotionSensor); Slog.e(TAG, "Unable to register for " + mMotionSensor); } } Loading Loading @@ -1307,6 +1386,8 @@ public class DeviceIdleController extends SystemService private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; 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_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11; private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12; 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 { final class MyHandler extends Handler { MyHandler(Looper looper) { MyHandler(Looper looper) { Loading Loading @@ -1443,6 +1524,30 @@ public class DeviceIdleController extends SystemService updatePreIdleFactor(); updatePreIdleFactor(); maybeDoImmediateMaintenance(); maybeDoImmediateMaintenance(); } break; } 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; } } } } } } Loading Loading @@ -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 class LocalService { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { synchronized (DeviceIdleController.this) { synchronized (DeviceIdleController.this) { Loading Loading @@ -1693,6 +1811,24 @@ public class DeviceIdleController extends SystemService public int[] getPowerSaveTempWhitelistAppIds() { public int[] getPowerSaveTempWhitelistAppIds() { return DeviceIdleController.this.getAppIdTempWhitelistInternal(); 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 { static class Injector { Loading Loading @@ -1734,6 +1870,11 @@ public class DeviceIdleController extends SystemService return mConstants; return mConstants; } } /** Returns the current elapsed realtime in milliseconds. */ long getElapsedRealtime() { return SystemClock.elapsedRealtime(); } LocationManager getLocationManager() { LocationManager getLocationManager() { if (mLocationManager == null) { if (mLocationManager == null) { mLocationManager = mContext.getSystemService(LocationManager.class); mLocationManager = mContext.getSystemService(LocationManager.class); Loading Loading @@ -2601,6 +2742,8 @@ public class DeviceIdleController extends SystemService void updateQuickDozeFlagLocked(boolean enabled) { void updateQuickDozeFlagLocked(boolean enabled) { if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled); if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled); mQuickDozeActivated = enabled; mQuickDozeActivated = enabled; mQuickDozeActivatedWhileIdling = mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE); if (enabled) { if (enabled) { // If Quick Doze is enabled, see if we should go straight into it. // If Quick Doze is enabled, see if we should go straight into it. becomeInactiveIfAppropriateLocked(); becomeInactiveIfAppropriateLocked(); Loading Loading @@ -2767,10 +2910,11 @@ public class DeviceIdleController extends SystemService mNextIdleDelay = 0; mNextIdleDelay = 0; mNextLightIdleDelay = 0; mNextLightIdleDelay = 0; mIdleStartTime = 0; mIdleStartTime = 0; mQuickDozeActivatedWhileIdling = false; cancelAlarmLocked(); cancelAlarmLocked(); cancelSensingTimeoutAlarmLocked(); cancelSensingTimeoutAlarmLocked(); cancelLocatingLocked(); cancelLocatingLocked(); stopMonitoringMotionLocked(); maybeStopMonitoringMotionLocked(); mAnyMotionDetector.stop(); mAnyMotionDetector.stop(); updateActiveConstraintsLocked(); updateActiveConstraintsLocked(); } } Loading Loading @@ -3270,11 +3414,23 @@ public class DeviceIdleController extends SystemService void motionLocked() { void motionLocked() { if (DEBUG) Slog.d(TAG, "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"); handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion"); } } void handleMotionDetectedLocked(long timeout, String type) { 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 // 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 // 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. // after moving out of the inactive state, so no need to worry about that. Loading Loading @@ -3326,10 +3482,15 @@ public class DeviceIdleController extends SystemService } } } } void stopMonitoringMotionLocked() { /** if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()"); * Stops motion monitoring. Will not stop monitoring if there are registered stationary if (mMotionSensor != null && mMotionListener.active) { * listeners. */ private void maybeStopMonitoringMotionLocked() { if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) { mMotionListener.unregisterLocked(); mMotionListener.unregisterLocked(); cancelMotionTimeoutAlarmLocked(); } } } } Loading @@ -3356,6 +3517,10 @@ public class DeviceIdleController extends SystemService } } } } private void cancelMotionTimeoutAlarmLocked() { mAlarmManager.cancel(mMotionTimeoutAlarmListener); } void cancelSensingTimeoutAlarmLocked() { void cancelSensingTimeoutAlarmLocked() { if (mNextSensingTimeoutAlarmTime != 0) { if (mNextSensingTimeoutAlarmTime != 0) { mNextSensingTimeoutAlarmTime = 0; mNextSensingTimeoutAlarmTime = 0; Loading Loading @@ -3402,6 +3567,14 @@ public class DeviceIdleController extends SystemService mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); 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) { void scheduleSensingTimeoutAlarmLocked(long delay) { if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay; mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay; Loading Loading @@ -4322,9 +4495,14 @@ public class DeviceIdleController extends SystemService } } pw.println(" }"); pw.println(" }"); } } if (mUseMotionSensor) { if (mUseMotionSensor || mStationaryListeners.size() > 0) { pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); 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(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); pw.print(mHasGps); pw.print(" mHasNetwork="); pw.print(mHasGps); pw.print(" mHasNetwork="); Loading
services/core/java/com/android/server/location/GnssLocationProvider.java +37 −6 Original line number Original line Diff line number Diff line Loading @@ -75,6 +75,8 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.location.ProviderRequest; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.telephony.TelephonyIntents; 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.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; Loading Loading @@ -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_MSA = 0x02; private static final int AGPS_SUPL_MODE_MSB = 0x01; 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 SET_REQUEST = 3; private static final int INJECT_NTP_TIME = 5; private static final int INJECT_NTP_TIME = 5; // PSDS stands for Predicted Satellite Data Service // PSDS stands for Predicted Satellite Data Service Loading Loading @@ -370,6 +373,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // True if gps should be disabled because of PowerManager controls // True if gps should be disabled because of PowerManager controls private boolean mDisableGpsForPowerManager = false; 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. * Properties loaded from PROPERTIES_FILE. * It must be accessed only inside {@link #mHandler}. * It must be accessed only inside {@link #mHandler}. Loading Loading @@ -462,6 +471,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { return mGnssNavigationMessageProvider; 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() { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @Override public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { Loading @@ -478,11 +496,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case ALARM_TIMEOUT: case ALARM_TIMEOUT: hibernate(); hibernate(); break; break; case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED: case PowerManager.ACTION_DEVICE_IDLE_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_OFF: case Intent.ACTION_SCREEN_ON: 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; break; case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: Loading Loading @@ -540,10 +569,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } private void updateLowPowerMode() { private void updateLowPowerMode() { // Disable GPS if we are in device idle mode. // Disable GPS if we are in device idle mode and the device is stationary. boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode(); boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary; final PowerSaveState result = final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION); mPowerManager.getPowerSaveState(ServiceType.LOCATION); switch (result.locationMode) { switch (result.locationMode) { case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: Loading Loading @@ -2048,6 +2076,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case REPORT_SV_STATUS: case REPORT_SV_STATUS: handleReportSvStatus((SvStatusInfo) msg.obj); handleReportSvStatus((SvStatusInfo) msg.obj); break; break; case UPDATE_LOW_POWER_MODE: updateLowPowerMode(); break; } } if (msg.arg2 == 1) { if (msg.arg2 == 1) { // wakelock was taken for this message, release it // wakelock was taken for this message, release it Loading
services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +125 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes