Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0204b63e authored by Kweku Adams's avatar Kweku Adams Committed by android-build-merger
Browse files

Merge "Don't disable GPS in Doze until the device is stationary." into qt-qpr1-dev

am: 858b6833

Change-Id: I13305fa7f2c7ee70d9ba925456ac2046ae4424c4
parents 2675ea8f 858b6833
Loading
Loading
Loading
Loading
+187 −9
Original line number Diff line number Diff line
@@ -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=");
+37 −6
Original line number Diff line number Diff line
@@ -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
+125 −5

File changed.

Preview size limit exceeded, changes collapsed.