Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45520e52 authored by Matt Sziklay's avatar Matt Sziklay
Browse files

Remove preserved display on restore start.

Rather than removing the preserved display once restore is finished, remove it before calling restoreDisplay and use the removed display to perform the transition. To help facilitate this, moves desktop's KeyguardChangeListener from DesktopTasksController to DesktopDisplayEventHandler.

This is to ensure multiple restoreDisplay calls do not occur for the same display, as this would cause issues with incorrect desk activation and tasks randomly unminimizing.

Removes displaysMidRestoration tracking as it's no longer needed.

Bug: 440280450
Bug: 440248564
Bug: 440399938
Test: Manual, perform repeated reconnects on displays with lots of tasks
Flag: com.android.window.flags.enable_display_reconnect_interaction
Change-Id: I0836759f896eaef9aae40597a539b9ea96726136
parent 2919442a
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1734,7 +1734,8 @@ public abstract class WMShellModule {
            DesktopRepositoryInitializer desktopRepositoryInitializer,
            Optional<DesksTransitionObserver> desksTransitionObserver,
            DesktopState desktopState,
            Transitions transitions
            Transitions transitions,
            KeyguardManager keyguardManager
    ) {
        if (!desktopState.canEnterDesktopMode()) {
            return Optional.empty();
@@ -1753,7 +1754,8 @@ public abstract class WMShellModule {
                        desktopDisplayModeController.get(),
                        desksTransitionObserver.get(),
                        desktopState,
                        transitions));
                        transitions,
                        keyguardManager));
    }

    @WMSingleton
+51 −41
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.desktopmode

