Connected displays wait for boot complete Sending display "connected" for external display too early during boot may result into ExternalDisplayStatsService won't be able to register listeners due to other services for example ActivityManager may not be available so early in the boot. Also, there is a problem with disabling display early in the boot which results into display not possible to re-enable upon user click on Mirror dialog. This CL postpones the disabling the connected display together with initializing ExternalDisplay StatsService until boot completes. Bug: 328463830 Bug: 318595765 Test: restart device while external display is connected Test: atest ExternalDisplayPolicyTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:66b273e12d79b59478d17205982b428aecc29edd) Merged-In: I472a82cfe8db01b644b14c05bab90f1de91818e8 Change-Id: I472a82cfe8db01b644b14c05bab90f1de91818e8
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java index ab7c503..a12d248 100644 --- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java +++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -42,6 +42,9 @@ import com.android.server.display.notifications.DisplayNotificationManager; import com.android.server.display.utils.DebugUtils; +import java.util.HashSet; +import java.util.Set; + /** * Listens for Skin thermal sensor events, disables external displays if thermal status becomes * equal or above {@link android.os.Temperature#THROTTLING_CRITICAL}, enables external displays if @@ -106,6 +109,10 @@ private final ExternalDisplayStatsService mExternalDisplayStatsService; @ThrottlingStatus private volatile int mStatus = THROTTLING_NONE; + //@GuardedBy("mSyncRoot") + private boolean mIsBootCompleted; + //@GuardedBy("mSyncRoot") + private final Set<Integer> mDisplayIdsWaitingForBootCompletion = new HashSet<>(); ExternalDisplayPolicy(@NonNull final Injector injector) { mInjector = injector; @@ -121,6 +128,17 @@ * Starts listening for temperature changes. */ void onBootCompleted() { + synchronized (mSyncRoot) { + mIsBootCompleted = true; + for (var displayId : mDisplayIdsWaitingForBootCompletion) { + var logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (logicalDisplay != null) { + handleExternalDisplayConnectedLocked(logicalDisplay); + } + } + mDisplayIdsWaitingForBootCompletion.clear(); + } + if (!mFlags.isConnectedDisplayManagementEnabled()) { if (DEBUG) { Slog.d(TAG, "External display management is not enabled on your device:" @@ -189,6 +207,11 @@ return; } + if (!mIsBootCompleted) { + mDisplayIdsWaitingForBootCompletion.add(logicalDisplay.getDisplayIdLocked()); + return; + } + mExternalDisplayStatsService.onDisplayConnected(logicalDisplay); if ((Build.IS_ENG || Build.IS_USERDEBUG) @@ -227,7 +250,12 @@ return; } - mExternalDisplayStatsService.onDisplayDisconnected(logicalDisplay.getDisplayIdLocked()); + var displayId = logicalDisplay.getDisplayIdLocked(); + if (mDisplayIdsWaitingForBootCompletion.remove(displayId)) { + return; + } + + mExternalDisplayStatsService.onDisplayDisconnected(displayId); } /**
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index b142334..18f0311 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2408,6 +2408,7 @@ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); DisplayManagerInternal localService = displayManager.new LocalService(); DisplayManagerService.BinderService bs = displayManager.new BinderService(); LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); @@ -2440,6 +2441,7 @@ .when(() -> SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)); manageDisplaysPermission(/* granted= */ true); DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); DisplayManagerInternal localService = displayManager.new LocalService(); DisplayManagerService.BinderService bs = displayManager.new BinderService(); LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); @@ -2487,6 +2489,7 @@ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); DisplayManagerService.BinderService bs = displayManager.new BinderService(); LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); @@ -2652,6 +2655,7 @@ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); DisplayManagerService.BinderService bs = displayManager.new BinderService(); DisplayManagerInternal localService = displayManager.new LocalService(); LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); @@ -2699,6 +2703,7 @@ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); DisplayManagerService.BinderService bs = displayManager.new BinderService(); LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java index 1529a08..1a71e77 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -228,13 +228,27 @@ @Test public void testOnExternalDisplayAvailable() { - when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false); + mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay); + assertNotAskedToEnableDisplay(); + verify(mMockedExternalDisplayStatsService, never()).onDisplayConnected(any()); + + mExternalDisplayPolicy.onBootCompleted(); assertAskedToEnableDisplay(); verify(mMockedExternalDisplayStatsService).onDisplayConnected(eq(mMockedLogicalDisplay)); } @Test + public void testOnExternalDisplayUnpluggedBeforeBootCompletes() { + mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay); + mExternalDisplayPolicy.handleLogicalDisplayDisconnectedLocked(mMockedLogicalDisplay); + mExternalDisplayPolicy.onBootCompleted(); + assertNotAskedToEnableDisplay(); + verify(mMockedExternalDisplayStatsService, never()).onDisplayConnected(any()); + verify(mMockedExternalDisplayStatsService, never()).onDisplayDisconnected(anyInt()); + } + + @Test public void testOnExternalDisplayAvailable_criticalThermalCondition() throws RemoteException { // Disallow external displays due to thermals. @@ -303,8 +317,14 @@ mDisplayEventCaptor.capture()); assertThat(mLogicalDisplayCaptor.getValue()).isEqualTo(mMockedLogicalDisplay); assertThat(mDisplayEventCaptor.getValue()).isEqualTo(EVENT_DISPLAY_CONNECTED); + verify(mMockedLogicalDisplay).setEnabledLocked(false); clearInvocations(mMockedLogicalDisplayMapper); - when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(true); + clearInvocations(mMockedLogicalDisplay); + } + + private void assertNotAskedToEnableDisplay() { + verify(mMockedInjector, never()).sendExternalDisplayEventLocked(any(), anyInt()); + verify(mMockedLogicalDisplay, never()).setEnabledLocked(anyBoolean()); } private void assertIsExternalDisplayAllowed(final boolean enabled) {