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

Commit c0add4e1 authored by Jorge Gil's avatar Jorge Gil
Browse files

[39/N] Desks: Restore persisted desks on reboot

Adds support for restoring persisted desks on shell init. Restoring is
done as follows:
1) DisplayController init must run before
   DesktopRepositoryInitializerImpl to have a list of available
   displays ready (already runs first because of dagger dependencies)
2) On init, DesktopRepositoryInitializerImpl selects desks to persist
   based on available displays from (1) and desk id validity for
   single-desk edge cases
3) For each desk, DesktopRepositoryInitializerImpl creates a new desk
   instance (root task), inserts the desktop data with the new deskId
   and deletes the data under the old deskId
4) DesktopRepositoryInitializerImpl maintains an "isInitialized"
   StateFlow<Boolean> and pings it when finished
5) DesktopDisplayEventHandler collects this StateFlow, and when |true|,
   checks whether an empty desk needs to be created. This prevents
   creating the empty/default desk when persisted desks will be
   restored.

It also starts non-running tasks when activating a desk similar to how
bringDesktopAppsToFront() did it for single-desk setups.

Flag: com.android.window.flags.enable_multiple_desktops_backend
Bug: 393961770
Bug: 391484873
Fix: 393978864
Test: create a couple of desks and add tasks to them, reboot - verify
desks still exist in the repository (with new deskIds)

Change-Id: Idf38fef305088043ea4dbb0a8095181248b6116d
parent 8703cf93
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -757,6 +757,7 @@ public abstract class WMShellModule {
            ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
            DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
            @DynamicOverride DesktopUserRepositories desktopUserRepositories,
            DesktopRepositoryInitializer desktopRepositoryInitializer,
            Optional<DesktopImmersiveController> desktopImmersiveController,
            DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
            LaunchAdjacentController launchAdjacentController,
@@ -802,6 +803,7 @@ public abstract class WMShellModule {
                dragToDesktopTransitionHandler,
                desktopImmersiveController.get(),
                desktopUserRepositories,
                desktopRepositoryInitializer,
                recentsTransitionHandler,
                multiInstanceHelper,
                mainExecutor,
@@ -1309,10 +1311,12 @@ public abstract class WMShellModule {
    static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
            Context context,
            ShellInit shellInit,
            @ShellMainThread CoroutineScope mainScope,
            DisplayController displayController,
            Optional<DesktopUserRepositories> desktopUserRepositories,
            Optional<DesktopTasksController> desktopTasksController,
            Optional<DesktopDisplayModeController> desktopDisplayModeController
            Optional<DesktopDisplayModeController> desktopDisplayModeController,
            DesktopRepositoryInitializer desktopRepositoryInitializer
    ) {
        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
            return Optional.empty();
@@ -1321,7 +1325,9 @@ public abstract class WMShellModule {
                new DesktopDisplayEventHandler(
                        context,
                        shellInit,
                        mainScope,
                        displayController,
                        desktopRepositoryInitializer,
                        desktopUserRepositories.get(),
                        desktopTasksController.get(),
                        desktopDisplayModeController.get()));
+19 −9
Original line number Diff line number Diff line
@@ -23,15 +23,21 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch

/** Handles display events in desktop mode */
class DesktopDisplayEventHandler(
    private val context: Context,
    shellInit: ShellInit,
    private val mainScope: CoroutineScope,
    private val displayController: DisplayController,
    private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
    private val desktopUserRepositories: DesktopUserRepositories,
    private val desktopTasksController: DesktopTasksController,
    private val desktopDisplayModeController: DesktopDisplayModeController,
@@ -61,15 +67,19 @@ class DesktopDisplayEventHandler(
            logV("Display #$displayId does not support desks")
            return
        }

        mainScope.launch {
            desktopRepositoryInitializer.isInitialized.collect { initialized ->
                if (!initialized) return@collect
                if (desktopRepository.getNumberOfDesks(displayId) == 0) {
                    logV("Creating new desk in new display#$displayId")
        // TODO: b/362720497 - when SystemUI crashes with a freeform task open for any reason, the
        //  task is recreated and received in [FreeformTaskListener] before this display callback
        //  is invoked, which results in the repository trying to add the task to a desk before the
        //  desk has been recreated here, which may result in a crash-loop if the repository is
        //  checking that the desk exists before adding a task to it. See b/391984373.
                    // TODO: b/393978539 - consider activating the desk on creation when
                    //  applicable, such as for connected displays.
                    desktopTasksController.createDesk(displayId)
        // TODO: b/393978539 - consider activating the desk on creation when applicable, such as
        //  for connected displays.
                }
                cancel()
            }
        }
    }

    override fun onDisplayRemoved(displayId: Int) {
+25 −1
Original line number Diff line number Diff line
@@ -818,7 +818,6 @@ class DesktopRepository(
    }

    /** Minimizes the task in its desk. */
    @VisibleForTesting
    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)
@@ -933,6 +932,12 @@ class DesktopRepository(
                listener.onDeskRemoved(displayId = desk.displayId, deskId = desk.deskId)
            }
        }
        if (
            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue &&
                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
        ) {
            removeDeskFromPersistentRepository(desk)
        }
        return activeTasks
    }

@@ -1031,6 +1036,24 @@ class DesktopRepository(
        }
    }

    private fun removeDeskFromPersistentRepository(desk: Desk) {
        mainCoroutineScope.launch {
            try {
                logD(
                    "updatePersistentRepositoryForRemovedDesk user=%d desk=%d",
                    userId,
                    desk.deskId,
                )
                persistentRepository.removeDesktop(userId = userId, desktopId = desk.deskId)
            } catch (throwable: Throwable) {
                logE(
                    "An exception occurred while updating the persistent repository \n%s",
                    throwable.stackTrace,
                )
            }
        }
    }

    internal fun dump(pw: PrintWriter, prefix: String) {
        val innerPrefix = "$prefix  "
        pw.println("${prefix}DesktopRepository")
@@ -1049,6 +1072,7 @@ class DesktopRepository(
            }
            .forEach { (displayId, activeDeskId, desks) ->
                pw.println("${prefix}Display #$displayId:")
                pw.println("${innerPrefix}numOfDesks=${desks.size}")
                pw.println("${innerPrefix}activeDesk=$activeDeskId")
                pw.println("${innerPrefix}desks:")
                val desksPrefix = "$innerPrefix  "
+38 −12
Original line number Diff line number Diff line
@@ -112,6 +112,9 @@ import com.android.wm.shell.desktopmode.multidesks.DeskTransition
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.desktopmode.multidesks.createDesk
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -191,6 +194,7 @@ class DesktopTasksController(
    private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
    private val desktopImmersiveController: DesktopImmersiveController,
    private val userRepositories: DesktopUserRepositories,
    desktopRepositoryInitializer: DesktopRepositoryInitializer,
    private val recentsTransitionHandler: RecentsTransitionHandler,
    private val multiInstanceHelper: MultiInstanceHelper,
    @ShellMainThread private val mainExecutor: ShellExecutor,
@@ -268,6 +272,19 @@ class DesktopTasksController(
        }
        userId = ActivityManager.getCurrentUser()
        taskRepository = userRepositories.getProfile(userId)

        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
            desktopRepositoryInitializer.deskRecreationFactory =
                DeskRecreationFactory { deskUserId, destinationDisplayId, deskId ->
                    if (deskUserId != userId) {
                        // TODO: b/400984250 - add multi-user support for multi-desk restoration.
                        logW("Tried to recreated desk of another user.")
                        deskId
                    } else {
                        desksOrganizer.createDesk(destinationDisplayId)
                    }
                }
        }
    }

    private fun onInit() {
@@ -1689,15 +1706,7 @@ class DesktopTasksController(
                    wct.reorder(runningTaskInfo.token, /* onTop= */ true)
                } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
                    // Task is not running, start it
                    wct.startTask(
                        taskId,
                        ActivityOptions.makeBasic()
                            .apply {
                                launchWindowingMode = WINDOWING_MODE_FREEFORM
                                splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
                            }
                            .toBundle(),
                    )
                    wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
                }
            }

