diff options
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()); } |