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

Commit b0a77564 authored by mattsziklay's avatar mattsziklay Committed by Matt Sziklay
Browse files

Persistent repo on external display fix.

Removes the early return blocking repository initialization if internal
display does not support desktop; instead, if a desk would be
initialized to DEFAULT_DISPLAY on such a device, mark it as transient
instead.

Transient desks will be recorded in repository for preservation
purposes only; once preserved, the desk will be removed from the
repository, making sure not to inform any listeners along the way.

Bug: 437420625
Test: Manual
Flag: com.android.window.flags.enable_display_reconnect_interaction
Flag: com.android.window.flags.enable_external_display_persistence_bugfix
Change-Id: I96c904b407baa40eb6a370a8e45d3fa0dc5ab706
parent b6a56c4f
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.common;

import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -125,6 +126,19 @@ public class DisplayController {
        return r != null ? r.mUniqueId : null;
    }

    /**
     * Gets the displayId associated with the provided uniqueId, if it is associated with one.
     * Because this calls an IPC, we should only use this in time sensitive cases where we suspect
     * DisplayManager has more up to date information than mDisplays (i.e., during reboot). For
     * other cases, use getAllDisplaysByUniqueId below.
     */
    public int getDisplayIdByUniqueIdBlocking(String uniqueId) {
        for (Display display : mDisplayManager.getDisplays()) {
            if (uniqueId.equals(display.getUniqueId())) return display.getDisplayId();
        }
        return INVALID_DISPLAY;
    }

    /**
     * Gets a map of all displays by uniqueId from DisplayManager.
     */