@@ -2805,9 +2814,6 @@ class DesktopTasksController(
        }
        prepareForDeskActivation(displayId, wct)
        desksOrganizer.activateDesk(wct, deskId)
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
            // TODO: 362720497 - do non-running tasks need to be restarted with |wct#startTask|?
        }
        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
            doesAnyTaskRequireTaskbarRounding(displayId)
        )
@@ -2825,6 +2831,19 @@ class DesktopTasksController(
                desksOrganizer.minimizeTask(wct, deskId, taskToMinimize)
            }
        }
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
            expandedTasksOrderedFrontToBack
                .filter { taskId -> taskId != taskIdToMinimize }
                .reversed()
                .forEach { taskId ->
                    val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
                    if (runningTaskInfo == null) {
                        wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
                    } else {
                        desksOrganizer.reorderTaskToFront(wct, deskId, runningTaskInfo)
                    }
                }
        }
        return { transition ->
            val activateDeskTransition =
                if (newTaskIdInFront != null) {
@@ -3476,6 +3495,13 @@ class DesktopTasksController(
        }
    }

    private fun createActivityOptionsForStartTask(): ActivityOptions {
        return ActivityOptions.makeBasic().apply {
            launchWindowingMode = WINDOWING_MODE_FREEFORM
            splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
        }
    }

    private fun dump(pw: PrintWriter, prefix: String) {
        val innerPrefix = "$prefix  "
        pw.println("${prefix}DesktopTasksController")
+8 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.wm.shell.desktopmode.multidesks
import android.app.ActivityManager
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
import kotlin.coroutines.suspendCoroutine

/** An organizer of desk containers in which to host child desktop windows. */
interface DesksOrganizer {
@@ -82,3 +84,9 @@ interface DesksOrganizer {
        fun onCreated(deskId: Int)
    }
}

/** Creates a new desk container in the given display. */
suspend fun DesksOrganizer.createDesk(displayId: Int): Int = suspendCoroutine { cont ->
    val onCreateCallback = OnCreateCallback { deskId -> cont.resumeWith(Result.success(deskId)) }
    createDesk(displayId, onCreateCallback)
}
Loading