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

Commit 008fa43f authored by Prem's avatar Prem
Browse files

Enable quick doze using mode manager

Bug: 289979530

Test: atest FrameworksMockingServicesTests: com.android.server.DeviceIdleControllerTest

Change-Id: I86c932fa756a1c09bfbcf81f2cf966c45d8f0af7
parent 0da94ffc
Loading
Loading
Loading
Loading
+78 −102
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WearModeManagerInternal;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -126,6 +127,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
@@ -373,10 +375,9 @@ public class DeviceIdleController extends SystemService
    @GuardedBy("this")
    private boolean mBatterySaverEnabled;
    @GuardedBy("this")
    private boolean mIsOffBody;
    private boolean mModeManagerRequestedQuickDoze;
    @GuardedBy("this")
    private boolean mForceBodyState;
    private Sensor mOffBodySensor;
    private boolean mForceModeManagerQuickDozeRequest;

    /** Time in the elapsed realtime timebase when this listener last received a motion event. */
    @GuardedBy("this")
@@ -435,7 +436,7 @@ public class DeviceIdleController extends SystemService
    private static final int ACTIVE_REASON_FORCED = 6;
    private static final int ACTIVE_REASON_ALARM = 7;
    private static final int ACTIVE_REASON_EMERGENCY_CALL = 8;
    private static final int ACTIVE_REASON_ONBODY = 9;
    private static final int ACTIVE_REASON_MODE_MANAGER = 9;

    @VisibleForTesting
    static String stateToString(int state) {
@@ -832,64 +833,35 @@ public class DeviceIdleController extends SystemService
        }
    }

    /**
     * LowLatencyOffBodyListener monitors if a device is on body or off body.
     */
    @VisibleForTesting
    final class LowLatencyOffBodyListener implements SensorEventListener {
    class ModeManagerQuickDozeRequestConsumer implements Consumer<Boolean> {
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (DEBUG) {
                Slog.d(TAG, "LowLatencyOffBodyListener detects onSensorChanged event, values are: "
                        + Arrays.toString(event.values));
            }
            if (event.values == null || event.values.length == 0) {
                // The event returned should contain a single value to indicate off-body state.
                // No value indicates something went wrong. Take no action and log an error.
                Slog.e(TAG,
                        "LowLatencyOffBodyListener detects onSensorChanged event but no event "
                                + "value returns.");
                return;
            }
        public void accept(Boolean enabled) {
            Slog.d(TAG, "Mode manager quick doze request: " + enabled);
            synchronized (DeviceIdleController.this) {
                final boolean isOffBody = (event.values[0] == 0);
                if (!mForceBodyState && mIsOffBody != isOffBody) {
                    // Only consider the sensor value change when mForceBodyState is false, which
                    // is used to enforce the mIsOffBody to be set by the adb shell command.
                    mIsOffBody = isOffBody;
                    onOffBodyChangedLocked();
                if (!mForceModeManagerQuickDozeRequest
                        && mModeManagerRequestedQuickDoze != enabled) {
                    mModeManagerRequestedQuickDoze = enabled;
                    onModeManagerRequestChangedLocked();
                }
            }
        }

        @GuardedBy("DeviceIdleController.this")
        public void onOffBodyChangedLocked() {
            // Get into quick doze faster when the device is off body instead of taking
        public void onModeManagerRequestChangedLocked() {
            // Get into quick doze faster when mode manager requests instead of taking
            // traditional multi-stage approach.
            updateQuickDozeFlagLocked();
            if (!mIsOffBody && !mBatterySaverEnabled) {
                mActiveReason = ACTIVE_REASON_ONBODY;
                becomeActiveLocked("onbody", Process.myUid());
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {}

        public void registerLocked() {
            mOffBodySensor =
                    mSensorManager.getDefaultSensor(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
            if (mOffBodySensor == null) {
                Slog.w(TAG, "Body sensor is NULL, unable to register mOffBodySensor.");
                return;
            if (!mModeManagerRequestedQuickDoze && !mBatterySaverEnabled) {
                mActiveReason = ACTIVE_REASON_MODE_MANAGER;
                becomeActiveLocked("mode_manager", Process.myUid());
            }
            mSensorManager.registerListener(this, mOffBodySensor,
                    SensorManager.SENSOR_DELAY_NORMAL);
        }
    }

    @VisibleForTesting
    final LowLatencyOffBodyListener mLowLatencyOffBodyListener = new LowLatencyOffBodyListener();
    final ModeManagerQuickDozeRequestConsumer mModeManagerQuickDozeRequestConsumer =
            new ModeManagerQuickDozeRequestConsumer();

    @VisibleForTesting
    final class MotionListener extends TriggerEventListener
@@ -1052,7 +1024,7 @@ public class DeviceIdleController extends SystemService
         */
        private static final String KEY_WAIT_FOR_UNLOCK = "wait_for_unlock";
        private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
        private static final String KEY_USE_BODY_SENSOR = "use_body_sensor";
        private static final String KEY_USE_MODE_MANAGER = "use_mode_manager";

        private long mDefaultFlexTimeShort =
                !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
@@ -1112,7 +1084,7 @@ public class DeviceIdleController extends SystemService
        private long mDefaultNotificationAllowlistDurationMs = 30 * 1000L;
        private boolean mDefaultWaitForUnlock = true;
        private boolean mDefaultUseWindowAlarms = true;
        private boolean mDefaultUseBodySensor = false;
        private boolean mDefaultUseModeManager = false;

        /**
         * A somewhat short alarm window size that we will tolerate for various alarm timings.
@@ -1356,7 +1328,7 @@ public class DeviceIdleController extends SystemService
        /**
         * Whether to use an on/off body signal to affect state transition policy.
         */
        public boolean USE_BODY_SENSOR = mDefaultUseBodySensor;
        public boolean USE_MODE_MANAGER = mDefaultUseModeManager;

        private final boolean mSmallBatteryDevice;

@@ -1464,8 +1436,8 @@ public class DeviceIdleController extends SystemService
                    com.android.internal.R.bool.device_idle_wait_for_unlock);
            mDefaultUseWindowAlarms = res.getBoolean(
                    com.android.internal.R.bool.device_idle_use_window_alarms);
            mDefaultUseBodySensor = res.getBoolean(
                    com.android.internal.R.bool.device_idle_use_body_sensor);
            mDefaultUseModeManager = res.getBoolean(
                    com.android.internal.R.bool.device_idle_use_mode_manager);

            FLEX_TIME_SHORT = mDefaultFlexTimeShort;
            LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
@@ -1499,7 +1471,7 @@ public class DeviceIdleController extends SystemService
            NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
            WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
            USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
            USE_BODY_SENSOR = mDefaultUseBodySensor;
            USE_MODE_MANAGER = mDefaultUseModeManager;
        }

        private long getTimeout(long defTimeout, long compTimeout) {
@@ -1661,9 +1633,9 @@ public class DeviceIdleController extends SystemService
                            USE_WINDOW_ALARMS = properties.getBoolean(
                                    KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
                            break;
                        case KEY_USE_BODY_SENSOR:
                            USE_BODY_SENSOR = properties.getBoolean(
                                    KEY_USE_BODY_SENSOR, mDefaultUseBodySensor);
                        case KEY_USE_MODE_MANAGER:
                            USE_MODE_MANAGER = properties.getBoolean(
                                    KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
                            break;
                        default:
                            Slog.e(TAG, "Unknown configuration key: " + name);
@@ -1802,8 +1774,8 @@ public class DeviceIdleController extends SystemService
            pw.print("    "); pw.print(KEY_USE_WINDOW_ALARMS); pw.print("=");
            pw.println(USE_WINDOW_ALARMS);

            pw.print("    "); pw.print(KEY_USE_BODY_SENSOR); pw.print("=");
            pw.println(USE_BODY_SENSOR);
            pw.print("    "); pw.print(KEY_USE_MODE_MANAGER); pw.print("=");
            pw.println(USE_MODE_MANAGER);
        }
    }

@@ -2668,8 +2640,15 @@ public class DeviceIdleController extends SystemService
                        mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
                mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);

                if (mConstants.USE_BODY_SENSOR) {
                    mLowLatencyOffBodyListener.registerLocked();
                if (mConstants.USE_MODE_MANAGER) {
                    WearModeManagerInternal modeManagerInternal = LocalServices.getService(
                            WearModeManagerInternal.class);
                    if (modeManagerInternal != null) {
                        modeManagerInternal.addActiveStateChangeListener(
                                WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER,
                                AppSchedulingModuleThread.getExecutor(),
                                mModeManagerQuickDozeRequestConsumer);
                    }
                }
                mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
                        state -> {
@@ -3374,9 +3353,10 @@ public class DeviceIdleController extends SystemService
    /** Calls to {@link #updateQuickDozeFlagLocked(boolean)} by considering appropriate signals. */
    @GuardedBy("this")
    private void updateQuickDozeFlagLocked() {
        if (mConstants.USE_BODY_SENSOR) {
            // Only disable the quick doze flag when the device is on body and battery saver is off.
            updateQuickDozeFlagLocked(mIsOffBody || mBatterySaverEnabled);
        if (mConstants.USE_MODE_MANAGER) {
            // Only disable the quick doze flag when mode manager request is false and
            // battery saver is off.
            updateQuickDozeFlagLocked(mModeManagerRequestedQuickDoze || mBatterySaverEnabled);
        } else {
            updateQuickDozeFlagLocked(mBatterySaverEnabled);
        }
@@ -4482,7 +4462,7 @@ public class DeviceIdleController extends SystemService
        pw.println("  unforce");
        pw.println(
                "    Resume normal functioning after force-idle or force-inactive or "
                        + "force-offbody or force-onbody.");
                        + "force-modemanager-quickdoze.");
        pw.println("  get [light|deep|force|screen|charging|network|offbody|forcebodystate]");
        pw.println("    Retrieve the current given state.");
        pw.println("  disable [light|deep|all]");
@@ -4517,14 +4497,9 @@ public class DeviceIdleController extends SystemService
                + "and any [-d] is ignored");
        pw.println("  motion");
        pw.println("    Simulate a motion event to bring the device out of deep doze");
        pw.println("  force-offbody");
        pw.println(
                "    Simulate a low latency body sensor detecting a device is offbody. "
                        + "mForceBodyState will be set to true to ignore body sensor reading.");
        pw.println("  force-onbody");
        pw.println(
                "    Simulate a low latency body sensor detecting a device is onbody. "
                        + "mForceBodyState will be set to true to ignore body sensor reading.");
        pw.println("  force-modemanager-quickdoze [true|false]");
        pw.println("    Simulate mode manager request to enable (true) or disable (false) "
                + "quick doze. Mode manager changes will be ignored until unforce is called.");
    }

    class Shell extends ShellCommand {
@@ -4656,8 +4631,9 @@ public class DeviceIdleController extends SystemService
                    pw.print(lightStateToString(mLightState));
                    pw.print(", deep state: ");
                    pw.println(stateToString(mState));
                    mForceBodyState = false;
                    pw.println("mForceBodyState: " + mForceBodyState);
                    mForceModeManagerQuickDozeRequest = false;
                    pw.println("mForceModeManagerQuickDozeRequest: "
                            + mForceModeManagerQuickDozeRequest);
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
@@ -4678,8 +4654,12 @@ public class DeviceIdleController extends SystemService
                            case "screen": pw.println(mScreenOn); break;
                            case "charging": pw.println(mCharging); break;
                            case "network": pw.println(mNetworkConnected); break;
                            case "offbody": pw.println(mIsOffBody); break;
                            case "forcebodystate": pw.println(mForceBodyState); break;
                            case "modemanagerquick":
                                pw.println(mModeManagerRequestedQuickDoze);
                                break;
                            case "forcemodemanagerquick":
                                pw.println(mForceModeManagerQuickDozeRequest);
                                break;
                            default: pw.println("Unknown get option: " + arg); break;
                        }
                    } finally {
@@ -4976,35 +4956,31 @@ public class DeviceIdleController extends SystemService
                    Binder.restoreCallingIdentity(token);
                }
            }
        } else if ("force-offbody".equals(cmd)) {
        } else if ("force-modemanager-quickdoze".equals(cmd)) {
            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                    null);
            String arg = shell.getNextArg();

            if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) {
                boolean enabled = Boolean.parseBoolean(arg);

                synchronized (DeviceIdleController.this) {
                    final long token = Binder.clearCallingIdentity();
                    try {
                    mForceBodyState = true;
                    pw.println("mForceBodyState: " + mForceBodyState);
                    mIsOffBody = true;
                    pw.println("mIsOffBody: " + mIsOffBody);
                    mLowLatencyOffBodyListener.onOffBodyChangedLocked();
                        mForceModeManagerQuickDozeRequest = true;
                        pw.println("mForceModeManagerQuickDozeRequest: "
                                + mForceModeManagerQuickDozeRequest);
                        mModeManagerRequestedQuickDoze = enabled;
                        pw.println("mModeManagerRequestedQuickDoze: "
                                + mModeManagerRequestedQuickDoze);
                        mModeManagerQuickDozeRequestConsumer.onModeManagerRequestChangedLocked();
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
        } else if ("force-onbody".equals(cmd)) {
            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                    null);
            synchronized (DeviceIdleController.this) {
                final long token = Binder.clearCallingIdentity();
                try {
                    mForceBodyState = true;
                    pw.println("mForceBodyState: " + mForceBodyState);
                    mIsOffBody = false;
                    pw.println("mIsOffBody: " + mIsOffBody);
                    mLowLatencyOffBodyListener.onOffBodyChangedLocked();
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
            } else {
                pw.println("Provide true or false argument after force-modemanager-quickdoze");
                return -1;
            }
        } else {
            return shell.handleDefaultCommands(cmd);
+2 −2
Original line number Diff line number Diff line
@@ -120,7 +120,7 @@
    <!-- Default for DeviceIdleController.Constants.USE_WINDOW_ALARMS -->
    <bool name="device_idle_use_window_alarms">true</bool>

    <!-- Default for DeviceIdleController.Constants.USE_BODY_SENSOR -->
    <bool name="device_idle_use_body_sensor">false</bool>
    <!-- Default for DeviceIdleController.Constants.USE_MODE_MANAGER -->
    <bool name="device_idle_use_mode_manager">false</bool>
</resources>
+1 −1
Original line number Diff line number Diff line
@@ -4520,7 +4520,7 @@
  <java-symbol type="integer" name="device_idle_notification_allowlist_duration_ms" />
  <java-symbol type="bool" name="device_idle_wait_for_unlock" />
  <java-symbol type="bool" name="device_idle_use_window_alarms" />
  <java-symbol type="bool" name="device_idle_use_body_sensor" />
  <java-symbol type="bool" name="device_idle_use_mode_manager" />

  <!-- Binder heavy hitter watcher configs -->
  <java-symbol type="bool" name="config_defaultBinderHeavyHitterWatcherEnabled" />
+26 −45
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -83,6 +82,7 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.SystemClock;
import android.os.WearModeManagerInternal;
import android.provider.DeviceConfig;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -147,6 +147,8 @@ public class DeviceIdleControllerTest {
    private TelephonyManager mTelephonyManager;
    @Mock
    private Sensor mOffBodySensor;
    @Mock
    private WearModeManagerInternal mWearModeManagerInternal;

    class InjectorForTest extends DeviceIdleController.Injector {
        ConnectivityManager connectivityManager;
@@ -348,6 +350,9 @@ public class DeviceIdleControllerTest {
        mAnyMotionDetector = new AnyMotionDetectorForTest();
        mInjector = new InjectorForTest(getContext());

        doReturn(mWearModeManagerInternal)
                .when(() -> LocalServices.getService(WearModeManagerInternal.class));

        setupDeviceIdleController();
    }

@@ -2413,22 +2418,20 @@ public class DeviceIdleControllerTest {
    }

    @Test
    public void testLowLatencyBodyDetection_NoBodySensor() {
        mConstants.USE_BODY_SENSOR = true;
        doReturn(null).when(mSensorManager).getDefaultSensor(
                eq(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT), anyBoolean());
    public void testModeManager_NoModeManagerLocalService_AddListenerNotCalled() {
        mConstants.USE_MODE_MANAGER = true;
        doReturn(null)
                .when(() -> LocalServices.getService(WearModeManagerInternal.class));
        cleanupDeviceIdleController();
        setupDeviceIdleController();
        verify(mSensorManager, never())
                .registerListener(any(), any(), anyInt());
        verify(mWearModeManagerInternal, never()).addActiveStateChangeListener(
                eq(WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER), any(),
                eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer));
    }

    @Test
    public void testLowLatencyBodyDetection_NoBatterySaver_QuickDoze() {
        mConstants.USE_BODY_SENSOR = true;
        doReturn(mOffBodySensor)
                .when(mSensorManager)
                .getDefaultSensor(eq(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT), anyBoolean());
    public void testModeManager_NoBatterySaver_QuickDoze() {
        mConstants.USE_MODE_MANAGER = true;
        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
                false).build();
        when(mPowerManagerInternal.getLowPowerState(anyInt()))
@@ -2436,32 +2439,19 @@ public class DeviceIdleControllerTest {
        cleanupDeviceIdleController();
        setupDeviceIdleController();

        ArgumentCaptor<SensorEventListener> listenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);
        verify(mSensorManager)
                .registerListener(listenerCaptor.capture(), eq(mOffBodySensor),
                        eq(SensorManager.SENSOR_DELAY_NORMAL));
        final SensorEventListener listener = listenerCaptor.getValue();
        // Set the device as off body
        float[] valsZero = {0.0f};
        SensorEvent offbodyEvent = new SensorEvent(mOffBodySensor, 1, 1L, valsZero);
        listener.onSensorChanged(offbodyEvent);
        // Mode manager quick doze request: true.
        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
        assertTrue(mDeviceIdleController.isQuickDozeEnabled());

        // Set the device as on body
        float[] valsNonZero = {1.0f};
        SensorEvent onbodyEvent = new SensorEvent(mOffBodySensor, 1, 1L, valsNonZero);
        listener.onSensorChanged(onbodyEvent);
        // Mode manager quick doze request: false.
        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
        assertFalse(mDeviceIdleController.isQuickDozeEnabled());
        verifyStateConditions(STATE_ACTIVE);
    }

    @Test
    public void testLowLatencyBodyDetection_WithBatterySaver_QuickDoze() {
        mConstants.USE_BODY_SENSOR = true;
        doReturn(mOffBodySensor)
                .when(mSensorManager)
                .getDefaultSensor(eq(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT), anyBoolean());
    public void testModeManager_WithBatterySaver_QuickDoze() {
        mConstants.USE_MODE_MANAGER = true;
        PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled(
                true).build();
        when(mPowerManagerInternal.getLowPowerState(anyInt()))
@@ -2469,22 +2459,13 @@ public class DeviceIdleControllerTest {
        cleanupDeviceIdleController();
        setupDeviceIdleController();

        ArgumentCaptor<SensorEventListener> listenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);
        verify(mSensorManager)
                .registerListener(listenerCaptor.capture(), eq(mOffBodySensor),
                        eq(SensorManager.SENSOR_DELAY_NORMAL));
        final SensorEventListener listener = listenerCaptor.getValue();
        // Set the device as off body
        float[] valsZero = {0.0f};
        SensorEvent offbodyEvent = new SensorEvent(mOffBodySensor, 1, 1L, valsZero);
        listener.onSensorChanged(offbodyEvent);
        // Mode manager quick doze request: true.
        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true);
        assertTrue(mDeviceIdleController.isQuickDozeEnabled());

        // Set the device as on body. Quick doze should remain enabled because battery saver is on.
        float[] valsNonZero = {1.0f};
        SensorEvent onbodyEvent = new SensorEvent(mOffBodySensor, 1, 1L, valsNonZero);
        listener.onSensorChanged(onbodyEvent);
        // Mode manager quick doze request: false.
        // Quick doze should remain enabled because battery saver is on.
        mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false);
        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
    }