diff --git a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java index 969975762c91f98f036c947a3d4766885fa75ca5..75838c213eb6ccd7af258aa2af6bdb84a4717b84 100644 --- a/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java +++ b/apex/jobscheduler/framework/java/android/os/WearModeManagerInternal.java @@ -48,6 +48,17 @@ public interface WearModeManagerInternal { */ String QUICK_DOZE_REQUEST_IDENTIFIER = "quick_doze_request"; + /** + * Mode manager off body state identifier. + * + * <p>Unique identifier that can be used as identifier parameter in + * registerInternalStateObserver + * to listen to changes in quick doze request state from mode manager. + * + * TODO(b/288276510): convert to int constant + */ + String OFFBODY_STATE_ID = "off_body"; + /** * StringDef for Mode manager identifiers. * @@ -55,7 +66,8 @@ public interface WearModeManagerInternal { */ @Retention(RetentionPolicy.SOURCE) @StringDef({ - QUICK_DOZE_REQUEST_IDENTIFIER + QUICK_DOZE_REQUEST_IDENTIFIER, + OFFBODY_STATE_ID }) @Target(ElementType.TYPE_USE) @interface Identifier { diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index b8596d5a392644a7b984386287b886107262e2c7..1eaabf5b884f7e29cd371050b2a5dadbf620a887 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -377,7 +377,11 @@ public class DeviceIdleController extends SystemService @GuardedBy("this") private boolean mModeManagerRequestedQuickDoze; @GuardedBy("this") + private boolean mIsOffBody; + @GuardedBy("this") private boolean mForceModeManagerQuickDozeRequest; + @GuardedBy("this") + private boolean mForceModeManagerOffBodyState; /** Time in the elapsed realtime timebase when this listener last received a motion event. */ @GuardedBy("this") @@ -437,6 +441,7 @@ public class DeviceIdleController extends SystemService private static final int ACTIVE_REASON_ALARM = 7; private static final int ACTIVE_REASON_EMERGENCY_CALL = 8; private static final int ACTIVE_REASON_MODE_MANAGER = 9; + private static final int ACTIVE_REASON_ONBODY = 10; @VisibleForTesting static String stateToString(int state) { @@ -837,7 +842,7 @@ public class DeviceIdleController extends SystemService class ModeManagerQuickDozeRequestConsumer implements Consumer<Boolean> { @Override public void accept(Boolean enabled) { - Slog.d(TAG, "Mode manager quick doze request: " + enabled); + Slog.i(TAG, "Mode manager quick doze request: " + enabled); synchronized (DeviceIdleController.this) { if (!mForceModeManagerQuickDozeRequest && mModeManagerRequestedQuickDoze != enabled) { @@ -848,13 +853,46 @@ public class DeviceIdleController extends SystemService } @GuardedBy("DeviceIdleController.this") - public void onModeManagerRequestChangedLocked() { + private void onModeManagerRequestChangedLocked() { // Get into quick doze faster when mode manager requests instead of taking // traditional multi-stage approach. + maybeBecomeActiveOnModeManagerEventsLocked(); updateQuickDozeFlagLocked(); - if (!mModeManagerRequestedQuickDoze && !mBatterySaverEnabled) { - mActiveReason = ACTIVE_REASON_MODE_MANAGER; - becomeActiveLocked("mode_manager", Process.myUid()); + } + } + + @VisibleForTesting + class ModeManagerOffBodyStateConsumer implements Consumer<Boolean> { + @Override + public void accept(Boolean isOffBody) { + Slog.i(TAG, "Offbody event from mode manager: " + isOffBody); + synchronized (DeviceIdleController.this) { + if (!mForceModeManagerOffBodyState && mIsOffBody != isOffBody) { + mIsOffBody = isOffBody; + onModeManagerOffBodyChangedLocked(); + } + } + } + + @GuardedBy("DeviceIdleController.this") + private void onModeManagerOffBodyChangedLocked() { + maybeBecomeActiveOnModeManagerEventsLocked(); + } + } + + @GuardedBy("DeviceIdleController.this") + private void maybeBecomeActiveOnModeManagerEventsLocked() { + synchronized (DeviceIdleController.this) { + if (mQuickDozeActivated) { + // Quick doze is enabled so don't turn the device active. + return; + } + // Fall through when quick doze is not requested. + + if (!mIsOffBody) { + // Quick doze was not requested and device is on body so turn the device active. + mActiveReason = ACTIVE_REASON_ONBODY; + becomeActiveLocked("on_body", Process.myUid()); } } } @@ -863,6 +901,10 @@ public class DeviceIdleController extends SystemService final ModeManagerQuickDozeRequestConsumer mModeManagerQuickDozeRequestConsumer = new ModeManagerQuickDozeRequestConsumer(); + @VisibleForTesting + final ModeManagerOffBodyStateConsumer mModeManagerOffBodyStateConsumer = + new ModeManagerOffBodyStateConsumer(); + @VisibleForTesting final class MotionListener extends TriggerEventListener implements SensorEventListener { @@ -2648,6 +2690,12 @@ public class DeviceIdleController extends SystemService WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER, AppSchedulingModuleThread.getExecutor(), mModeManagerQuickDozeRequestConsumer); + + modeManagerInternal.addActiveStateChangeListener( + WearModeManagerInternal.OFFBODY_STATE_ID, + AppSchedulingModuleThread.getExecutor(), + mModeManagerOffBodyStateConsumer + ); } } mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE, @@ -4463,7 +4511,7 @@ public class DeviceIdleController extends SystemService pw.println( " Resume normal functioning after force-idle or force-inactive or " + "force-modemanager-quickdoze."); - pw.println(" get [light|deep|force|screen|charging|network|offbody|forcebodystate]"); + pw.println(" get [light|deep|force|screen|charging|network|offbody|forceoffbody]"); pw.println(" Retrieve the current given state."); pw.println(" disable [light|deep|all]"); pw.println(" Completely disable device idle mode."); @@ -4500,6 +4548,10 @@ public class DeviceIdleController extends SystemService 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."); + pw.println(" force-modemanager-offbody [true|false]"); + pw.println(" Force mode manager offbody state, this can be used to simulate " + + "device being off-body (true) or on-body (false). Mode manager changes " + + "will be ignored until unforce is called."); } class Shell extends ShellCommand { @@ -4634,6 +4686,9 @@ public class DeviceIdleController extends SystemService mForceModeManagerQuickDozeRequest = false; pw.println("mForceModeManagerQuickDozeRequest: " + mForceModeManagerQuickDozeRequest); + mForceModeManagerOffBodyState = false; + pw.println("mForceModeManagerOffBodyState: " + + mForceModeManagerOffBodyState); } finally { Binder.restoreCallingIdentity(token); } @@ -4660,6 +4715,8 @@ public class DeviceIdleController extends SystemService case "forcemodemanagerquick": pw.println(mForceModeManagerQuickDozeRequest); break; + case "offbody": pw.println(mIsOffBody); break; + case "forceoffbody": pw.println(mForceModeManagerOffBodyState); break; default: pw.println("Unknown get option: " + arg); break; } } finally { @@ -4982,6 +5039,31 @@ public class DeviceIdleController extends SystemService pw.println("Provide true or false argument after force-modemanager-quickdoze"); return -1; } + } else if ("force-modemanager-offbody".equals(cmd)) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + String arg = shell.getNextArg(); + + if ("true".equalsIgnoreCase(arg) || "false".equalsIgnoreCase(arg)) { + boolean isOffBody = Boolean.parseBoolean(arg); + + synchronized (DeviceIdleController.this) { + final long token = Binder.clearCallingIdentity(); + try { + mForceModeManagerOffBodyState = true; + pw.println("mForceModeManagerOffBodyState: " + + mForceModeManagerOffBodyState); + mIsOffBody = isOffBody; + pw.println("mIsOffBody: " + mIsOffBody); + mModeManagerOffBodyStateConsumer.onModeManagerOffBodyChangedLocked(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } else { + pw.println("Provide true or false argument after force-modemanager-offbody"); + return -1; + } } else { return shell.handleDefaultCommands(cmd); } @@ -5233,6 +5315,12 @@ public class DeviceIdleController extends SystemService if (mAlarmsActive) { pw.print(" mAlarmsActive="); pw.println(mAlarmsActive); } + if (mConstants.USE_MODE_MANAGER) { + pw.print(" mModeManagerRequestedQuickDoze="); + pw.println(mModeManagerRequestedQuickDoze); + pw.print(" mIsOffBody"); + pw.println(mIsOffBody); + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 525bfd75269b1b4434f3f1454d3322552415c18c..5b1508b8393be1e8eff82cb4c40610169d085849 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -16,7 +16,6 @@ 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; @@ -42,7 +41,6 @@ import static com.android.server.DeviceIdleController.STATE_QUICK_DOZE_DELAY; import static com.android.server.DeviceIdleController.STATE_SENSING; import static com.android.server.DeviceIdleController.lightStateToString; import static com.android.server.DeviceIdleController.stateToString; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -2418,7 +2416,7 @@ public class DeviceIdleControllerTest { } @Test - public void testModeManager_NoModeManagerLocalService_AddListenerNotCalled() { + public void testModeManager_NoModeManagerLocalService_AddQuickDozeListenerNotCalled() { mConstants.USE_MODE_MANAGER = true; doReturn(null) .when(() -> LocalServices.getService(WearModeManagerInternal.class)); @@ -2429,6 +2427,33 @@ public class DeviceIdleControllerTest { eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer)); } + @Test + public void testModeManager_NoModeManagerLocalService_AddOffBodyListenerNotCalled() { + mConstants.USE_MODE_MANAGER = true; + doReturn(null) + .when(() -> LocalServices.getService(WearModeManagerInternal.class)); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + verify(mWearModeManagerInternal, never()).addActiveStateChangeListener( + eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(), + eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer)); + } + + @Test + public void testModeManager_USEMODEMANAGERIsFalse_AddListenerNotCalled() { + mConstants.USE_MODE_MANAGER = false; + doReturn(new Object()) + .when(() -> LocalServices.getService(WearModeManagerInternal.class)); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + verify(mWearModeManagerInternal, never()).addActiveStateChangeListener( + eq(WearModeManagerInternal.OFFBODY_STATE_ID), any(), + eq(mDeviceIdleController.mModeManagerOffBodyStateConsumer)); + verify(mWearModeManagerInternal, never()).addActiveStateChangeListener( + eq(WearModeManagerInternal.QUICK_DOZE_REQUEST_IDENTIFIER), any(), + eq(mDeviceIdleController.mModeManagerQuickDozeRequestConsumer)); + } + @Test public void testModeManager_NoBatterySaver_QuickDoze() { mConstants.USE_MODE_MANAGER = true; @@ -2469,6 +2494,102 @@ public class DeviceIdleControllerTest { assertTrue(mDeviceIdleController.isQuickDozeEnabled()); } + @Test + public void testModeManager_QuickDozeRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + true).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + + @Test + public void testModeManager_QuickDozeRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + true).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + + @Test + public void testModeManager_QuickDozeRequestedBatterySaverDisabledOnBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + false).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + + @Test + public void testModeManager_QuickDozeRequestedBatterySaverDisabledOffBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + false).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(true); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + + @Test + public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOnBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + true).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(false); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + + @Test + public void testModeManager_QuickDozeNotRequestedBatterySaverEnabledOffBody_QuickDozeEnabled() { + mConstants.USE_MODE_MANAGER = true; + PowerSaveState powerSaveState = new PowerSaveState.Builder().setBatterySaverEnabled( + true).build(); + when(mPowerManagerInternal.getLowPowerState(anyInt())) + .thenReturn(powerSaveState); + cleanupDeviceIdleController(); + setupDeviceIdleController(); + + mDeviceIdleController.mModeManagerOffBodyStateConsumer.accept(true); + mDeviceIdleController.mModeManagerQuickDozeRequestConsumer.accept(false); + + assertTrue(mDeviceIdleController.isQuickDozeEnabled()); + } + private void enterDeepState(int state) { switch (state) { case STATE_ACTIVE: