diff options
6 files changed, 334 insertions, 0 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 66b779d51669..03786a1c4e59 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7811,10 +7811,14 @@ package android.os { 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 185693e8e6e0..3ae570081f4d 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -80,6 +80,14 @@ interface IPowerManager // 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 0414b14ae02d..bf13c35867a4 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1870,6 +1870,77 @@ public final class PowerManager { } /** + * 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 c1b71aab38fd..41fc9c69f00d 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.provider.Settings.SettingNotFoundException; 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.ArrayList; 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 @@ public final class PowerManagerService extends SystemService // 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 @@ public final class PowerManagerService extends SystemService } } + 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 @@ public final class PowerManagerService extends SystemService } @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 710e8dfe6aa2..d2ddff3627b9 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 25cef56cd21e..6eef41aafc98 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 @@ public class PowerManagerServiceTest { 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(); + } } |