diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a9799f243cb75..077b6181743a4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2479,6 +2479,8 @@
900000
false
+
+ false
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 97105d80bf794..6ba46a8fdb4db 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2235,6 +2235,7 @@
+
diff --git a/services/core/java/com/android/server/power/AmbientDisplaySuppressionController.java b/services/core/java/com/android/server/power/AmbientDisplaySuppressionController.java
index aad7b1457b3c2..7440fc75bf511 100644
--- a/services/core/java/com/android/server/power/AmbientDisplaySuppressionController.java
+++ b/services/core/java/com/android/server/power/AmbientDisplaySuppressionController.java
@@ -40,13 +40,24 @@ import java.util.Set;
public class AmbientDisplaySuppressionController {
private static final String TAG = "AmbientDisplaySuppressionController";
- private final Context mContext;
private final Set> mSuppressionTokens;
+ private final AmbientDisplaySuppressionChangedCallback mCallback;
private IStatusBarService mStatusBarService;
- AmbientDisplaySuppressionController(Context context) {
- mContext = requireNonNull(context);
+ /** Interface to get a list of available logical devices. */
+ interface AmbientDisplaySuppressionChangedCallback {
+ /**
+ * Called when the suppression state changes.
+ *
+ * @param isSuppressed Whether ambient is suppressed.
+ */
+ void onSuppressionChanged(boolean isSuppressed);
+ }
+
+ AmbientDisplaySuppressionController(
+ @NonNull AmbientDisplaySuppressionChangedCallback callback) {
mSuppressionTokens = Collections.synchronizedSet(new ArraySet<>());
+ mCallback = requireNonNull(callback);
}
/**
@@ -58,6 +69,7 @@ public class AmbientDisplaySuppressionController {
*/
public void suppress(@NonNull String token, int callingUid, boolean suppress) {
Pair suppressionToken = Pair.create(requireNonNull(token), callingUid);
+ final boolean wasSuppressed = isSuppressed();
if (suppress) {
mSuppressionTokens.add(suppressionToken);
@@ -65,9 +77,14 @@ public class AmbientDisplaySuppressionController {
mSuppressionTokens.remove(suppressionToken);
}
+ final boolean isSuppressed = isSuppressed();
+ if (isSuppressed != wasSuppressed) {
+ mCallback.onSuppressionChanged(isSuppressed);
+ }
+
try {
synchronized (mSuppressionTokens) {
- getStatusBar().suppressAmbientDisplay(isSuppressed());
+ getStatusBar().suppressAmbientDisplay(isSuppressed);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to suppress ambient display", e);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a63647cc15490..4ec92ec9190bf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -127,6 +127,7 @@ import com.android.server.am.BatteryStatsService;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.AmbientDisplaySuppressionController.AmbientDisplaySuppressionChangedCallback;
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
@@ -507,6 +508,9 @@ public final class PowerManagerService extends SystemService
// effectively and terminate the dream. Use -1 to disable this safety feature.
private int mDreamsBatteryLevelDrainCutoffConfig;
+ // Whether dreams should be disabled when ambient mode is suppressed.
+ private boolean mDreamsDisabledByAmbientModeSuppressionConfig;
+
// True if dreams are enabled by the user.
private boolean mDreamsEnabledSetting;
@@ -964,8 +968,8 @@ public final class PowerManagerService extends SystemService
}
AmbientDisplaySuppressionController createAmbientDisplaySuppressionController(
- Context context) {
- return new AmbientDisplaySuppressionController(context);
+ @NonNull AmbientDisplaySuppressionChangedCallback callback) {
+ return new AmbientDisplaySuppressionController(callback);
}
InattentiveSleepWarningController createInattentiveSleepWarningController() {
@@ -1044,7 +1048,8 @@ public final class PowerManagerService extends SystemService
mConstants = new Constants(mHandler);
mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
mAmbientDisplaySuppressionController =
- mInjector.createAmbientDisplaySuppressionController(context);
+ mInjector.createAmbientDisplaySuppressionController(
+ mAmbientSuppressionChangedCallback);
mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
mFaceDownDetector = new FaceDownDetector(this::onFlip);
mScreenUndimDetector = new ScreenUndimDetector();
@@ -1403,6 +1408,8 @@ public final class PowerManagerService extends SystemService
com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenNotPowered);
mDreamsBatteryLevelDrainCutoffConfig = resources.getInteger(
com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff);
+ mDreamsDisabledByAmbientModeSuppressionConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
mDozeAfterScreenOff = resources.getBoolean(
com.android.internal.R.bool.config_dozeAfterScreenOffByDefault);
mMinimumScreenOffTimeoutConfig = resources.getInteger(
@@ -3329,7 +3336,7 @@ public final class PowerManagerService extends SystemService
}
// Doze has ended or will be stopped. Update the power state.
- sleepPowerGroupLocked(powerGroup, now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
+ sleepPowerGroupLocked(powerGroup, now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
Process.SYSTEM_UID);
}
}
@@ -3340,12 +3347,32 @@ public final class PowerManagerService extends SystemService
}
}
+ @GuardedBy("mLock")
+ private void onDreamSuppressionChangedLocked(final boolean isSuppressed) {
+ if (!mDreamsDisabledByAmbientModeSuppressionConfig) {
+ return;
+ }
+ if (!isSuppressed && mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting
+ && shouldNapAtBedTimeLocked() && isItBedTimeYetLocked(
+ mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP))) {
+ napInternal(SystemClock.uptimeMillis(), Process.SYSTEM_UID, /* allowWake= */ true);
+ } else if (isSuppressed) {
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+
+
/**
* Returns true if the {@code groupId} is allowed to dream in its current state.
*/
@GuardedBy("mLock")
private boolean canDreamLocked(final PowerGroup powerGroup) {
+ final boolean dreamsSuppressed = mDreamsDisabledByAmbientModeSuppressionConfig
+ && mAmbientDisplaySuppressionController.isSuppressed();
+
if (!mBootCompleted
+ || dreamsSuppressed
|| getGlobalWakefulnessLocked() != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
@@ -5037,6 +5064,16 @@ public final class PowerManagerService extends SystemService
}
};
+ private final AmbientDisplaySuppressionChangedCallback mAmbientSuppressionChangedCallback =
+ new AmbientDisplaySuppressionChangedCallback() {
+ @Override
+ public void onSuppressionChanged(boolean isSuppressed) {
+ synchronized (mLock) {
+ onDreamSuppressionChangedLocked(isSuppressed);
+ }
+ }
+ };
+
/**
* Callback for asynchronous operations performed by the power manager.
*/
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 2d0cbfd272899..f5ed41a7fa8b5 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -393,6 +393,12 @@ public class PowerManagerServiceTest {
.thenReturn(minimumScreenOffTimeoutConfigMillis);
}
+ private void setDreamsDisabledByAmbientModeSuppressionConfig(boolean disable) {
+ when(mResourcesSpy.getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig))
+ .thenReturn(disable);
+ }
+
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -790,6 +796,91 @@ public class PowerManagerServiceTest {
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testAmbientSuppression_disablesDreamingAndWakesDevice() {
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, 1);
+
+ setDreamsDisabledByAmbientModeSuppressionConfig(true);
+ setMinimumScreenOffTimeoutConfig(10000);
+ createService();
+ startSystem();
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setPluggedIn(true);
+ // Allow asynchronous sandman calls to execute.
+ advanceTime(10000);
+
+ forceDream();
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ advanceTime(50);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testAmbientSuppressionDisabled_shouldNotWakeDevice() {
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, 1);
+
+ setDreamsDisabledByAmbientModeSuppressionConfig(false);
+ setMinimumScreenOffTimeoutConfig(10000);
+ createService();
+ startSystem();
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setPluggedIn(true);
+ // Allow asynchronous sandman calls to execute.
+ advanceTime(10000);
+
+ forceDream();
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ advanceTime(50);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ }
+
+ @Test
+ public void testAmbientSuppression_doesNotAffectDreamForcing() {
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, 1);
+
+ setDreamsDisabledByAmbientModeSuppressionConfig(true);
+ setMinimumScreenOffTimeoutConfig(10000);
+ createService();
+ startSystem();
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ setPluggedIn(true);
+ // Allow asynchronous sandman calls to execute.
+ advanceTime(10000);
+
+ // Verify that forcing dream still works even though ambient display is suppressed
+ forceDream();
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ }
+
@Test
public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() {
final String suspendBlockerName = "PowerManagerService.Display";