diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index cd38e8a01d627..dd5373ff8c388 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -58,4 +58,32 @@ public abstract class DreamManagerInternal { * @param isScreenOn True if the screen is currently on. */ public abstract boolean canStartDreaming(boolean isScreenOn); + + /** + * Register a {@link DreamManagerStateListener}, which will be called when there are changes to + * dream state. + * + * @param listener The listener to register. + */ + public abstract void registerDreamManagerStateListener(DreamManagerStateListener listener); + + /** + * Unregister a {@link DreamManagerStateListener}, which will be called when there are changes + * to dream state. + * + * @param listener The listener to unregister. + */ + public abstract void unregisterDreamManagerStateListener(DreamManagerStateListener listener); + + /** + * Called when there are changes to dream state. + */ + public interface DreamManagerStateListener { + /** + * Called when keep dreaming when undocked has changed. + * + * @param keepDreaming True if the current dream should continue when undocking. + */ + void onKeepDreamingWhenUndockedChanged(boolean keepDreaming); + } } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 4a0ba2221ae79..ea096299cf48f 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -81,6 +81,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; /** * Service api for managing dreams. @@ -120,6 +121,10 @@ public final class DreamManagerService extends SystemService { private final boolean mDreamsEnabledByDefaultConfig; private final boolean mDreamsActivatedOnChargeByDefault; private final boolean mDreamsActivatedOnDockByDefault; + private final boolean mKeepDreamingWhenUndockedDefault; + + private final CopyOnWriteArrayList + mDreamManagerStateListeners = new CopyOnWriteArrayList<>(); @GuardedBy("mLock") private DreamRecord mCurrentDream; @@ -226,6 +231,8 @@ public final class DreamManagerService extends SystemService { mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean( com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault); mSettingsObserver = new SettingsObserver(mHandler); + mKeepDreamingWhenUndockedDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_keepDreamingWhenUndocking); } @Override @@ -294,12 +301,12 @@ public final class DreamManagerService extends SystemService { pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled); pw.println("mDreamsOnlyEnabledForDockUser=" + mDreamsOnlyEnabledForDockUser); pw.println("mDreamsEnabledSetting=" + mDreamsEnabledSetting); - pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled); pw.println("mDreamsActivatedOnDockByDefault=" + mDreamsActivatedOnDockByDefault); pw.println("mDreamsActivatedOnChargeByDefault=" + mDreamsActivatedOnChargeByDefault); pw.println("mIsDocked=" + mIsDocked); pw.println("mIsCharging=" + mIsCharging); pw.println("mWhenToDream=" + mWhenToDream); + pw.println("mKeepDreamingWhenUndockedDefault=" + mKeepDreamingWhenUndockedDefault); pw.println("getDozeComponent()=" + getDozeComponent()); pw.println(); @@ -328,7 +335,16 @@ public final class DreamManagerService extends SystemService { } } - /** Whether a real dream is occurring. */ + private void reportKeepDreamingWhenUndockedChanged(boolean keepDreaming) { + mHandler.post(() -> { + for (DreamManagerInternal.DreamManagerStateListener listener + : mDreamManagerStateListeners) { + listener.onKeepDreamingWhenUndockedChanged(keepDreaming); + } + }); + } + + /** Whether a real dream is occurring. */ private boolean isDreamingInternal() { synchronized (mLock) { return mCurrentDream != null && !mCurrentDream.isPreview @@ -571,6 +587,7 @@ public final class DreamManagerService extends SystemService { } mSystemDreamComponent = componentName; + reportKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked()); // Switch dream if currently dreaming and not dozing. if (isDreamingInternal() && !isDozingInternal()) { @@ -580,6 +597,10 @@ public final class DreamManagerService extends SystemService { } } + private boolean shouldKeepDreamingWhenUndocked() { + return mKeepDreamingWhenUndockedDefault && mSystemDreamComponent == null; + } + private ComponentName getDefaultDreamComponentForUser(int userId) { String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, @@ -1012,6 +1033,18 @@ public final class DreamManagerService extends SystemService { public void requestDream() { requestDreamInternal(); } + + @Override + public void registerDreamManagerStateListener(DreamManagerStateListener listener) { + mDreamManagerStateListeners.add(listener); + // Initialize the listener's state. + listener.onKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked()); + } + + @Override + public void unregisterDreamManagerStateListener(DreamManagerStateListener listener) { + mDreamManagerStateListeners.remove(listener); + } } private static final class DreamRecord { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index cc84c85c7518a..20235eb2c4648 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -472,9 +472,6 @@ public final class PowerManagerService extends SystemService // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; - // True if the device should keep dreaming when undocked. - private boolean mKeepDreamingWhenUndockingConfig; - // True if the device should wake up when plugged or unplugged in theater mode. private boolean mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig; @@ -681,6 +678,19 @@ public final class PowerManagerService extends SystemService // but the DreamService has not yet been told to start (it's an async process). private boolean mDozeStartInProgress; + // Whether to keep dreaming when the device is undocked. + private boolean mKeepDreamingWhenUndocked; + + private final class DreamManagerStateListener implements + DreamManagerInternal.DreamManagerStateListener { + @Override + public void onKeepDreamingWhenUndockedChanged(boolean keepDreaming) { + synchronized (mLock) { + mKeepDreamingWhenUndocked = keepDreaming; + } + } + } + private final class PowerGroupWakefulnessChangeListener implements PowerGroup.PowerGroupListener { @GuardedBy("mLock") @@ -1285,6 +1295,9 @@ public final class PowerManagerService extends SystemService new DisplayGroupPowerChangeListener(); mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener); + // This DreamManager method does not acquire a lock, so it should be safe to call. + mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener()); + mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager, mInjector.createSuspendBlocker( this, "PowerManagerService.WirelessChargerDetector"), @@ -1402,8 +1415,6 @@ public final class PowerManagerService extends SystemService com.android.internal.R.bool.config_powerDecoupleInteractiveModeFromDisplay); mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean( com.android.internal.R.bool.config_unplugTurnsOnScreen); - mKeepDreamingWhenUndockingConfig = resources.getBoolean( - com.android.internal.R.bool.config_keepDreamingWhenUndocking); mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig = resources.getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromUnplug); mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean( @@ -2518,7 +2529,7 @@ public final class PowerManagerService extends SystemService } // Don't wake when undocking while dreaming if configured not to. - if (mKeepDreamingWhenUndockingConfig + if (mKeepDreamingWhenUndocked && getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING && wasPowered && !mIsPowered && oldPlugType == BatteryManager.BATTERY_PLUGGED_DOCK) { @@ -4472,8 +4483,7 @@ public final class PowerManagerService extends SystemService + mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig); pw.println(" mTheaterModeEnabled=" + mTheaterModeEnabled); - pw.println(" mKeepDreamingWhenUndockingConfig=" - + mKeepDreamingWhenUndockingConfig); + pw.println(" mKeepDreamingWhenUndocked=" + mKeepDreamingWhenUndocked); pw.println(" mSuspendWhenScreenOffDueToProximityConfig=" + mSuspendWhenScreenOffDueToProximityConfig); pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index db2630e2683ce..d0d2b412b1b65 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -658,17 +658,52 @@ public class PowerManagerServiceTest { } /** - * Tests that dreaming continues when undocking and configured to do so. + * Tests that dreaming stops when undocking and not configured to keep dreaming. */ @Test - public void testWakefulnessDream_shouldKeepDreamingWhenUndocked() { + public void testWakefulnessDream_shouldStopDreamingWhenUndocked_whenNotConfigured() { + // Make sure "unplug turns on screen" is configured to true. + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen)) + .thenReturn(true); + createService(); startSystem(); - when(mResourcesSpy.getBoolean( - com.android.internal.R.bool.config_keepDreamingWhenUndocking)) + ArgumentCaptor dreamManagerStateListener = + ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class); + verify(mDreamManagerInternalMock).registerDreamManagerStateListener( + dreamManagerStateListener.capture()); + dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false); + + when(mBatteryManagerInternalMock.getPlugType()) + .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK); + setPluggedIn(true); + + forceAwake(); // Needs to be awake first before it can dream. + forceDream(); + when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0); + setPluggedIn(false); + + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + /** + * Tests that dreaming continues when undocking and configured to do so. + */ + @Test + public void testWakefulnessDream_shouldKeepDreamingWhenUndocked_whenConfigured() { + // Make sure "unplug turns on screen" is configured to true. + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen)) .thenReturn(true); - mService.readConfigurationLocked(); + + createService(); + startSystem(); + + ArgumentCaptor dreamManagerStateListener = + ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class); + verify(mDreamManagerInternalMock).registerDreamManagerStateListener( + dreamManagerStateListener.capture()); + dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true); when(mBatteryManagerInternalMock.getPlugType()) .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK); @@ -682,6 +717,37 @@ public class PowerManagerServiceTest { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING); } + /** + * Tests that dreaming stops when undocking while showing a dream that prevents it. + */ + @Test + public void testWakefulnessDream_shouldStopDreamingWhenUndocked_whenDreamPrevents() { + // Make sure "unplug turns on screen" is configured to true. + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen)) + .thenReturn(true); + + createService(); + startSystem(); + + ArgumentCaptor dreamManagerStateListener = + ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class); + verify(mDreamManagerInternalMock).registerDreamManagerStateListener( + dreamManagerStateListener.capture()); + dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true); + + when(mBatteryManagerInternalMock.getPlugType()) + .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK); + setPluggedIn(true); + + forceAwake(); // Needs to be awake first before it can dream. + forceDream(); + dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false); + when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0); + setPluggedIn(false); + + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + @Test public void testWakefulnessDoze_goToSleep() { createService();