diff --git a/api/test-current.txt b/api/test-current.txt index de2919b8936a4..9e5fb33163f99 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -17,6 +17,7 @@ package android { field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; + field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; @@ -1304,6 +1305,8 @@ package android.hardware.display { method public android.graphics.Point getStableDisplaySize(); method public boolean isMinimalPostProcessingRequested(int); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); + method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean); + method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode(); field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 392f56212058b..8f99edf61ece3 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -840,6 +840,30 @@ public final class DisplayManager { return mGlobal.getMinimumBrightnessCurve(); } + /** + * When enabled the app requested mode is always selected regardless of user settings and + * policies for low brightness, low battery, etc. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) + public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { + mGlobal.setShouldAlwaysRespectAppRequestedMode(enabled); + } + + /** + * Returns whether we are running in a mode which always selects the app requested display mode + * and ignores user settings and policies for low brightness, low battery etc. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) + public boolean shouldAlwaysRespectAppRequestedMode() { + return mGlobal.shouldAlwaysRespectAppRequestedMode(); + } + /** * Listens for changes in available display devices. */ diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 0f9c7088a1d01..b046d1df5b8c1 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -699,6 +699,32 @@ public final class DisplayManagerGlobal { } } + /** + * When enabled the app requested display resolution and refresh rate is always selected + * in DisplayModeDirector regardless of user settings and policies for low brightness, low + * battery etc. + */ + public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { + try { + mDm.setShouldAlwaysRespectAppRequestedMode(enabled); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Returns whether DisplayModeDirector is running in a mode which always selects the app + * requested display mode and ignores user settings and policies for low brightness, low + * battery etc. + */ + public boolean shouldAlwaysRespectAppRequestedMode() { + try { + return mDm.shouldAlwaysRespectAppRequestedMode(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index c697106d0c171..85da6424377aa 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -128,4 +128,10 @@ interface IDisplayManager { // The wide gamut color space is returned from composition pipeline // based on hardware capability. int getPreferredWideGamutColorSpaceId(); + + // When enabled the app requested display resolution and refresh rate is always selected + // in DisplayModeDirector regardless of user settings and policies for low brightness, low + // battery etc. + void setShouldAlwaysRespectAppRequestedMode(boolean enabled); + boolean shouldAlwaysRespectAppRequestedMode(); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 560e3c11d2f45..85dc92d7f252f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3910,6 +3910,14 @@ + + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 190015cebe302..f5f58efb72e6c 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -323,6 +323,9 @@ + + + (); mSupportedModesByDisplay = new SparseArray<>(); - mDefaultModeByDisplay = new SparseArray<>(); + mDefaultModeByDisplay = new SparseArray<>(); mAppRequestObserver = new AppRequestObserver(); mSettingsObserver = new SettingsObserver(context, handler); mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); + mAlwaysRespectAppRequest = false; } /** @@ -127,7 +130,6 @@ public class DisplayModeDirector { // notify them to pick up our newly initialized state. notifyDesiredDisplayModeSpecsChangedLocked(); } - } @NonNull @@ -173,9 +175,14 @@ public class DisplayModeDirector { // VoteSummary is returned as an output param to cut down a bit on the number of temporary // objects. private void summarizeVotes( - SparseArray votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) { + SparseArray votes, + int lowestConsideredPriority, + int highestConsideredPriority, + /*out*/ VoteSummary summary) { summary.reset(); - for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) { + for (int priority = highestConsideredPriority; + priority >= lowestConsideredPriority; + priority--) { Vote vote = votes.get(priority); if (vote == null) { continue; @@ -217,8 +224,16 @@ public class DisplayModeDirector { int[] availableModes = new int[]{defaultMode.getModeId()}; VoteSummary primarySummary = new VoteSummary(); int lowestConsideredPriority = Vote.MIN_PRIORITY; - while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { - summarizeVotes(votes, lowestConsideredPriority, primarySummary); + int highestConsideredPriority = Vote.MAX_PRIORITY; + + if (mAlwaysRespectAppRequest) { + lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_REFRESH_RATE; + highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE; + } + + while (lowestConsideredPriority <= highestConsideredPriority) { + summarizeVotes( + votes, lowestConsideredPriority, highestConsideredPriority, primarySummary); // If we don't have anything specifying the width / height of the display, just use // the default width and height. We don't want these switching out from underneath @@ -261,7 +276,10 @@ public class DisplayModeDirector { VoteSummary appRequestSummary = new VoteSummary(); summarizeVotes( - votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary); + votes, + Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, + Vote.MAX_PRIORITY, + appRequestSummary); appRequestSummary.minRefreshRate = Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate); appRequestSummary.maxRefreshRate = @@ -338,8 +356,7 @@ public class DisplayModeDirector { } /** - * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate - * ranges. + * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges. */ public void setDesiredDisplayModeSpecsListener( @Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) { @@ -348,6 +365,26 @@ public class DisplayModeDirector { } } + /** + * When enabled the app requested display mode is always selected and all + * other votes will be ignored. This is used for testing purposes. + */ + public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { + synchronized (mLock) { + mAlwaysRespectAppRequest = enabled; + } + } + + /** + * Returns whether we are running in a mode which always selects the app requested display mode + * and ignores user settings and policies for low brightness, low battery etc. + */ + public boolean shouldAlwaysRespectAppRequestedMode() { + synchronized (mLock) { + return mAlwaysRespectAppRequest; + } + } + /** * Print the object's state and debug information into the given stream. * @@ -380,6 +417,7 @@ public class DisplayModeDirector { pw.println(" " + Vote.priorityToString(p) + " -> " + vote); } } + pw.println(" mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest); mSettingsObserver.dumpLocked(pw); mAppRequestObserver.dumpLocked(pw); mBrightnessObserver.dumpLocked(pw); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 43a396d8e5d7d..b8dbd6267bc51 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -302,4 +302,42 @@ public class DisplayModeDirectorTest { verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90); verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90); } + + @Test + public void testVotingWithAlwaysRespectAppRequest() { + final int displayId = 0; + DisplayModeDirector director = createDirectorFromFpsRange(50, 90); + SparseArray votes = new SparseArray<>(); + SparseArray> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(displayId, votes); + votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + + + director.injectVotesByDisplay(votesByDisplay); + Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + + director.setShouldAlwaysRespectAppRequestedMode(true); + Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue(); + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(90); + + director.setShouldAlwaysRespectAppRequestedMode(false); + Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); + + desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + } }