summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Rupesh Bansal <brup@google.com> 2025-02-20 04:19:52 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-20 04:19:52 -0800
commitdd0c640576d1eaebd852475e14624f7292188a9e (patch)
tree47da304a0e63e8850522c4a5bdd22af5fa853724
parent242ce0cd11ceb5b600f4ac3e0c62a5b8e599751c (diff)
parentc300e970f0ce366ce811c4c91c00f3e462bc6c5c (diff)
Merge "Added support for not registering for RR events by default" into main
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/hardware/display/DisplayManager.java74
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java103
-rw-r--r--core/java/android/view/Display.java7
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java116
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java14
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
8 files changed, 303 insertions, 31 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 89b377314887..4222c7c64672 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1737,6 +1737,7 @@ package android.hardware.display {
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
+ method @FlaggedApi("com.android.server.display.feature.flags.delay_implicit_rr_registration_until_rr_accessed") public void resetImplicitRefreshRateCallbackStatus();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 7850e377ec4d..92a56fc575e3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -851,6 +851,12 @@ public final class DisplayManager {
* Registers a display listener to receive notifications about when
* displays are added, removed or changed.
*
+ * Because of the high frequency at which the refresh rate can change, clients will be
+ * registered for refresh rate change callbacks only when they request for refresh rate
+ * data({@link Display#getRefreshRate()}. Or alternately, you can consider using
+ * {@link #registerDisplayListener(Executor, long, DisplayListener)} and explicitly subscribe to
+ * {@link #EVENT_TYPE_DISPLAY_REFRESH_RATE} event
+ *
* We encourage to use {@link #registerDisplayListener(Executor, long, DisplayListener)}
* instead to subscribe for explicit events of interest
*
@@ -863,8 +869,8 @@ public final class DisplayManager {
public void registerDisplayListener(DisplayListener listener, Handler handler) {
registerDisplayListener(listener, handler, EVENT_TYPE_DISPLAY_ADDED
| EVENT_TYPE_DISPLAY_CHANGED
- | EVENT_TYPE_DISPLAY_REFRESH_RATE
- | EVENT_TYPE_DISPLAY_REMOVED);
+ | EVENT_TYPE_DISPLAY_REMOVED, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ false);
}
/**
@@ -882,9 +888,8 @@ public final class DisplayManager {
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter) {
- mGlobal.registerDisplayListener(listener, handler,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, handler, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -901,9 +906,8 @@ public final class DisplayManager {
@FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public void registerDisplayListener(@NonNull Executor executor, @EventType long eventFilter,
@NonNull DisplayListener listener) {
- mGlobal.registerDisplayListener(listener, executor,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, executor, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -924,9 +928,39 @@ public final class DisplayManager {
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter,
@PrivateEventType long privateEventFilter) {
+ registerDisplayListener(listener, handler, eventFilter, privateEventFilter,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @param eventFilter A bitmask of the event types for which this listener is subscribed.
+ * @param privateEventFilter A bitmask of the private event types for which this listener
+ * is subscribed.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
+ *
+ */
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
mGlobal.registerDisplayListener(listener, handler,
mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
- ActivityThread.currentPackageName());
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
+ }
+
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ Executor executor, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
+ mGlobal.registerDisplayListener(listener, executor,
+ mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
}
/**
@@ -1146,6 +1180,28 @@ public final class DisplayManager {
}
/**
+ * Resets the behavior that automatically registers clients for refresh rate change callbacks
+ * when they register via {@link #registerDisplayListener(DisplayListener, Handler)}
+ *
+ * <p>By default, clients are not registered for refresh rate change callbacks via
+ * {@link #registerDisplayListener(DisplayListener, Handler)}. However, calling
+ * {@link Display#getRefreshRate()} triggers automatic registration for existing and future
+ * {@link DisplayListener} instances. This method reverts this behavior, preventing new
+ * clients from being automatically registered for refresh rate change callbacks. Note that the
+ * existing ones will continue to stay registered
+ *
+ * <p>In essence, this method returns the system to its initial state, where explicit calls to
+ * {{@link Display#getRefreshRate()} are required to receive refresh rate change notifications.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ @TestApi
+ public void resetImplicitRefreshRateCallbackStatus() {
+ mGlobal.resetImplicitRefreshRateCallbackStatus();
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
* @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a7d610e54e2c..c4af87116eed 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -187,6 +187,9 @@ public final class DisplayManagerGlobal {
private final Binder mToken = new Binder();
+ // Guarded by mLock
+ private boolean mShouldImplicitlyRegisterRrChanges = false;
+
@VisibleForTesting
public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
@@ -390,27 +393,49 @@ public final class DisplayManagerGlobal {
* the handler for the main thread.
* If that is still null, a runtime exception will be thrown.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
- String packageName) {
+ String packageName, boolean isEventFilterExplicit) {
Looper looper = getLooperForHandler(handler);
Handler springBoard = new Handler(looper);
registerDisplayListener(listener, new HandlerExecutor(springBoard), internalEventFlagsMask,
- packageName);
+ packageName, isEventFilterExplicit);
}
/**
* Register a listener for display-related changes.
*
* @param listener The listener that will be called when display changes occur.
+ * @param handler Handler for the thread that will be receiving the callbacks. May be null.
+ * If null, listener will use the handler for the current thread, and if still null,
+ * the handler for the main thread.
+ * If that is still null, a runtime exception will be thrown.
+ * @param internalEventFlagsMask Mask of events to be listened to.
+ * @param packageName of the calling package.
+ */
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
+ String packageName) {
+ registerDisplayListener(listener, handler, internalEventFlagsMask, packageName, true);
+ }
+
+
+ /**
+ * Register a listener for display-related changes.
+ *
+ * @param listener The listener that will be called when display changes occur.
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
* @param internalEventFlagsMask Mask of events to be listened to.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the explicit events to be subscribed to
+ * were supplied or not
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@NonNull Executor executor, @InternalEventFlag long internalEventFlagsMask,
- String packageName) {
+ String packageName, boolean isEventFilterExplicit) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -429,7 +454,7 @@ public final class DisplayManagerGlobal {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
- internalEventFlagsMask, packageName));
+ internalEventFlagsMask, packageName, isEventFilterExplicit));
registerCallbackIfNeededLocked();
} else {
mDisplayListeners.get(index).setEventsMask(internalEventFlagsMask);
@@ -439,6 +464,22 @@ public final class DisplayManagerGlobal {
}
}
+
+ /**
+ * Registers all the clients to INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE events if qualified
+ */
+ public void registerForRefreshRateChanges() {
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mShouldImplicitlyRegisterRrChanges) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ updateCallbackIfNeededLocked();
+ }
+ }
+ }
+
public void unregisterDisplayListener(DisplayListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
@@ -521,8 +562,14 @@ public final class DisplayManagerGlobal {
long mask = 0;
final int numListeners = mDisplayListeners.size();
for (int i = 0; i < numListeners; i++) {
- mask |= mDisplayListeners.get(i).mInternalEventFlagsMask;
+ DisplayListenerDelegate displayListenerDelegate = mDisplayListeners.get(i);
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()
+ || mShouldImplicitlyRegisterRrChanges) {
+ displayListenerDelegate.implicitlyRegisterForRRChanges();
+ }
+ mask |= displayListenerDelegate.mInternalEventFlagsMask;
}
+
if (mDispatchNativeCallbacks) {
mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
@@ -802,6 +849,18 @@ public final class DisplayManagerGlobal {
}
/**
+ * Resets the implicit registration of refresh rate change callbacks
+ *
+ */
+ public void resetImplicitRefreshRateCallbackStatus() {
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ synchronized (mLock) {
+ mShouldImplicitlyRegisterRrChanges = false;
+ }
+ }
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
*/
@@ -1439,21 +1498,27 @@ public final class DisplayManagerGlobal {
}
}
- private static final class DisplayListenerDelegate {
+ @VisibleForTesting
+ static final class DisplayListenerDelegate {
public final DisplayListener mListener;
public volatile long mInternalEventFlagsMask;
+ // Indicates if the client explicitly supplied the display events to be subscribed to.
+ private final boolean mIsEventFilterExplicit;
+
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
private AtomicLong mGenerationId = new AtomicLong(1);
private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @InternalEventFlag long internalEventFlag, String packageName) {
+ @InternalEventFlag long internalEventFlag, String packageName,
+ boolean isEventFilterExplicit) {
mExecutor = executor;
mListener = listener;
mInternalEventFlagsMask = internalEventFlag;
mPackageName = packageName;
+ mIsEventFilterExplicit = isEventFilterExplicit;
}
void sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info,
@@ -1470,6 +1535,11 @@ public final class DisplayManagerGlobal {
});
}
+ @VisibleForTesting
+ boolean isEventFilterExplicit() {
+ return mIsEventFilterExplicit;
+ }
+
void clearEvents() {
mGenerationId.incrementAndGet();
}
@@ -1478,6 +1548,17 @@ public final class DisplayManagerGlobal {
mInternalEventFlagsMask = newInternalEventFlagsMask;
}
+ private void implicitlyRegisterForRRChanges() {
+ // For backward compatibility, if the user didn't supply the explicit events while
+ // subscribing, register them to refresh rate change events if they subscribed to
+ // display changed events
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED) != 0
+ && !mIsEventFilterExplicit) {
+ setEventsMask(mInternalEventFlagsMask
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE);
+ }
+ }
+
private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
@Nullable DisplayInfo info, boolean forceUpdate) {
if (extraLogging()) {
@@ -1677,6 +1758,9 @@ public final class DisplayManagerGlobal {
public void registerNativeChoreographerForRefreshRateCallbacks() {
synchronized (mLock) {
mDispatchNativeCallbacks = true;
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ }
registerCallbackIfNeededLocked();
updateCallbackIfNeededLocked();
DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
@@ -1806,4 +1890,9 @@ public final class DisplayManagerGlobal {
return baseEventMask;
}
+
+ @VisibleForTesting
+ CopyOnWriteArrayList<DisplayListenerDelegate> getDisplayListeners() {
+ return mDisplayListeners;
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 231aa6816908..3f45e29f2d43 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -108,6 +108,7 @@ public final class Display {
private final String mOwnerPackageName;
private final Resources mResources;
private DisplayAdjustments mDisplayAdjustments;
+ private boolean mRefreshRateChangesRegistered;
@UnsupportedAppUsage
private DisplayInfo mDisplayInfo; // never null
@@ -1217,6 +1218,10 @@ public final class Display {
*/
public float getRefreshRate() {
synchronized (mLock) {
+ if (!mRefreshRateChangesRegistered) {
+ DisplayManagerGlobal.getInstance().registerForRefreshRateChanges();
+ mRefreshRateChangesRegistered = true;
+ }
updateDisplayInfoLocked();
return mDisplayInfo.getRefreshRate();
}
@@ -1601,7 +1606,7 @@ public final class Display {
.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
| DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
- ActivityThread.currentPackageName());
+ ActivityThread.currentPackageName(), /* isEventFilterImplicit */ true);
}
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 9383807ec761..8ef105f79988 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -56,6 +56,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -116,7 +117,8 @@ public class DisplayManagerGlobalTest {
@Test
public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -151,7 +153,7 @@ public class DisplayManagerGlobalTest {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| INTERNAL_EVENT_FLAG_DISPLAY_STATE,
- null);
+ null, /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -172,11 +174,80 @@ public class DisplayManagerGlobalTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksEnabled()
+ throws RemoteException {
+ // Subscription without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // After registering to refresh rate changes, subscription without supplied events subscribe
+ // to RR events
+ mDisplayManagerGlobal.registerForRefreshRateChanges();
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+
+ // Assert all the existing listeners are also subscribed to RR events
+ CopyOnWriteArrayList<DisplayManagerGlobal.DisplayListenerDelegate> delegates =
+ mDisplayManagerGlobal.getDisplayListeners();
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ }
+
+ // Subscription to RR when events are supplied doesn't happen
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Assert one listeners are not subscribed to RR events
+ delegates = mDisplayManagerGlobal.getDisplayListeners();
+ int subscribedListenersCount = 0;
+ int nonSubscribedListenersCount = 0;
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+
+ if (delegate.isEventFilterExplicit()) {
+ assertEquals(ALL_DISPLAY_EVENTS, delegate.mInternalEventFlagsMask);
+ nonSubscribedListenersCount++;
+ } else {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ subscribedListenersCount++;
+ }
+ }
+
+ assertEquals(2, subscribedListenersCount);
+ assertEquals(1, nonSubscribedListenersCount);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksDisabled()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
// First we subscribe to all events in order to test that the subsequent calls to
// registerDisplayListener will update the event mask.
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -184,21 +255,24 @@ public class DisplayManagerGlobalTest {
int displayId = 1;
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DISPLAY_CHANGE_EVENTS, null);
+ & ~DISPLAY_CHANGE_EVENTS, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
@@ -218,11 +292,34 @@ public class DisplayManagerGlobalTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_registerNativeRefreshRateCallbacks_enablesRRImplicitRegistrations()
+ throws RemoteException {
+ // Registering the display listener without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Native subscription for refresh rates is done
+ mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
+
+ // Registering the display listener without supplied events subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
- null);
+ null, /* isEventFilterExplicit */ true);
InOrder inOrder = Mockito.inOrder(mDisplayManager);
inOrder.verify(mDisplayManager)
@@ -260,9 +357,10 @@ public class DisplayManagerGlobalTest {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
- null /* packageName */);
+ null /* packageName */, /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener2, mHandler,
- DISPLAY_CHANGE_EVENTS, null /* packageName */);
+ DISPLAY_CHANGE_EVENTS, null /* packageName */,
+ /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index c3057ded66eb..7cc178d5ff6c 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@ public class DisplayManagerFlags {
Flags::committedStateSeparateEvent
);
+ private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
+ Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
+ Flags::delayImplicitRrRegistrationUntilRrAccessed
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -586,7 +591,6 @@ public class DisplayManagerFlags {
return mFramerateOverrideTriggersRrCallbacks.isEnabled();
}
-
/**
* @return {@code true} if the flag for sending refresh rate events only for the apps in
* foreground is enabled
@@ -604,6 +608,13 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for only explicit subscription for RR changes is enabled
+ */
+ public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
+ return mDelayImplicitRrRegistrationUntilRrAccessed.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -660,6 +671,7 @@ public class DisplayManagerFlags {
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
pw.println(" " + mCommittedStateSeparateEvent);
+ pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index f5451307afb7..a0064a9f5d1d 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -474,9 +474,9 @@ flag {
description: "Feature flag to trigger the RR callbacks when framerate overridding happens."
bug: "390113266"
is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -508,3 +508,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "delay_implicit_rr_registration_until_rr_accessed"
+ namespace: "display_manager"
+ description: "Feature flag for clients to subscribe to RR changes by either explicitly subscribing for refresh rate changes or request for refresh rate data"
+ bug: "391828526"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 9406779c929d..3776b03695d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -264,7 +264,7 @@ public class SystemServicesTestRule implements TestRule {
final DisplayManagerGlobal dmg = DisplayManagerGlobal.getInstance();
spyOn(dmg);
doNothing().when(dmg).registerDisplayListener(
- any(), any(Executor.class), anyLong(), anyString());
+ any(), any(Executor.class), anyLong(), anyString(), anyBoolean());
doNothing().when(dmg).registerTopologyListener(any(Executor.class), any(), anyString());
}