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

Commit b7cc19dc authored by Jorge Gil's avatar Jorge Gil Committed by Android (Google) Code Review
Browse files

Merge "Desks: Allow pre-creating desk roots without creating the desk" into main

parents 405f6f5f 06bfc124
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1384,6 +1384,7 @@ public abstract class WMShellModule {
            ShellController shellController,
            DisplayController displayController,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            DesksOrganizer desksOrganizer,
            Optional<DesktopUserRepositories> desktopUserRepositories,
            Optional<DesktopTasksController> desktopTasksController,
            Optional<DesktopDisplayModeController> desktopDisplayModeController,
@@ -1401,6 +1402,7 @@ public abstract class WMShellModule {
                        shellController,
                        displayController,
                        rootTaskDisplayAreaOrganizer,
                        desksOrganizer,
                        desktopRepositoryInitializer,
                        desktopUserRepositories.get(),
                        desktopTasksController.get(),
+35 −16
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.multidesks.OnDeskDisplayChangeListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
@@ -44,6 +45,7 @@ class DesktopDisplayEventHandler(
    private val shellController: ShellController,
    private val displayController: DisplayController,
    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
    private val desksOrganizer: DesksOrganizer,
    private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
    private val desktopUserRepositories: DesktopUserRepositories,
    private val desktopTasksController: DesktopTasksController,
@@ -120,22 +122,19 @@ class DesktopDisplayEventHandler(
                val repository =
                    userId?.let { desktopUserRepositories.getProfile(userId) }
                        ?: desktopUserRepositories.current
                displayIds
                    .filter { displayId -> displayId != Display.INVALID_DISPLAY }
                    .filter { displayId -> supportsDesks(displayId) }
                    .filter { displayId -> repository.getNumberOfDesks(displayId) == 0 }
                    .also { displaysNeedingDesk ->
                        logV(
                            "createDefaultDesksIfNeeded creating default desks in displays=%s",
                            displaysNeedingDesk,
                        )
                    }
                    .forEach { displayId ->
                for (displayId in displayIds) {
                    if (!shouldCreateOrWarmUpDesk(displayId, repository)) continue
                    if (isDisplayDesktopFirst(displayId)) {
                        logV("Display %d is desktop-first and needs a default desk", displayId)
                        desktopTasksController.createDesk(
                            displayId,
                            repository.userId,
                            isDesktopFirstDisplay(displayId),
                            displayId = displayId,
                            userId = repository.userId,
                            activateDesk = true,
                        )
                    } else {
                        logV("Display %d is touch-first and needs to warm up a desk", displayId)
                        desksOrganizer.warmUpDefaultDesk(displayId, repository.userId)
                    }
                }
                cancel()
            }
@@ -149,8 +148,28 @@ class DesktopDisplayEventHandler(
        desktopTasksController.onDeskDisconnectTransition(deskDisplayChanges)
    }

    // TODO: b/393978539 - implement this
    private fun isDesktopFirstDisplay(displayId: Int): Boolean = displayId != DEFAULT_DISPLAY
    private fun shouldCreateOrWarmUpDesk(displayId: Int, repository: DesktopRepository): Boolean {
        if (displayId == Display.INVALID_DISPLAY) {
            logV("shouldCreateOrWarmUpDesk skipping reason: invalid display")
            return false
        }
        if (!supportsDesks(displayId)) {
            logV(
                "shouldCreateOrWarmUpDesk skipping displayId=%d reason: desktop ineligible",
                displayId,
            )
            return false
        }
        if (repository.getNumberOfDesks(displayId) > 0) {
            logV("shouldCreateOrWarmUpDesk skipping displayId=%d reason: has desk(s)", displayId)
            return false
        }
        return true
    }

    // TODO: b/362720497 - connected/projected display considerations.
    private fun isDisplayDesktopFirst(displayId: Int): Boolean =
        displayId != Display.DEFAULT_DISPLAY

    // TODO: b/362720497 - connected/projected display considerations.
    private fun supportsDesks(displayId: Int): Boolean =
+100 −22
Original line number Diff line number Diff line
@@ -292,7 +292,9 @@ class DesktopTasksController(
        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
            desktopRepositoryInitializer.deskRecreationFactory =
                DeskRecreationFactory { deskUserId, destinationDisplayId, _ ->
                    createDeskSuspending(displayId = destinationDisplayId, userId = deskUserId)
                    // TODO: b/393978539 - One of the recreated desks may need to be activated by
                    //  default in desktop-first.
                    createDeskRootSuspending(displayId = destinationDisplayId, userId = deskUserId)
                }
        }
    }
@@ -487,7 +489,7 @@ class DesktopTasksController(
    fun createDesk(displayId: Int, userId: Int = this.userId, activateDesk: Boolean = false) {
        logV("addDesk displayId=%d, userId=%d", displayId, userId)
        val repository = userRepositories.getProfile(userId)
        createDesk(displayId, userId) { deskId ->
        createDeskRoot(displayId, userId) { deskId ->
            if (deskId == null) {
                logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId)
            } else {
@@ -499,7 +501,24 @@ class DesktopTasksController(
        }
    }

    private fun createDesk(displayId: Int, userId: Int = this.userId, onResult: (Int?) -> Unit) {
    @Deprecated("Use createDesk() instead.", ReplaceWith("createDesk()"))
    private fun createDeskImmediate(displayId: Int, userId: Int = this.userId): Int? {
        logV("createDeskImmediate displayId=%d, userId=%d", displayId, userId)
        val repository = userRepositories.getProfile(userId)
        val deskId = createDeskRootImmediate(displayId, userId)
        if (deskId == null) {
            logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId)
            return null
        }
        repository.addDesk(displayId = displayId, deskId = deskId)
        return deskId
    }

    private fun createDeskRoot(
        displayId: Int,
        userId: Int = this.userId,
        onResult: (Int?) -> Unit,
    ) {
        if (displayId == Display.INVALID_DISPLAY) {
            logW("createDesk attempt with invalid displayId", displayId)
            onResult(null)
@@ -530,9 +549,34 @@ class DesktopTasksController(
        }
    }

    private suspend fun createDeskSuspending(displayId: Int, userId: Int = this.userId): Int? =
    @Deprecated(
        "Use createDeskRootSuspending() instead.",
        ReplaceWith("createDeskRootSuspending()"),
    )
    private fun createDeskRootImmediate(displayId: Int, userId: Int): Int? {
        if (displayId == Display.INVALID_DISPLAY) {
            logW("createDeskRootImmediate attempt with invalid displayId", displayId)
            return null
        }
        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
            // In single-desk, the desk reuses the display id.
            logD("createDeskRootImmediate reusing displayId=%d for single-desk", displayId)
            return displayId
        }
        if (
            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM.isTrue &&
                UserManager.isHeadlessSystemUserMode() &&
                UserHandle.USER_SYSTEM == userId
        ) {
            logW("createDeskRootImmediate ignoring attempt for system user")
            return null
        }
        return desksOrganizer.createDeskImmediate(displayId, userId)
    }

    private suspend fun createDeskRootSuspending(displayId: Int, userId: Int = this.userId): Int? =
        suspendCoroutine { cont ->
            createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
            createDeskRoot(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
        }

    /**
@@ -609,7 +653,7 @@ class DesktopTasksController(
            logW("moveTaskToDefaultDeskAndActivate taskId=%d not found", taskId)
            return false
        }
        val deskId = getDefaultDeskId(task.displayId)
        val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return false
        return moveTaskToDesk(
            taskId = taskId,
            deskId = deskId,
@@ -668,7 +712,7 @@ class DesktopTasksController(
            return false
        }
        logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
        val deskId = getDefaultDeskId(task.displayId)
        val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return false
        val runOnTransitStart = addDeskActivationChanges(deskId, wct, task)
        val exitResult =
            desktopImmersiveController.exitImmersiveIfApplicable(
@@ -793,7 +837,7 @@ class DesktopTasksController(
     * [startDragToDesktop].
     */
    private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo) {
        val deskId = getDefaultDeskId(taskInfo.displayId)
        val deskId = getOrCreateDefaultDeskId(taskInfo.displayId) ?: return
        ProtoLog.v(
            WM_SHELL_DESKTOP_MODE,
            "DesktopTasksController: finalizeDragToDesktop taskId=%d deskId=%d",
@@ -912,7 +956,7 @@ class DesktopTasksController(
                    logW("minimizeTask: desk not found for task: ${taskInfo.taskId}")
                    return
                } else {
                    getDefaultDeskId(taskInfo.displayId)
                    getOrCreateDefaultDeskId(taskInfo.displayId)
                }
        val isLastTask =
            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -1168,7 +1212,10 @@ class DesktopTasksController(
                .apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
                .toBundle(),
        )
        val deskId = taskRepository.getDeskIdForTask(taskId) ?: getDefaultDeskId(DEFAULT_DISPLAY)
        val deskId =
            taskRepository.getDeskIdForTask(taskId)
                ?: getOrCreateDefaultDeskId(DEFAULT_DISPLAY)
                ?: return
        startLaunchTransition(
            TRANSIT_OPEN,
            wct,
@@ -1193,7 +1240,9 @@ class DesktopTasksController(
        unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
    ) {
        val deskId =
            taskRepository.getDeskIdForTask(taskInfo.taskId) ?: getDefaultDeskId(taskInfo.displayId)
            taskRepository.getDeskIdForTask(taskInfo.taskId)
                ?: getOrCreateDefaultDeskId(taskInfo.displayId)
                ?: return
        logV("moveTaskToFront taskId=%s deskId=%s", taskInfo.taskId, deskId)
        // If a task is tiled, another task should be brought to foreground with it so let
        // tiling controller handle the request.
@@ -1381,7 +1430,7 @@ class DesktopTasksController(
            }

        wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
        val deskId = getDefaultDeskId(displayId)
        val deskId = getOrCreateDefaultDeskId(displayId) ?: return
        startLaunchTransition(
            TRANSIT_OPEN,
            wct,
@@ -2332,7 +2381,7 @@ class DesktopTasksController(
                    unminimizeReason = UnminimizeReason.APP_HANDLE_MENU_BUTTON,
                )
            } else {
                val deskId = getDefaultDeskId(callingTask.displayId)
                val deskId = getOrCreateDefaultDeskId(callingTask.displayId) ?: return
                moveTaskToDesk(
                    requestedTaskId,
                    deskId,
@@ -2403,7 +2452,8 @@ class DesktopTasksController(
                wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
                val deskId =
                    taskRepository.getDeskIdForTask(callingTaskInfo.taskId)
                        ?: getDefaultDeskId(callingTaskInfo.displayId)
                        ?: getOrCreateDefaultDeskId(callingTaskInfo.displayId)
                        ?: return
                startLaunchTransition(
                    transitionType = TRANSIT_OPEN,
                    wct = wct,
@@ -2518,7 +2568,7 @@ class DesktopTasksController(
            logV("skip keyguard is locked")
            return null
        }
        val deskId = getDefaultDeskId(task.displayId)
        val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return null
        val isKnownDesktopTask = taskRepository.isActiveTask(task.taskId)
        val shouldEnterDesktop =
            forceEnterDesktop
@@ -2633,7 +2683,7 @@ class DesktopTasksController(
        if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) {
            logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId)
            return WindowContainerTransaction().also { wct ->
                val deskId = getDefaultDeskId(task.displayId)
                val deskId = getOrCreateDefaultDeskId(task.displayId) ?: return@also
                addMoveToDeskTaskChanges(wct = wct, task = task, deskId = deskId)
                val runOnTransitStart: RunOnTransitStart? =
                    if (
@@ -3128,7 +3178,7 @@ class DesktopTasksController(
        displayId: Int,
        remoteTransition: RemoteTransition? = null,
    ) {
        val deskId = getDefaultDeskId(displayId)
        val deskId = getOrCreateDefaultDeskId(displayId) ?: return
        activateDesk(deskId, remoteTransition)
    }

@@ -3343,13 +3393,37 @@ class DesktopTasksController(
    /** Removes the default desk in the given display. */
    @Deprecated("Deprecated with multi-desks.", ReplaceWith("removeDesk()"))
    fun removeDefaultDeskInDisplay(displayId: Int) {
        val deskId = getDefaultDeskId(displayId)
        val deskId = getOrCreateDefaultDeskId(displayId) ?: return
        removeDesk(displayId = displayId, deskId = deskId)
    }

    private fun getDefaultDeskId(displayId: Int) =
        checkNotNull(taskRepository.getDefaultDeskId(displayId)) {
            "Expected a default desk to exist in display: $displayId"
    /**
     * Returns the default desk if it exists, or creates it if needed.
     *
     * Note: [DesktopDisplayEventHandler] is responsible for creating a default desk in
     * desktop-first displays or warming up a desk-root in touch-first displays. This guarantees
     * that a non-null desk can be returned by this function because even if one does not exist yet,
     * [createDeskImmediate] should succeed.
     *
     * TODO: b/406890311 - replace with a suspending version that can wait for a desk to be created
     *   from scratch before continuing their normal flow.
     */
    @Deprecated("Use createDesk() instead", ReplaceWith("createDesk()"))
    private fun getOrCreateDefaultDeskId(displayId: Int): Int? {
        val existingDefaultDeskId = taskRepository.getDefaultDeskId(displayId)
        if (existingDefaultDeskId != null) {
            return existingDefaultDeskId
        }
        val immediateDeskId = createDeskImmediate(displayId, userId)
        if (immediateDeskId == null) {
            logE(
                "Failed to create immediate desk in displayId=%s for userId=%s:\n%s",
                displayId,
                userId,
                Throwable().stackTraceToString(),
            )
        }
        return immediateDeskId
    }

    /** Removes the given desk. */
@@ -3883,7 +3957,7 @@ class DesktopTasksController(
        if (windowingMode == WINDOWING_MODE_FREEFORM) {
            if (DesktopModeFlags.ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX.isTrue()) {
                // TODO b/376389593: Use a custom tab tearing transition/animation
                val deskId = getDefaultDeskId(DEFAULT_DISPLAY)
                val deskId = getOrCreateDefaultDeskId(DEFAULT_DISPLAY) ?: return false
                startLaunchTransition(
                    TRANSIT_OPEN,
                    wct,
@@ -4280,6 +4354,10 @@ class DesktopTasksController(
        ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }

    private fun logE(msg: String, vararg arguments: Any?) {
        ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }

    companion object {
        @JvmField
        val DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
+10 −0
Original line number Diff line number Diff line
@@ -21,9 +21,19 @@ import android.window.WindowContainerTransaction

/** An organizer of desk containers in which to host child desktop windows. */
interface DesksOrganizer {
    /** Creates a new desk for the given user if none exist. */
    fun warmUpDefaultDesk(displayId: Int, userId: Int)

    /** Creates a new desk container to use in the given display for the given user. */
    fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback)

    /**
     * Creates and returns the id of a new desk container to use in the given display for the given
     * user if it can be created synchronously, or null if it cannot.
     */
    @Deprecated("Use createDesk() instead.", ReplaceWith("createDesk()"))
    fun createDeskImmediate(displayId: Int, userId: Int): Int?

    /** Activates the given desk, making it visible in its display. */
    fun activateDesk(wct: WindowContainerTransaction, deskId: Int)

+61 −11
Original line number Diff line number Diff line
@@ -73,26 +73,70 @@ class RootTaskDesksOrganizer(
        }
    }

    override fun warmUpDefaultDesk(displayId: Int, userId: Int) {
        logV("warmUpDefaultDesk in displayId=%d userId=%d", displayId, userId)
        // Check if a desk in this display is already created.
        deskRootsByDeskId.forEach { deskId, root ->
            if (root.taskInfo.displayId == displayId && deskId !in removeDeskRootRequests) {
                // A desk already exists.
                return
            }
        }
        val requestInProgress =
            createDeskRootRequests.any { request -> request.displayId == displayId }
        if (requestInProgress) {
            // There isn't one ready yet, but a request for one is already in progress.
            return
        }
        // Request a new one, but do not associate to the user.
        createDeskRoot(displayId, userId = null) { deskId ->
            logV("warmUpDefaultDesk created new desk root: %d", deskId)
        }
    }

    override fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback) {
        logV("createDesk in displayId=%d userId=%s", displayId, userId)
        // Find an existing desk that is not yet used by this user.
        val unassignedDesk =
            deskRootsByDeskId
                .valueIterator()
                .asSequence()
                .filterNot { desk -> userId in desk.users }
                .filterNot { desk -> desk.deskId in removeDeskRootRequests }
                .filter { desk -> desk.taskInfo.displayId == displayId }
                .firstOrNull()
        val unassignedDesk = firstUnassignedDesk(displayId, userId)
        if (unassignedDesk != null) {
            unassignedDesk.users.add(userId)
            callback.onCreated(unassignedDesk.deskId)
            return
        }
        // When there is an in-progress request without a user (as would be the case for a warm up
        // request), use that for this create request instead of creating another root.
        val unassignedRequest = createDeskRootRequests.firstOrNull { it.userId == null }
        if (unassignedRequest != null) {
            createDeskRootRequests.remove(unassignedRequest)
            createDeskRootRequests += unassignedRequest.copy(userId = userId)
            return
        }
        // Must request a new root.
        createDeskRoot(displayId, userId, callback)
    }

    private fun createDeskRoot(displayId: Int, userId: Int, callback: OnCreateCallback) {
    @Deprecated("Use createDesk() instead.", replaceWith = ReplaceWith("createDesk()"))
    override fun createDeskImmediate(displayId: Int, userId: Int): Int? {
        logV("createDeskImmediate in displayId=%d userId=%s", displayId, userId)
        // Find an existing desk that is not yet used by this user.
        val unassignedDesk = firstUnassignedDesk(displayId, userId)
        if (unassignedDesk != null) {
            unassignedDesk.users.add(userId)
            return unassignedDesk.deskId
        }
        return null
    }

    private fun firstUnassignedDesk(displayId: Int, userId: Int): DeskRoot? {
        return deskRootsByDeskId
            .valueIterator()
            .asSequence()
            .filterNot { desk -> userId in desk.users }
            .filterNot { desk -> desk.deskId in removeDeskRootRequests }
            .firstOrNull { desk -> desk.taskInfo.displayId == displayId }
    }

    private fun createDeskRoot(displayId: Int, userId: Int?, callback: OnCreateCallback) {
        logV("createDeskRoot in display: %d for user: %d", displayId, userId)
        createDeskRootRequests += CreateDeskRequest(displayId, userId, callback)
        shellTaskOrganizer.createRootTask(
@@ -334,7 +378,12 @@ class RootTaskDesksOrganizer(
                    deskId = deskId,
                    taskInfo = taskInfo,
                    leash = leash,
                    users = mutableSetOf(deskRequest.userId),
                    users =
                        if (deskRequest.userId != null) {
                            mutableSetOf(deskRequest.userId)
                        } else {
                            mutableSetOf()
                        },
                )
            createDeskRootRequests.remove(deskRequest)
            deskRequest.onCreateCallback.onCreated(deskId)
@@ -512,7 +561,7 @@ class RootTaskDesksOrganizer(

    private data class CreateDeskRequest(
        val displayId: Int,
        val userId: Int,
        val userId: Int?,
        val onCreateCallback: OnCreateCallback,
    )

@@ -542,6 +591,7 @@ class RootTaskDesksOrganizer(
        )
        pw.println("${innerPrefix}createDeskRootRequests=$createDeskRootRequests")
        pw.println("${innerPrefix}removeDeskRootRequests=$removeDeskRootRequests")
        pw.println("${innerPrefix}numOfDeskRoots=${deskRootsByDeskId.size()}")
        pw.println("${innerPrefix}Desk Roots:")
        deskRootsByDeskId.forEach { deskId, root ->
            val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
Loading