+8 −6
Original line number Diff line number Diff line
@@ -126,8 +126,6 @@ class DesktopDisplayEventHandler(
            // `displayId`.
            desktopDisplayModeController.updateDefaultDisplayWindowingMode()
            if (DesktopExperienceFlags.ENABLE_DISPLAY_RECONNECT_INTERACTION.isTrue) {
                // TODO - b/365873835: Restore a display if a uniqueId match is found in
                //  the desktop repository.
                displayController.getDisplay(displayId)?.uniqueId?.let { uniqueId ->
                    uniqueIdByDisplayId[displayId] = uniqueId
                }
@@ -209,13 +207,17 @@ class DesktopDisplayEventHandler(
            return false
        }
        if (uniqueDisplayId in displaysMidRestoration) {
            logV("handlePotentialReconnect: uniqueDisplay=$uniqueDisplayId " +
                "mid-restoration; aborting.")
            logV(
                "handlePotentialReconnect: uniqueDisplay=$uniqueDisplayId " +
                    "mid-restoration; aborting."
            )
            return false
        }
        if (!currentUserRepository.hasPreservedDisplayForUniqueDisplayId(uniqueDisplayId)) {
            logV("handlePotentialReconnect: No preserved display found for " +
                "uniqueDisplayId=$uniqueDisplayId; aborting.")
            logV(
                "handlePotentialReconnect: No preserved display found for " +
                    "uniqueDisplayId=$uniqueDisplayId; aborting."
            )
        }
        val preservedTasks =
            currentUserRepository.getPreservedTasks(uniqueDisplayId).toMutableList()
+9 −4
Original line number Diff line number Diff line
@@ -796,7 +796,6 @@ class DesktopTasksController(
        val wct = WindowContainerTransaction()
        addOnDisplayDisconnectChanges(wct, disconnectedDisplayId, destinationDisplayId)
            .invoke(transition)

        try {
            userRepositories.current.getExpandedTasksOrdered(disconnectedDisplayId).forEach {
                logD("addOnDisplayDisconnect: taking a snapshot of=$it before disconnect")
@@ -878,15 +877,21 @@ class DesktopTasksController(
        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(
                    displayIdByUniqueId.value,
                    displayIdByUniqueId.key,
                    taskRepository.userId,
                    displayId = displayIdByUniqueId.value,
                    uniqueDisplayId = displayIdByUniqueId.key,
                    userId = taskRepository.userId,
                )
            }
        }
+2 −0
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ data class Desk(
    var rightTiledTaskId: Int? = null,
    // The display's unique id that will remain the same across reboots.
    var uniqueDisplayId: String? = null,
    // A desk that will only exist briefly; remove it once it is preserved. Used for persistence.
    var transientDesk: Boolean = false,
) {
    // TODO: b/417907552 - Add these variables to persistent repository.
    // Bounds of tasks in this desk mapped to their respective task ids. Used for reconnect.
+91 −19
Original line number Diff line number Diff line
@@ -132,15 +132,40 @@ class DesktopRepository(
    }

    /** Stores the last state of the given display, along with the bounds of the tasks on it. */
    fun preserveDisplay(displayId: Int, uniqueId: String) {
        logD("preserveDisplay for displayId=%d, uniqueId=%s", displayId, uniqueId)
    fun preserveDisplay(displayId: Int, uniqueDisplayId: String) {
        logD("preserveDisplay for displayId=%d, uniqueId=%s", displayId, uniqueDisplayId)
        if (
            preservedDisplaysByUniqueId.containsKey(uniqueDisplayId) &&
                DesktopExperienceFlags.ENABLE_EXTERNAL_DISPLAY_PERSISTENCE_BUGFIX.isTrue
        ) {
            // Prevents multiple preserve requests from overwriting the previously
            // preserved display. Occurs during boot where we see display disabled
            // + display becoming desktop ineligible both requesting preserve.
            logD("Display already preserved; aborting.")
            return
        }
        val orderedDesks = desktopData.getOrderedDesks(displayId)
        // Do not preserve the display if there are no active tasks on it.
        if (!orderedDesks.any { it.activeTasks.isNotEmpty() }) return
        val preservedDisplay = DesktopDisplay(displayId)
        val preservedDisplay = DesktopDisplay(INVALID_DISPLAY)
        orderedDesks.mapTo(preservedDisplay.orderedDesks) { it.deepCopy() }
        preservedDisplay.activeDeskId = desktopData.getActiveDesk(displayId)?.deskId
        preservedDisplaysByUniqueId[uniqueId] = preservedDisplay
        preservedDisplaysByUniqueId[uniqueDisplayId] = preservedDisplay
    }

    /** Stores the last state of a single desk, creating a new PreservedDisplay if needed. */
    fun preserveDesk(deskId: Int, uniqueDisplayId: String) {
        logD("preserveDesk for deskId=%d, uniqueDisplayId=%s", deskId, uniqueDisplayId)
        val desk = desktopData.getDesk(deskId) ?: return
        if (desk.activeTasks.isEmpty()) return
        val preservedDisplay =
            preservedDisplaysByUniqueId[uniqueDisplayId]
                ?: DesktopDisplay(INVALID_DISPLAY).also {
                    preservedDisplaysByUniqueId[uniqueDisplayId] = it
                }
        preservedDisplay.orderedDesks.add(desk)
        // The transient desk is preserved and has served its purpose, it can be removed now.
        if (desk.transientDesk) removeDesk(deskId)
    }

    /** Removes the specified preserved display. */
@@ -261,10 +286,19 @@ class DesktopRepository(
    }

    /** Adds the given desk under the given display. */
    fun addDesk(displayId: Int, deskId: Int, uniqueDisplayId: String? = null) {
    // TODO: b/441764871 - Extract transientDesk logic into desk-creating vs desk-saving functions.
    //  To avoid ifs in order to dodge listener execution on transient desks, we should create
    //  the desk fully before deciding to either commit it to DesktopData or preserve it.
    fun addDesk(
        displayId: Int,
        deskId: Int,
        uniqueDisplayId: String? = null,
        transientDesk: Boolean = false,
    ) {
        logD("addDesk for displayId=%d and deskId=%d", displayId, deskId)
        val couldCreateDesk = canCreateDesks()
        desktopData.createDesk(displayId, deskId, uniqueDisplayId)
        desktopData.createDesk(displayId, deskId, uniqueDisplayId, transientDesk)
        if (transientDesk) return
        val canCreateDesk = canCreateDesks()
        deskChangeListeners.forEach { (listener, executor) ->
            executor.execute {
@@ -364,6 +398,7 @@ class DesktopRepository(
            return
        }
        desktopData.setActiveDesk(displayId = displayId, deskId = deskId)
        if (desktopData.getDesk(deskId)?.transientDesk == true) return
        deskChangeListeners.forEach { (listener, executor) ->
            executor.execute {
                listener.onActiveDeskChanged(
@@ -404,7 +439,7 @@ class DesktopRepository(
        )
        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
        desk.leftTiledTaskId = taskId
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
        if (!desk.transientDesk && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            if (DesktopExperienceFlags.REPOSITORY_BASED_PERSISTENCE.isTrue) {
                updatePersistentRepository(displayId)
            } else {
@@ -422,7 +457,7 @@ class DesktopRepository(
        )
        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
        desk.rightTiledTaskId = taskId
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
        if (!desk.transientDesk && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            if (DesktopExperienceFlags.REPOSITORY_BASED_PERSISTENCE.isTrue) {
                updatePersistentRepository(displayId)
            } else {
@@ -538,8 +573,8 @@ class DesktopRepository(

        // Removes task if it is active on another desk excluding this desk.
        removeActiveTask(taskId, excludedDeskId = deskId)

        if (desk.activeTasks.add(taskId)) {
            if (desk.transientDesk) return
            updateActiveTasksListeners(displayId)
        } else {
            logD("Active task=%d already added, displayId=%d, deskId=%d", taskId, displayId, deskId)
@@ -562,7 +597,7 @@ class DesktopRepository(
                        desk.displayId,
                        desk.deskId,
                    )
                    affectedDisplays.add(desk.displayId)
                    if (!desk.transientDesk) affectedDisplays.add(desk.displayId)
                }
            }
        affectedDisplays.forEach { displayId -> updateActiveTasksListeners(displayId) }
@@ -830,6 +865,7 @@ class DesktopRepository(
            desk.visibleTasks.remove(taskId)
        }
        taskBounds?.let { desk.boundsByTaskId[taskId] = it }
        if (desk.transientDesk) return
        val newCount = getVisibleTaskCountInDesk(deskId)
        if (prevCount != newCount) {
            logD(
@@ -972,6 +1008,7 @@ class DesktopRepository(
        }
        desk.freeformTasksInZOrder.add(0, taskId)
        if (!bounds.isEmpty) desk.boundsByTaskId[taskId] = bounds
        if (desk.transientDesk) return
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            // TODO: can probably just update the desk.
            updatePersistentRepository(displayId)
@@ -1003,9 +1040,11 @@ class DesktopRepository(
    /** Minimizes the task in its desk. */
    fun minimizeTaskInDesk(displayId: Int, deskId: Int, taskId: Int) {
        logD("MinimizeTaskInDesk: displayId=%d deskId=%d, task=%d", displayId, deskId, taskId)
        desktopData.getDesk(deskId)?.minimizedTasks?.add(taskId)
        val desk = desktopData.getDesk(deskId)
        desk?.minimizedTasks?.add(taskId)
            ?: logD("Minimize task: No active desk found for task: taskId=%d", taskId)
        updateTaskInDesk(displayId, deskId, taskId, isVisible = false, taskBounds = null)
        if (desk?.transientDesk == true) return
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            if (DesktopExperienceFlags.REPOSITORY_BASED_PERSISTENCE.isTrue) {
                updatePersistentRepository(displayId)
@@ -1086,7 +1125,7 @@ class DesktopRepository(
        setTaskInFullImmersiveStateInDesk(deskId = deskId, taskId = taskId, immersive = false)
        removeActiveTaskFromDesk(deskId = deskId, taskId = taskId)
        removeVisibleTaskFromDesk(deskId = deskId, taskId = taskId)
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
        if (!desk.transientDesk && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            if (DesktopExperienceFlags.REPOSITORY_BASED_PERSISTENCE.isTrue) {
                updatePersistentRepository(desk.displayId)
            } else {
@@ -1107,6 +1146,10 @@ class DesktopRepository(
        val wasActive = desktopData.getActiveDesk(desk.displayId)?.deskId == desk.deskId
        val activeTasks = ArraySet(desk.activeTasks)
        desktopData.remove(desk.deskId)
        if (desk.transientDesk) {
            // No need to inform listeners; just return active tasks.
            return activeTasks
        }
        val canCreateDesks = canCreateDesks()
        notifyVisibleTaskListeners(desk.displayId, getVisibleTaskCount(displayId = desk.displayId))
        deskChangeListeners.forEach { (listener, executor) ->
@@ -1220,7 +1263,11 @@ class DesktopRepository(
                    Trace.beginSection("DesktopRepository#UpdateRepoWork")
                    logD("updatePersistentRepository user=%d display=%d", userId, displayId)
                    try {
                        persistentRepository.addOrUpdateRepository(userId, desks)
                        persistentRepository.addOrUpdateRepository(
                            userId,
                            desks,
                            getActiveDeskId(displayId),
                        )
                    } catch (exception: Exception) {
                        logE(
                            "An exception occurred while updating the persistent repository \n%s",
@@ -1374,7 +1421,12 @@ class DesktopRepository(
    /** An interface for the desktop hierarchy's data managed by this repository. */
    private interface DesktopData {
        /** Creates a desk record. */
        fun createDesk(displayId: Int, deskId: Int, uniqueDisplayId: String?)
        fun createDesk(
            displayId: Int,
            deskId: Int,
            uniqueDisplayId: String?,
            transientDesk: Boolean,
        )

        /** Adds an existing desk to a specific display */
        fun addDesk(displayId: Int, desk: Desk)
@@ -1449,19 +1501,29 @@ class DesktopRepository(
        private val deskByDisplayId =
            object : SparseArray<Desk>() {
                /** Gets [Desk] for existing [displayId] or creates a new one. */
                fun getOrCreate(displayId: Int, uniqueDisplayId: String? = null): Desk =
                fun getOrCreate(
                    displayId: Int,
                    uniqueDisplayId: String? = null,
                    transientDesk: Boolean = false,
                ): Desk =
                    this[displayId]
                        ?: Desk(
                                deskId = displayId,
                                displayId = displayId,
                                uniqueDisplayId = uniqueDisplayId,
                                transientDesk = transientDesk,
                            )
                            .also { this[displayId] = it }
            }

        override fun createDesk(displayId: Int, deskId: Int, uniqueDisplayId: String?) {
        override fun createDesk(
            displayId: Int,
            deskId: Int,
            uniqueDisplayId: String?,
            transientDesk: Boolean,
        ) {
            check(displayId == deskId) { "Display and desk ids must match" }
            deskByDisplayId.getOrCreate(displayId, uniqueDisplayId)
            deskByDisplayId.getOrCreate(displayId, uniqueDisplayId, transientDesk)
        }

        override fun addDesk(displayId: Int, desk: Desk) {
@@ -1537,7 +1599,12 @@ class DesktopRepository(
    private class MultiDesktopData : DesktopData {
        private val desktopDisplays = SparseArray<DesktopDisplay>()

        override fun createDesk(displayId: Int, deskId: Int, uniqueDisplayId: String?) {
        override fun createDesk(
            displayId: Int,
            deskId: Int,
            uniqueDisplayId: String?,
            transientDesk: Boolean,
        ) {
            val display =
                desktopDisplays[displayId]
                    ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it }
@@ -1545,7 +1612,12 @@ class DesktopRepository(
                "Attempting to create desk#$deskId that already exists in display#$displayId"
            }
            display.orderedDesks.add(
                Desk(deskId = deskId, displayId = displayId, uniqueDisplayId = uniqueDisplayId)
                Desk(
                    deskId = deskId,
                    displayId = displayId,
                    uniqueDisplayId = uniqueDisplayId,
                    transientDesk = transientDesk,
                )
            )
        }

Loading