import android.app.KeyguardManager
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
@@ -23,7 +24,6 @@ import android.os.IBinder
import android.os.Trace
import android.os.UserHandle
import android.os.UserManager
import android.util.ArraySet
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
@@ -34,7 +34,6 @@ import android.window.DesktopModeFlags
import android.window.DisplayAreaInfo
import android.window.TransitionInfo
import com.android.app.tracing.traceSection
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener
@@ -52,6 +51,7 @@ import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.desktopmode.multidesks.PreserveDisplayRequestHandler
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopState
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.UserChangeListener
@@ -75,15 +75,23 @@ class DesktopDisplayEventHandler(
    private val desksTransitionObserver: DesksTransitionObserver,
    private val desktopState: DesktopState,
    private val transitions: Transitions,
    private val keyguardManager: KeyguardManager,
) :
    OnDisplaysChangedListener,
    OnDeskRemovedListener,
    PreserveDisplayRequestHandler,
    Transitions.TransitionObserver {
    Transitions.TransitionObserver,
    KeyguardChangeListener {

    private val onDisplayAreaChangeListener = OnDisplayAreaChangeListener { displayId ->
        logV("displayAreaChanged in displayId=%d", displayId)
        if (!handlePotentialReconnect(displayId)) {
        val keyguardLocked = keyguardManager.isKeyguardLocked
        logV(
            "displayAreaChanged in displayId=%d, keyguardLocked=%b",
            displayId,
            keyguardLocked
        )
        // Do not create default desk if keyguard is locked. It will be handled on unlock.
        if (!handlePotentialReconnect(displayId) && !keyguardLocked) {
            createDefaultDesksIfNeeded(displayIds = listOf(displayId), userId = null)
        }
    }
@@ -96,9 +104,6 @@ class DesktopDisplayEventHandler(
    private val boundsChangedByDisplayId = mutableSetOf<Int>()
    private val stableBoundsChangedByDisplayId = mutableSetOf<Int>()
    private val displayConfigById = mutableMapOf<Int, Configuration>()
    // All uniqueDisplayIds that are currently being restored; any further requests
    // to restore them will no-op.
    @VisibleForTesting val displaysMidRestoration = ArraySet<String>()

    init {
        shellInit.addInitCallback({ onInit() }, this)
@@ -120,6 +125,7 @@ class DesktopDisplayEventHandler(
            )
            if (DesktopExperienceFlags.ENABLE_DISPLAY_RECONNECT_INTERACTION.isTrue) {
                desktopTasksController.preserveDisplayRequestHandler = this
                shellController.addKeyguardChangeListener(this)
            }
        }
    }
@@ -284,13 +290,6 @@ class DesktopDisplayEventHandler(
                desktopDisplayModeController.updateDefaultDisplayWindowingMode()
            }
            val uniqueDisplayId = uniqueIdByDisplayId[displayId]
            if (uniqueDisplayId != null && uniqueDisplayId in displaysMidRestoration) {
                logW(
                    "onDisplayRemoved: Found display mid-restoration that did not finish: " +
                        "displayId=$displayId, uniqueDisplayId=$uniqueDisplayId"
                )
                displaysMidRestoration.remove(uniqueDisplayId)
            }
            uniqueIdByDisplayId.remove(displayId)
        }

@@ -317,12 +316,15 @@ class DesktopDisplayEventHandler(
    private fun handlePotentialDeskDisplayChange(displayId: Int) {
        if (desktopState.isDesktopModeSupportedOnDisplay(displayId)) {
            // A display has become desktop eligible. Treat this as a potential reconnect.
            val uniqueId = displayController.getDisplay(displayId)?.uniqueId ?: return
            val keyguardLocked = keyguardManager.isKeyguardLocked
            logV(
                "onDesktopModeEligibleChanged: displayId=%d has become desktop eligible",
                "onDesktopModeEligibleChanged: keyguardLocked=%b, " +
                        "displayId=%d has become desktop eligible",
                displayId,
                keyguardLocked
            )
            if (!handlePotentialReconnect(displayId)) {
            // Do not create default desk if keyguard is locked. It will be handled on unlock.
            if (!handlePotentialReconnect(displayId) && !keyguardLocked) {
                createDefaultDesksIfNeeded(displayIds = listOf(displayId), userId = null)
            }
        } else {
@@ -335,7 +337,26 @@ class DesktopDisplayEventHandler(
        }
    }

    override fun onKeyguardVisibilityChanged(
        visible: Boolean,
        occluded: Boolean,
        animatingDismiss: Boolean,
    ) {
        if (visible) return
        val displaysByUniqueId = displayController.allDisplaysByUniqueId ?: return
        val defaultDeskDisplayIds = mutableSetOf<Int>()
        for (displayIdByUniqueId in displaysByUniqueId) {
            val displayId = displayIdByUniqueId.value
            if (displayId != DEFAULT_DISPLAY && !handlePotentialReconnect(displayId)) {
                defaultDeskDisplayIds.add(displayIdByUniqueId.value)
            }
        }
        createDefaultDesksIfNeeded(defaultDeskDisplayIds, null)
    }

    private fun handlePotentialReconnect(displayId: Int): Boolean {
        // Do not handle restoration while locked; it will be handled when keyguard is gone.
        if (keyguardManager.isKeyguardLocked) return false
        val uniqueDisplayId = displayController.getDisplay(displayId)?.uniqueId ?: return false
        uniqueIdByDisplayId[displayId] = uniqueDisplayId
        val currentUserRepository = desktopUserRepositories.current
@@ -343,21 +364,17 @@ class DesktopDisplayEventHandler(
            logV("handlePotentialReconnect: Reconnect not supported; aborting.")
            return false
        }
        if (uniqueDisplayId in displaysMidRestoration) {
            logV(
                "handlePotentialReconnect: uniqueDisplay=$uniqueDisplayId " +
                    "mid-restoration; aborting."
            )
            return false
        }
        if (!currentUserRepository.hasPreservedDisplayForUniqueDisplayId(uniqueDisplayId)) {
        // To ensure only one restoreDisplay is actually called, remove the preserved display.
        val preservedDisplay = currentUserRepository.removePreservedDisplay(uniqueDisplayId)
        if (preservedDisplay == null) {
            logV(
                "handlePotentialReconnect: No preserved display found for " +
                    "uniqueDisplayId=$uniqueDisplayId; aborting."
            )
            return false
        }
        val preservedTasks =
            currentUserRepository.getPreservedTasks(uniqueDisplayId).toMutableList()
            currentUserRepository.getPreservedTasks(preservedDisplay).toMutableList()
        // Projected mode: Do not move anything focused on the internal display.
        if (!desktopState.isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)) {
            val focusedDefaultDisplayTaskIds =
@@ -367,22 +384,15 @@ class DesktopDisplayEventHandler(
            preservedTasks.removeAll { taskId -> focusedDefaultDisplayTaskIds.contains(taskId) }
        }
        if (preservedTasks.isEmpty()) {
            // The preserved display is normally removed at the end of restoreDisplay.
            // If we don't restore anything, remove it here instead.
            currentUserRepository.removePreservedDisplay(uniqueDisplayId)
            // If we don't restore anything, skip the restoration and return false so we
            // create a default desk.
            return false
        }
        displaysMidRestoration.add(uniqueDisplayId)
        mainScope.launch {
            // If the display has been removed by the time this executes, restore nothing.
            if (uniqueDisplayId !in displaysMidRestoration) return@launch
        desktopTasksController.restoreDisplay(
            displayId = displayId,
                uniqueDisplayId = uniqueDisplayId,
            preservedDisplay = preservedDisplay,
            userId = desktopUserRepositories.current.userId,
        )
            displaysMidRestoration.remove(uniqueDisplayId)
        }
        return true
    }

+17 −44
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.data.DesktopDisplay
import com.android.wm.shell.desktopmode.data.DesktopRepository
import com.android.wm.shell.desktopmode.data.DesktopRepository.Companion.INVALID_DESK_ID
import com.android.wm.shell.desktopmode.data.DesktopRepository.DeskChangeListener
@@ -171,7 +172,6 @@ import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -266,8 +266,7 @@ class DesktopTasksController(
    RemoteCallable<DesktopTasksController>,
    Transitions.TransitionHandler,
    DragAndDropController.DragAndDropListener,
    UserChangeListener,
    KeyguardChangeListener {
    UserChangeListener {

    private val desktopMode: DesktopModeImpl
    private var visualIndicator: DesktopModeVisualIndicator? = null
@@ -365,7 +364,6 @@ class DesktopTasksController(
            }
        )
        dragAndDropController.addListener(this)
        shellController.addKeyguardChangeListener(this)
        desksOrganizer.addOnDesktopTaskInfoChangedListener { taskInfo ->
            onTaskInfoChanged(taskInfo)
        }
@@ -870,31 +868,6 @@ class DesktopTasksController(
        }
    }

    override fun onKeyguardVisibilityChanged(
        visible: Boolean,
        occluded: Boolean,
        animatingDismiss: Boolean,
    ) {
        logD(
            "onKeyguardVisibilityChanged visible=%b, occluded=%b, animatingDismiss=%b",
            visible,
            occluded,
            animatingDismiss,
        )
        if (visible) return
        val displaysByUniqueId = displayController.allDisplaysByUniqueId ?: return
        for (displayIdByUniqueId in displaysByUniqueId) {
            val taskRepository = userRepositories.current
            if (taskRepository.hasPreservedDisplayForUniqueDisplayId(displayIdByUniqueId.key)) {
                restoreDisplay(
                    displayId = displayIdByUniqueId.value,
                    uniqueDisplayId = displayIdByUniqueId.key,
                    userId = taskRepository.userId,
                )
            }
        }
    }

    private fun handleExtendedModeDisconnect(
        desktopRepository: DesktopRepository,
        wct: WindowContainerTransaction,
@@ -1121,21 +1094,20 @@ class DesktopTasksController(
     *
     * TODO: b/365873835 - Restore for all users, not just current.
     */
    fun restoreDisplay(displayId: Int, uniqueDisplayId: String, userId: Int) {
    fun restoreDisplay(displayId: Int, preservedDisplay: DesktopDisplay, userId: Int) {
        logD(
            "restoreDisplay: displayId=%d, uniqueDisplayId=%d userId=%d",
            "restoreDisplay: displayId=%d former displayId=%d userId=%d",
            displayId,
            uniqueDisplayId,
            preservedDisplay.displayId,
            userId,
        )
        // TODO: b/365873835 - Utilize DesktopTask data class once it is
        //  implemented in DesktopRepository.
        // Do not handle restoration while locked; it will be handled when keyguard is gone.
        if (keyguardManager.isKeyguardLocked) return
        val repository = userRepositories.getProfile(userId)
        val preservedTaskIdsByDeskId = repository.getPreservedTasksByDeskIdInZOrder(uniqueDisplayId)
        val boundsByTaskId = repository.getPreservedTaskBounds(uniqueDisplayId)
        val activeDeskId = repository.getPreservedActiveDesk(uniqueDisplayId)
        val preservedTaskIdsByDeskId =
            repository.getPreservedTasksByDeskIdInZOrder(preservedDisplay)
        val boundsByTaskId = repository.getPreservedTaskBounds(preservedDisplay)
        val activeDeskId = preservedDisplay.activeDeskId
        val wct = WindowContainerTransaction()
        var runOnTransitStartList = mutableListOf<RunOnTransitStart>()
        val tilingReconnectHandler =
@@ -1177,7 +1149,7 @@ class DesktopTasksController(
                                taskId = taskId,
                                userId = userId,
                                displayId = displayId,
                                uniqueDisplayId = uniqueDisplayId,
                                preservedDisplay = preservedDisplay,
                                taskBounds = boundsByTaskId[taskId],
                            )
                            ?.let { runOnTransitStartList.add(it) }
@@ -1185,7 +1157,7 @@ class DesktopTasksController(
                }

                val preservedTilingData =
                    repository.getPreservedTilingData(uniqueDisplayId, preservedDeskId)
                    repository.getPreservedTilingData(preservedDisplay, preservedDeskId)
                if (preservedTilingData != null) {
                    tilingReconnectHandler.addTilingDisplayReconnectSession(
                        TilingDisplayReconnectEventHandler.TilingDisplayReconnectSession(
@@ -1203,7 +1175,6 @@ class DesktopTasksController(
            val transition = transitions.startTransition(TRANSIT_CHANGE, wct, null)
            tilingReconnectHandler.activationBinder = transition
            runOnTransitStartList.forEach { it.invoke(transition) }
            repository.removePreservedDisplay(uniqueDisplayId)
        }
    }

@@ -1213,16 +1184,18 @@ class DesktopTasksController(
        taskId: Int,
        userId: Int,
        displayId: Int,
        uniqueDisplayId: String,
        preservedDisplay: DesktopDisplay,
        taskBounds: Rect?,
    ): RunOnTransitStart? {
        logD(
            "addRestoreTaskToDeskChanges: taskId=$taskId; deskId=$deskId; userId=$userId; " +
                "taskBounds=$taskBounds; uniqueDisplayId=$uniqueDisplayId"
                "taskBounds=$taskBounds."
        )

        val repository = userRepositories.getProfile(userId)
        val minimized = repository.isPreservedTaskMinimized(uniqueDisplayId, taskId)
        val minimized =
            preservedDisplay.orderedDesks.any { desk ->
                taskId in desk.minimizedTasks
            }
        val task =
            shellTaskOrganizer.getRunningTaskInfo(taskId)
                ?: recentTasksController?.findTaskInBackground(taskId)
+12 −28
Original line number Diff line number Diff line
@@ -162,20 +162,13 @@ class DesktopRepository(
    }

    /** Removes the specified preserved display. */
    fun removePreservedDisplay(uniqueDisplayId: String) {
    fun removePreservedDisplay(uniqueDisplayId: String) =
        preservedDisplaysByUniqueId.remove(uniqueDisplayId)
    }

    /** Whether or not the given uniqueDisplayId matches a display that is being preserved. */
    fun hasPreservedDisplayForUniqueDisplayId(uniqueDisplayId: String): Boolean =
        preservedDisplaysByUniqueId.containsKey(uniqueDisplayId)

    /** Returns all active tasks on the preserved display separated by desk. */
    fun getPreservedTasksByDeskIdInZOrder(uniqueDisplayId: String): Map<Int, List<Int>> {
        val preservedDesks =
            preservedDisplaysByUniqueId[uniqueDisplayId]?.orderedDesks ?: emptySet()
    fun getPreservedTasksByDeskIdInZOrder(preservedDisplay: DesktopDisplay): Map<Int, List<Int>> {
        val tasksByDeskId = mutableMapOf<Int, List<Int>>()
        for (desk in preservedDesks) {
        for (desk in preservedDisplay.orderedDesks) {
            tasksByDeskId[desk.deskId] = desk.freeformTasksInZOrder
        }
        return tasksByDeskId
@@ -184,28 +177,21 @@ class DesktopRepository(
    /**
     * Returns all preserved tasks for the preserved display regardless of what desk they appear in.
     */
    fun getPreservedTasks(uniqueDisplayId: String): List<Int> {
        val preservedDesks =
            preservedDisplaysByUniqueId[uniqueDisplayId]?.orderedDesks ?: emptySet()
    fun getPreservedTasks(preservedDisplay: DesktopDisplay): List<Int> {
        val preservedTasks = mutableListOf<Int>()
        for (desk in preservedDesks) {
        for (desk in preservedDisplay.orderedDesks) {
            desk.freeformTasksInZOrder.forEach { taskId -> preservedTasks.add(taskId) }
        }
        return preservedTasks
    }

    /** Returns the active desk on the preserved display for the specified unique display id. */
    fun getPreservedActiveDesk(uniqueDisplayId: String): Int? =
        preservedDisplaysByUniqueId[uniqueDisplayId]?.activeDeskId

    /** Returns a preserved desk data post a display reconnect event. */
    fun getPreservedTilingData(
        uniqueDisplayId: String,
        preservedDisplay: DesktopDisplay,
        preservedDeskId: Int?,
    ): PreservedTiledAppData? =
        preservedDisplaysByUniqueId[uniqueDisplayId]
            ?.orderedDesks
            ?.firstOrNull { desk -> desk.deskId == preservedDeskId }
        preservedDisplay.orderedDesks
            .firstOrNull { desk -> desk.deskId == preservedDeskId }
            ?.let { PreservedTiledAppData(it.leftTiledTaskId, it.rightTiledTaskId) }
            ?.takeIf { it.leftTiledTask != null || it.rightTiledTask != null }

@@ -219,19 +205,17 @@ class DesktopRepository(
        } ?: false

    /** Returns the bounds of all tasks in all desks of the preserved display. */
    fun getPreservedTaskBounds(uniqueDisplayId: String): Map<Int, Rect> {
    fun getPreservedTaskBounds(preservedDisplay: DesktopDisplay): Map<Int, Rect> {
        val combinedBoundsMap = mutableMapOf<Int, Rect>()
        val orderedDesks =
            preservedDisplaysByUniqueId[uniqueDisplayId]?.orderedDesks ?: return emptyMap()
        for (desk in orderedDesks) {
        for (desk in preservedDisplay.orderedDesks) {
            combinedBoundsMap.putAll(desk.boundsByTaskId)
        }
        return combinedBoundsMap
    }

    @VisibleForTesting
    fun getPreservedDeskIds(uniqueDisplayId: String): List<Int> =
        preservedDisplaysByUniqueId[uniqueDisplayId]?.orderedDesks?.map { it.deskId } ?: emptyList()
    fun getPreservedDeskIds(preservedDisplay: DesktopDisplay): List<Int> =
        preservedDisplay.orderedDesks.map { it.deskId }

    /** Returns a list of all [Desk]s in the repository. */
    private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence()
+61 −21

File changed.

Preview size limit exceeded, changes collapsed.

Loading