diff options
| author | 2020-09-07 15:49:35 +0200 | |
|---|---|---|
| committer | 2020-09-18 13:32:54 +0200 | |
| commit | 97ba024fb03838b814cfb921c35ad3f0ac0c9ff9 (patch) | |
| tree | 56ff28d35399c2be5e0584c7aa251a8b7c2cadf5 | |
| parent | 82bc48af03d5cb2a192682f6a0f219dc1066b9ad (diff) | |
Add TestApi to always select the app requested display mode
The added test API is used to ignore all votes in
DisplayModeDirector app votes. This way only the app
requested display mode will be picked. The API will be
used for testing app mode switch requests.
Bug: 159113268
Bug: 158316271
Test: m
Change-Id: I257636ae2a6ed74044e71e49a99024682c3a92c5
9 files changed, 192 insertions, 11 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index de2919b8936a..9e5fb33163f9 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 392f56212058..8f99edf61ece 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -841,6 +841,30 @@ public final class DisplayManager { } /** + * 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. */ public interface DisplayListener { diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 0f9c7088a1d0..b046d1df5b8c 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 c697106d0c17..85da6424377a 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 560e3c11d2f4..85dc92d7f252 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3910,6 +3910,14 @@ <permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" android:protectionLevel="signature" /> + <!-- Allows an application to override the display mode requests + so the app requested mode will be selected and user settings and display + policies will be ignored. + @hide + @TestApi --> + <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to control VPN. <p>Not for use by third-party applications.</p> @hide --> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 190015cebe30..f5f58efb72e6 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -323,6 +323,9 @@ <!-- Permissions required for CTS test - AdbManagerTest --> <uses-permission android:name="android.permission.MANAGE_DEBUGGING" /> + <!-- Permission needed for CTS test - DisplayTest --> + <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 4a12ee71adbe..97c4cf531a53 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1179,6 +1179,15 @@ public final class DisplayManagerService extends SystemService { return mWideColorSpace.getId(); } + void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) { + mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled); + } + + + boolean shouldAlwaysRespectAppRequestedModeInternal() { + return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode(); + } + private void setBrightnessConfigurationForUserInternal( @Nullable BrightnessConfiguration c, @UserIdInt int userId, @Nullable String packageName) { @@ -2463,6 +2472,32 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, + "Permission required to override display mode requests."); + final long token = Binder.clearCallingIdentity(); + try { + setShouldAlwaysRespectAppRequestedModeInternal(enabled); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public boolean shouldAlwaysRespectAppRequestedMode() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, + "Permission required to override display mode requests."); + final long token = Binder.clearCallingIdentity(); + try { + return shouldAlwaysRespectAppRequestedModeInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean validatePackageName(int uid, String packageName) { if (packageName != null) { String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index cc6687f8566d..5034c03ea6a8 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -56,8 +56,8 @@ import java.util.List; import java.util.Objects; /** - * The DisplayModeDirector is responsible for determining what modes are allowed to be - * automatically picked by the system based on system-wide and display-specific configuration. + * The DisplayModeDirector is responsible for determining what modes are allowed to be automatically + * picked by the system based on system-wide and display-specific configuration. */ public class DisplayModeDirector { private static final String TAG = "DisplayModeDirector"; @@ -97,17 +97,20 @@ public class DisplayModeDirector { private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; + private boolean mAlwaysRespectAppRequest; + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mVotesByDisplay = new SparseArray<>(); 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<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) { + SparseArray<Vote> 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) { @@ -349,6 +366,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. * * @param pw The stream to dump information to. @@ -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 43a396d8e5d7..b8dbd6267bc5 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<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> 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); + } } |