From edfaf5ac63d516c81e75f9818de7d30d258b9f93 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Tue, 14 Dec 2021 20:03:13 -0800 Subject: [PATCH] Allow camera to specify a refresh rate range Instead of using a hard coded 60Hz value, allow the camera service to specify a range of refresh rate for the display to use. In this CL we still use hardcoded 60Hz, but we move it to the CameraServiceProxy. In a follow up CL the camera would provide the real range to use. Bug: 209669709 Test: atest RefreshRatePolicy Change-Id: I222dedbdef9f9d0e1f64d01074d2e9a04867b178 --- .../server/camera/CameraServiceProxy.java | 6 +- .../android/server/wm/RefreshRatePolicy.java | 52 +++++++++++++--- .../server/wm/WindowManagerInternal.java | 9 +-- .../server/wm/WindowManagerService.java | 10 +-- .../server/wm/RefreshRatePolicyTest.java | 62 +++++++++++++++++-- 5 files changed, 115 insertions(+), 24 deletions(-) diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index e0b768d7c5926..ffba59895bdc8 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -1063,7 +1063,9 @@ public class CameraServiceProxy extends SystemService if (!alreadyActivePackage) { WindowManagerInternal wmi = LocalServices.getService(WindowManagerInternal.class); - wmi.addNonHighRefreshRatePackage(clientName); + // TODO(b/209669709): populate min.max refreshRate based on + // the camera capture speed + wmi.addRefreshRateRangeForPackage(clientName, 60.0f, 60.0f); } // Update activity events @@ -1102,7 +1104,7 @@ public class CameraServiceProxy extends SystemService if (!stillActivePackage) { WindowManagerInternal wmi = LocalServices.getService(WindowManagerInternal.class); - wmi.removeNonHighRefreshRatePackage(clientName); + wmi.removeRefreshRateRangeForPackage(clientName); } } diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 7bddb620c94d5..f3713eb7f474f 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -19,20 +19,46 @@ package com.android.server.wm; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; -import android.util.ArraySet; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.view.Display; import android.view.Display.Mode; import android.view.DisplayInfo; +import java.util.HashMap; + /** * Policy to select a lower refresh rate for the display if applicable. */ class RefreshRatePolicy { + class PackageRefreshRate { + private final HashMap mPackages = new HashMap<>(); + + public void add(String s, float minRefreshRate, float maxRefreshRate) { + float minSupportedRefreshRate = + Math.max(RefreshRatePolicy.this.mMinSupportedRefreshRate, minRefreshRate); + float maxSupportedRefreshRate = + Math.min(RefreshRatePolicy.this.mMaxSupportedRefreshRate, maxRefreshRate); + + mPackages.put(s, + new RefreshRateRange(minSupportedRefreshRate, maxSupportedRefreshRate)); + } + + public RefreshRateRange get(String s) { + return mPackages.get(s); + } + + public void remove(String s) { + mPackages.remove(s); + } + } + private final Mode mLowRefreshRateMode; - private final ArraySet mNonHighRefreshRatePackages = new ArraySet<>(); + private final PackageRefreshRate mNonHighRefreshRatePackages = new PackageRefreshRate(); private final HighRefreshRateDenylist mHighRefreshRateDenylist; private final WindowManagerService mWmService; + private float mMinSupportedRefreshRate; + private float mMaxSupportedRefreshRate; /** * The following constants represent priority of the window. SF uses this information when @@ -70,7 +96,12 @@ class RefreshRatePolicy { Mode mode = displayInfo.getDefaultMode(); float[] refreshRates = displayInfo.getDefaultRefreshRates(); float bestRefreshRate = mode.getRefreshRate(); + mMinSupportedRefreshRate = bestRefreshRate; + mMaxSupportedRefreshRate = bestRefreshRate; for (int i = refreshRates.length - 1; i >= 0; i--) { + mMinSupportedRefreshRate = Math.min(mMinSupportedRefreshRate, refreshRates[i]); + mMaxSupportedRefreshRate = Math.max(mMaxSupportedRefreshRate, refreshRates[i]); + if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) { bestRefreshRate = refreshRates[i]; } @@ -78,12 +109,13 @@ class RefreshRatePolicy { return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate); } - void addNonHighRefreshRatePackage(String packageName) { - mNonHighRefreshRatePackages.add(packageName); + void addRefreshRateRangeForPackage(String packageName, + float minRefreshRate, float maxRefreshRate) { + mNonHighRefreshRatePackages.add(packageName, minRefreshRate, maxRefreshRate); mWmService.requestTraversal(); } - void removeNonHighRefreshRatePackage(String packageName) { + void removeRefreshRateRangeForPackage(String packageName) { mNonHighRefreshRatePackages.remove(packageName); mWmService.requestTraversal(); } @@ -172,8 +204,9 @@ class RefreshRatePolicy { // If app is using Camera, we set both the min and max refresh rate to the camera's // preferred refresh rate to make sure we don't end up with a refresh rate lower // than the camera capture rate, which will lead to dropping camera frames. - if (mNonHighRefreshRatePackages.contains(packageName)) { - return mLowRefreshRateMode.getRefreshRate(); + RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); + if (range != null) { + return range.min; } return 0; @@ -192,8 +225,9 @@ class RefreshRatePolicy { final String packageName = w.getOwningPackage(); // If app is using Camera, force it to default (lower) refresh rate. - if (mNonHighRefreshRatePackages.contains(packageName)) { - return mLowRefreshRateMode.getRefreshRate(); + RefreshRateRange range = mNonHighRefreshRatePackages.get(packageName); + if (range != null) { + return range.max; } return 0; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 9585a4b93a972..0a3c3f049f43e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -725,17 +725,18 @@ public abstract class WindowManagerInternal { public abstract void hideIme(IBinder imeTargetWindowToken, int displayId); /** - * Tell window manager about a package that should not be running with high refresh rate - * setting until removeNonHighRefreshRatePackage is called for the same package. + * Tell window manager about a package that should be running with a restricted range of + * refresh rate setting until removeRefreshRateRangeForPackage is called for the same package. * * This must not be called again for the same package. */ - public abstract void addNonHighRefreshRatePackage(@NonNull String packageName); + public abstract void addRefreshRateRangeForPackage(@NonNull String packageName, + float minRefreshRate, float maxRefreshRate); /** * Tell window manager to stop constraining refresh rate for the given package. */ - public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName); + public abstract void removeRefreshRateRangeForPackage(@NonNull String packageName); /** * Checks if the device supports touch or faketouch. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6445d1e657dae..95d2fd19b9a29 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7941,18 +7941,20 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void addNonHighRefreshRatePackage(@NonNull String packageName) { + public void addRefreshRateRangeForPackage(@NonNull String packageName, + float minRefreshRate, float maxRefreshRate) { synchronized (mGlobalLock) { mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() - .addNonHighRefreshRatePackage(packageName)); + .addRefreshRateRangeForPackage( + packageName, minRefreshRate, maxRefreshRate)); } } @Override - public void removeNonHighRefreshRatePackage(@NonNull String packageName) { + public void removeRefreshRateRangeForPackage(@NonNull String packageName) { synchronized (mGlobalLock) { mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() - .removeNonHighRefreshRatePackage(packageName)); + .removeRefreshRateRangeForPackage(packageName)); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index 19247604ad100..9d2eb26f5f21f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -101,14 +101,63 @@ public class RefreshRatePolicyTest extends WindowTestsBase { assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); - mPolicy.addNonHighRefreshRatePackage("com.android.test"); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE, LOW_REFRESH_RATE); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(LOW_REFRESH_RATE, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(LOW_REFRESH_RATE, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); - mPolicy.removeNonHighRefreshRatePackage("com.android.test"); + mPolicy.removeRefreshRateRangeForPackage("com.android.test"); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + } + + @Test + public void testCameraRange() { + final WindowState cameraUsingWindow = createWindow("cameraUsingWindow"); + cameraUsingWindow.mAttrs.packageName = "com.android.test"; + parcelLayoutParams(cameraUsingWindow); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE, MID_REFRESH_RATE); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(LOW_REFRESH_RATE, + mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(MID_REFRESH_RATE, + mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + mPolicy.removeRefreshRateRangeForPackage("com.android.test"); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + } + + @Test + public void testCameraRange_OutOfRange() { + final WindowState cameraUsingWindow = createWindow("cameraUsingWindow"); + cameraUsingWindow.mAttrs.packageName = "com.android.test"; + parcelLayoutParams(cameraUsingWindow); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE - 10, HI_REFRESH_RATE + 10); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(LOW_REFRESH_RATE, + mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(HI_REFRESH_RATE, + mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + mPolicy.removeRefreshRateRangeForPackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); @@ -162,7 +211,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase { overrideWindow.mAttrs.packageName = "com.android.test"; overrideWindow.mAttrs.preferredDisplayModeId = HI_MODE_ID; parcelLayoutParams(overrideWindow); - mPolicy.addNonHighRefreshRatePackage("com.android.test"); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE, LOW_REFRESH_RATE); assertEquals(HI_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(HI_REFRESH_RATE, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); @@ -178,7 +228,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase { overrideWindow.mAttrs.packageName = "com.android.test"; overrideWindow.mAttrs.preferredRefreshRate = HI_REFRESH_RATE; parcelLayoutParams(overrideWindow); - mPolicy.addNonHighRefreshRatePackage("com.android.test"); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE, LOW_REFRESH_RATE); assertEquals(0, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(HI_REFRESH_RATE, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); @@ -257,7 +308,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase { cameraUsingWindow.mAttrs.packageName = "com.android.test"; parcelLayoutParams(cameraUsingWindow); - mPolicy.addNonHighRefreshRatePackage("com.android.test"); + mPolicy.addRefreshRateRangeForPackage("com.android.test", + LOW_REFRESH_RATE, LOW_REFRESH_RATE); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(LOW_REFRESH_RATE,