From ee31f85781861dfecafd10142eeec1ae6e3f7bdc Mon Sep 17 00:00:00 2001 From: Sean Stout Date: Mon, 25 Jan 2021 20:27:12 -0800 Subject: [PATCH] Send events when DisplayGroups are added/modified/removed DisplayGroupListeners can now be added and will be informed of addition, removal, and modification of DisplayGroups. Bug: 138328918 Test: make Change-Id: I1dcb52931a798288a0255acd2862f9188f5d83ff --- .../display/DisplayManagerInternal.java | 32 ++++++-- .../android/server/display/DisplayGroup.java | 34 +++++++- .../server/display/DisplayManagerService.java | 80 +++++++++++++++++- .../server/display/LogicalDisplayMapper.java | 81 +++++++++++++++---- .../server/power/PowerManagerService.java | 4 +- .../server/power/PowerManagerServiceTest.java | 3 +- 6 files changed, 204 insertions(+), 30 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 95f1d124b9a1c..48b05b7d9e15f 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -47,20 +47,22 @@ public abstract class DisplayManagerInternal { * begins adjusting the power state to match what was requested. *

* + * @param groupId The identifier for the display group being requested to change power state * @param request The requested power state. - * @param waitForNegativeProximity If true, issues a request to wait for + * @param waitForNegativeProximity If {@code true}, issues a request to wait for * negative proximity before turning the screen back on, assuming the screen * was turned off by the proximity sensor. - * @return True if display is ready, false if there are important changes that must - * be made asynchronously (such as turning the screen on), in which case the caller - * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} - * then try the request again later until the state converges. + * @return {@code true} if display group is ready, {@code false} if there are important + * changes that must be made asynchronously (such as turning the screen on), in which case + * the caller should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged} + * then try the request again later until the state converges. If the provided {@code groupId} + * cannot be found then {@code true} will be returned. */ - public abstract boolean requestPowerState(DisplayPowerRequest request, + public abstract boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity); /** - * Returns true if the proximity sensor screen-off function is available. + * Returns {@code true} if the proximity sensor screen-off function is available. */ public abstract boolean isProximitySensorAvailable(); @@ -70,6 +72,22 @@ public abstract class DisplayManagerInternal { */ public abstract int getDisplayGroupId(int displayId); + /** + * Registers a display group listener which will be informed of the addition, removal, or change + * of display groups. + * + * @param listener The listener to register. + */ + public abstract void registerDisplayGroupListener(DisplayGroupListener listener); + + /** + * Unregisters a display group listener which will be informed of the addition, removal, or + * change of display groups. + * + * @param listener The listener to unregister. + */ + public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener); + /** * Screenshot for internal system-only use such as rotation, etc. This method includes * secure layers and the result should never be exposed to non-system applications. diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java index a11a745d0b7e0..663883ab2825b 100644 --- a/services/core/java/com/android/server/display/DisplayGroup.java +++ b/services/core/java/com/android/server/display/DisplayGroup.java @@ -21,7 +21,8 @@ import java.util.List; /** * Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and - * operations. + * operations; particularly display-state. + * * @hide */ public class DisplayGroup { @@ -33,17 +34,44 @@ public class DisplayGroup { mGroupId = groupId; } + /** Returns the identifier for the Group. */ int getGroupId() { return mGroupId; } - void addDisplay(LogicalDisplay display) { + /** + * Adds the provided {@code display} to the Group + * + * @param display the {@link LogicalDisplay} to add to the Group + */ + void addDisplayLocked(LogicalDisplay display) { if (!mDisplays.contains(display)) { mDisplays.add(display); } } - boolean removeDisplay(LogicalDisplay display) { + /** + * Removes the provided {@code display} from the Group. + * + * @param display The {@link LogicalDisplay} to remove from the Group. + * @return {@code true} if the {@code display} was removed; otherwise {@code false} + */ + boolean removeDisplayLocked(LogicalDisplay display) { return mDisplays.remove(display); } + + /** Returns {@code true} if there are no {@link LogicalDisplay LogicalDisplays} in the Group. */ + boolean isEmptyLocked() { + return mDisplays.isEmpty(); + } + + /** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */ + int getSizeLocked() { + return mDisplays.size(); + } + + /** Returns the ID of the {@link LogicalDisplay} at the provided {@code index}. */ + int getIdLocked(int index) { + return mDisplays.get(index).getDisplayIdLocked(); + } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index c3f8d8cf219ac..e0baee70b819f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -57,6 +57,7 @@ import android.hardware.display.Curve; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; @@ -190,6 +191,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_UPDATE_VIEWPORT = 5; private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6; private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; + private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8; private final Context mContext; private final DisplayManagerHandler mHandler; @@ -237,6 +239,10 @@ public final class DisplayManagerService extends SystemService { private final CopyOnWriteArrayList mDisplayTransactionListeners = new CopyOnWriteArrayList(); + /** List of all display group listeners. */ + private final CopyOnWriteArrayList mDisplayGroupListeners = + new CopyOnWriteArrayList<>(); + /** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */ private final SparseArray mDisplayPowerControllers = new SparseArray<>(); @@ -1677,6 +1683,11 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); } + private void sendDisplayGroupEvent(int groupId, int event) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event); + mHandler.sendMessage(msg); + } + private void sendDisplayEventFrameRateOverrideLocked(int displayId) { Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE, displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); @@ -1721,6 +1732,35 @@ public final class DisplayManagerService extends SystemService { mTempCallbacks.clear(); } + // Runs on Handler thread. + // Delivers display group event notifications to callbacks. + private void deliverDisplayGroupEvent(int groupId, int event) { + if (DEBUG) { + Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event=" + + event); + } + + switch (event) { + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupAdded(groupId); + } + break; + + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupChanged(groupId); + } + break; + + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupRemoved(groupId); + } + break; + } + } + private IMediaProjectionManager getProjectionService() { if (mProjectionService == null) { IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE); @@ -1943,6 +1983,11 @@ public final class DisplayManagerService extends SystemService { } deliverDisplayEvent(msg.arg1, uids, msg.arg2); break; + + case MSG_DELIVER_DISPLAY_GROUP_EVENT: + deliverDisplayGroupEvent(msg.arg1, msg.arg2); + break; + } } } @@ -1973,6 +2018,11 @@ public final class DisplayManagerService extends SystemService { } } + @Override + public void onDisplayGroupEventLocked(int groupId, int event) { + sendDisplayGroupEvent(groupId, event); + } + @Override public void onTraversalRequested() { synchronized (mSyncRoot) { @@ -2708,11 +2758,25 @@ public final class DisplayManagerService extends SystemService { } @Override - public boolean requestPowerState(DisplayPowerRequest request, + public boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { - return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY) - .requestPowerState(request, waitForNegativeProximity); + final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked( + groupId); + if (displayGroup == null) { + return true; + } + + final int size = displayGroup.getSizeLocked(); + boolean ready = true; + for (int i = 0; i < size; i++) { + final DisplayPowerController displayPowerController = + mDisplayPowerControllers.get(displayGroup.getIdLocked(i)); + ready &= displayPowerController.requestPowerState(request, + waitForNegativeProximity); + } + + return ready; } } @@ -2731,6 +2795,16 @@ public final class DisplayManagerService extends SystemService { } } + @Override + public void registerDisplayGroupListener(DisplayGroupListener listener) { + mDisplayGroupListeners.add(listener); + } + + @Override + public void unregisterDisplayGroupListener(DisplayGroupListener listener) { + mDisplayGroupListeners.remove(listener); + } + @Override public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) { return systemScreenshotInternal(displayId); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index e7388787ecf9c..8ee01bef8f546 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -55,6 +55,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5; + public static final int DISPLAY_GROUP_EVENT_ADDED = 1; + public static final int DISPLAY_GROUP_EVENT_CHANGED = 2; + public static final int DISPLAY_GROUP_EVENT_REMOVED = 3; + /** * Temporary display info, used for comparing display configurations. */ @@ -99,7 +103,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; /** A mapping from logical display id to display group. */ - private final SparseArray mDisplayGroups = new SparseArray<>(); + private final SparseArray mDisplayIdToGroupMap = new SparseArray<>(); private final DisplayDeviceRepository mDisplayDeviceRepo; private final Listener mListener; @@ -183,7 +187,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } public int getDisplayGroupIdLocked(int displayId) { - final DisplayGroup displayGroup = mDisplayGroups.get(displayId); + final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); if (displayGroup != null) { return displayGroup.getGroupId(); } @@ -191,6 +195,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return -1; } + public DisplayGroup getDisplayGroupLocked(int groupId) { + final int size = mDisplayIdToGroupMap.size(); + for (int i = 0; i < size; i++) { + final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i); + if (displayGroup.getGroupId() == groupId) { + return displayGroup; + } + } + + return null; + } + public void dumpLocked(PrintWriter pw) { pw.println("LogicalDisplayMapper:"); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -320,7 +336,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final int groupId = assignDisplayGroupIdLocked(isDefault); displayGroup = new DisplayGroup(groupId); } else { - displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY); + displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY); } LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); @@ -335,11 +351,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mLogicalDisplays.put(displayId, display); - displayGroup.addDisplay(display); - mDisplayGroups.append(displayId, displayGroup); + displayGroup.addDisplayLocked(display); + mDisplayIdToGroupMap.append(displayId, displayGroup); + + if (addNewDisplayGroup) { + // Group added events happen before Logical Display added events. + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED); + } mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED); + + if (!addNewDisplayGroup) { + // Group changed events happen after Logical Display added events. + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED); + } } /** @@ -356,33 +384,47 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides = display.getFrameRateOverrides(); display.updateLocked(mDisplayDeviceRepo); + final DisplayGroup changedDisplayGroup; if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); - mDisplayGroups.removeReturnOld(displayId).removeDisplay(display); + final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId); + displayGroup.removeDisplayLocked(display); mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED); + + changedDisplayGroup = displayGroup; } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { final int flags = display.getDisplayInfoLocked().flags; - final DisplayGroup defaultDisplayGroup = mDisplayGroups.get( + final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get( Display.DEFAULT_DISPLAY); if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) { // The display should have its own DisplayGroup. - if (defaultDisplayGroup.removeDisplay(display)) { + if (defaultDisplayGroup.removeDisplayLocked(display)) { final int groupId = assignDisplayGroupIdLocked(false); final DisplayGroup displayGroup = new DisplayGroup(groupId); - displayGroup.addDisplay(display); - mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup); + displayGroup.addDisplayLocked(display); display.updateDisplayGroupIdLocked(groupId); + mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup); + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED); + changedDisplayGroup = defaultDisplayGroup; + } else { + changedDisplayGroup = null; } } else { // The display should be a part of the default DisplayGroup. - final DisplayGroup displayGroup = mDisplayGroups.get(displayId); + final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); if (displayGroup != defaultDisplayGroup) { - displayGroup.removeDisplay(display); - defaultDisplayGroup.addDisplay(display); - mDisplayGroups.put(displayId, defaultDisplayGroup); + displayGroup.removeDisplayLocked(display); + defaultDisplayGroup.addDisplayLocked(display); display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId()); + mListener.onDisplayGroupEventLocked(defaultDisplayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED); + mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup); + changedDisplayGroup = displayGroup; + } else { + changedDisplayGroup = null; } } @@ -394,6 +436,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) { mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED); + changedDisplayGroup = null; } else { // While applications shouldn't know nor care about the non-overridden info, we // still need to let WindowManager know so it can update its own internal state for @@ -403,6 +446,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED); } + changedDisplayGroup = null; + } + + // CHANGED and REMOVED DisplayGroup events should always fire after Display events. + if (changedDisplayGroup != null) { + final int event = changedDisplayGroup.isEmptyLocked() + ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED + : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED; + mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event); } } } @@ -438,6 +490,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); + void onDisplayGroupEventLocked(int groupId, int event); void onTraversalRequested(); } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index db4b6d0a3005d..8e0d632dd1a8c 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2892,8 +2892,8 @@ public final class PowerManagerService extends SystemService PowerManager.BRIGHTNESS_INVALID_FLOAT; } - mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest, - mRequestWaitForNegativeProximity); + mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP, + displayPowerRequest, mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; if (DEBUG_SPEW) { diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 1d0b595d8fc19..592eea1cd55a1 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -179,7 +179,8 @@ public class PowerManagerServiceTest { .thenReturn(mPowerSaveState); when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false); - when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true); + when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean())) + .thenReturn(true); when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);