Add APIs in PowerManager for suppressing ambient display.
Added 4 methods:
boolean isAmbientDisplayAvailable()
void suppressAmbientDisplay(String token, boolean suppress)
boolean isAmbientDisplaySuppressedForToken(String token)
boolean isAmbientDisplaySuppressed()
This CL simply adds the API to toggle SUPPRESS_DOZE. The code for
actually turning off doze when the secure setting is updated will be
implemented in a follow-up CL.
Test: manual, atest FrameworksServicesTests:PowerManagerServiceTest
Bug: 147584235, 147587449
Change-Id: I54f46f75fb84aae2ae806690e73eeb427ad8e8e1
diff --git a/api/system-current.txt b/api/system-current.txt
index 66b779d..03786a1 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7811,10 +7811,14 @@
method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger();
+ method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
+ method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
+ method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 185693e..3ae5700 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -80,6 +80,14 @@
// controls whether PowerManager should doze after the screen turns off or not
void setDozeAfterScreenOff(boolean on);
+ // returns whether ambient display is available on the device.
+ boolean isAmbientDisplayAvailable();
+ // suppresses the current ambient display configuration and disables ambient display.
+ void suppressAmbientDisplay(String token, boolean suppress);
+ // returns whether ambient display is suppressed by the calling app with the given token.
+ boolean isAmbientDisplaySuppressedForToken(String token);
+ // returns whether ambient display is suppressed by any app with any token.
+ boolean isAmbientDisplaySuppressed();
// Forces the system to suspend even if there are held wakelocks.
boolean forceSuspend();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 0414b14..bf13c35 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1870,6 +1870,77 @@
}
/**
+ * Returns true if ambient display is available on the device.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+ public boolean isAmbientDisplayAvailable() {
+ try {
+ return mService.isAmbientDisplayAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * If true, suppresses the current ambient display configuration and disables ambient display.
+ *
+ * <p>This method has no effect if {@link #isAmbientDisplayAvailable()} is false.
+ *
+ * @param token A persistable identifier for the ambient display suppression that is unique
+ * within the calling application.
+ * @param suppress If set to {@code true}, ambient display will be suppressed. If set to
+ * {@code false}, ambient display will no longer be suppressed for the given
+ * token.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void suppressAmbientDisplay(@NonNull String token, boolean suppress) {
+ try {
+ mService.suppressAmbientDisplay(token, suppress);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns true if ambient display is suppressed by the calling app with the given
+ * {@code token}.
+ *
+ * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+ *
+ * @param token The identifier of the ambient display suppression.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+ public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) {
+ try {
+ return mService.isAmbientDisplaySuppressedForToken(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns true if ambient display is suppressed by <em>any</em> app with <em>any</em> token.
+ *
+ * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE)
+ public boolean isAmbientDisplaySuppressed() {
+ try {
+ return mService.isAmbientDisplaySuppressed();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the reason the phone was last shutdown. Calling app must have the
* {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
* @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index c1b71aa..41fc9c6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -72,6 +72,7 @@
import android.service.dreams.DreamManagerInternal;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
+import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -109,6 +110,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* The power manager service is responsible for coordinating power management
@@ -565,6 +567,9 @@
// but the DreamService has not yet been told to start (it's an async process).
private boolean mDozeStartInProgress;
+ // Set of all tokens suppressing ambient display.
+ private final Set<String> mAmbientDisplaySuppressionTokens = new ArraySet<>();
+
private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
@Override
public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException {
@@ -3357,6 +3362,26 @@
}
}
+ private void suppressAmbientDisplayInternal(String token, boolean suppress) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "Suppress ambient display for token " + token + ": " + suppress);
+ }
+
+ if (suppress) {
+ mAmbientDisplaySuppressionTokens.add(token);
+ } else {
+ mAmbientDisplaySuppressionTokens.remove(token);
+ }
+
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE,
+ Math.min(mAmbientDisplaySuppressionTokens.size(), 1));
+ }
+
+ private String createAmbientDisplayToken(String token, int callingUid) {
+ return callingUid + "_" + token;
+ }
+
private void boostScreenBrightnessInternal(long eventTime, int uid) {
synchronized (mLock) {
if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
@@ -5007,6 +5032,61 @@
}
@Override // Binder call
+ public boolean isAmbientDisplayAvailable() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_STATE, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mAmbientDisplayConfiguration.ambientDisplayAvailable();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void suppressAmbientDisplay(@NonNull String token, boolean suppress) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_DREAM_STATE, null);
+
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ suppressAmbientDisplayInternal(createAmbientDisplayToken(token, uid), suppress);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_STATE, null);
+
+ final int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mAmbientDisplaySuppressionTokens.contains(
+ createAmbientDisplayToken(token, uid));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public boolean isAmbientDisplaySuppressed() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_STATE, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mAmbientDisplaySuppressionTokens.size() > 0;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void boostScreenBrightness(long eventTime) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 710e8df..d2ddff3 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -74,6 +74,8 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
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 25cef56..6eef41a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -806,4 +806,173 @@
assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
DisplayPowerRequest.POLICY_BRIGHT);
}
+
+ @Test
+ public void testIsAmbientDisplayAvailable_available() throws Exception {
+ createService();
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplayAvailable()).isTrue();
+ }
+
+ @Test
+ public void testIsAmbientDisplayAvailable_unavailable() throws Exception {
+ createService();
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplayAvailable()).isFalse();
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_multipleCallers_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_suppressTwice_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_suppressTwiceThenUnsuppress_notSuppressed()
+ throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_notSuppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_unsuppressTwice_notSuppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+
+ assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0);
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressed_default_notSuppressed() throws Exception {
+ createService();
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressed_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isTrue();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressed_notSuppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isTrue();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() throws Exception {
+ createService();
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test"))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForToken_suppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", true);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test"))
+ .isTrue();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForToken_notSuppressed() throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test", false);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test"))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed()
+ throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test1"))
+ .isTrue();
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test2"))
+ .isTrue();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed()
+ throws Exception {
+ createService();
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true);
+ mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false);
+
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test1"))
+ .isTrue();
+ assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test2"))
+ .isFalse();
+ }
}