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

Commit 1664fbd9 authored by Matt Sziklay's avatar Matt Sziklay Committed by Android (Google) Code Review
Browse files

Merge "Add preserved displays to persistent repo." into main

parents 6824be40 2919442a
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.desktopmode.data

/** A display that supports desktops. */
class DesktopDisplay(val displayId: Int) {
    // The set implementation must preserve order.
    val orderedDesks: MutableSet<Desk> = mutableSetOf()
    var activeDeskId: Int? = null
}
+1 −7
Original line number Diff line number Diff line
@@ -54,13 +54,6 @@ class DesktopRepository(
    val userId: Int,
    val desktopConfig: DesktopConfig,
) {
    /** A display that supports desktops. */
    private class DesktopDisplay(val displayId: Int) {
        // The set implementation must preserve order.
        val orderedDesks: MutableSet<Desk> = mutableSetOf()
        var activeDeskId: Int? = null
    }

    /** Tiling data preserved after a display got disconnected. */
    data class PreservedTiledAppData(val leftTiledTask: Int?, val rightTiledTask: Int?)

@@ -1267,6 +1260,7 @@ class DesktopRepository(
                            userId,
                            desks,
                            getActiveDeskId(displayId),
                            preservedDisplaysByUniqueId,
                        )
                    } catch (exception: Exception) {
                        logE(
+186 −148
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode.data

import android.content.Context
import android.graphics.Rect
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.INVALID_DISPLAY
import android.window.DesktopExperienceFlags
@@ -30,6 +31,7 @@ import com.android.wm.shell.desktopmode.data.persistence.DesktopPersistentReposi
import com.android.wm.shell.desktopmode.data.persistence.DesktopRepositoryState
import com.android.wm.shell.desktopmode.data.persistence.DesktopTaskState
import com.android.wm.shell.desktopmode.data.persistence.DesktopTaskTilingState
import com.android.wm.shell.desktopmode.data.persistence.Rect as RectProto
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopConfig
@@ -81,28 +83,64 @@ class DesktopRepositoryInitializerImpl(
                    val desktopRepositoryState =
                        persistentRepository.getDesktopRepositoryState(userId) ?: continue
                    val desksToRestore = getDesksToRestore(desktopRepositoryState, userId)
                    val preservedDesksToRestore = getPreservedDesksToRestore(desktopRepositoryState)
                    logV(
                        "initialize() will restore desks=%s user=%d",
                        "initialize() will restore desks=%s preservedDesks=%s user=%d",
                        desksToRestore.map { it.desktopId },
                        preservedDesksToRestore.map { it.desktopId },
                        userId,
                    )
                    for (persistentDesktop in desksToRestore) {
                        restoreDesktop(
                            desktopRepositoryState,
                            persistentDesktop,
                            uniqueIdToDisplayIdMap,
                            desktopSupportedOnDefaultDisplay,
                            userId,
                            repository,
                            wasPreservedDisplay = false,
                        )
                    }
                    for (preservedDesktop in preservedDesksToRestore) {
                        restoreDesktop(
                            desktopRepositoryState,
                            preservedDesktop,
                            uniqueIdToDisplayIdMap,
                            desktopSupportedOnDefaultDisplay,
                            userId,
                            repository,
                            wasPreservedDisplay = true,
                        )
                    }
                }
            } finally {
                _isInitialized.value = true
            }
        }
    }

    /** TODO: b/444034767 - Consider splitting this method into pieces. */
    private suspend fun restoreDesktop(
        desktopRepositoryState: DesktopRepositoryState,
        persistentDesktop: Desktop,
        uniqueIdToDisplayIdMap: MutableMap<String, Int>?,
        desktopSupportedOnDefaultDisplay: Boolean,
        userId: Int,
        repository: DesktopRepository,
        wasPreservedDisplay: Boolean,
    ) {
        val maxTasks = getTaskLimit(persistentDesktop)
        var uniqueDisplayId = persistentDesktop.uniqueDisplayId
        // TODO: b/441767264 - Consider waiting for DisplayController to receive all
        //  displayAdded signals before initializing here. This way we don't
        //  rely on an IPC if the display is not yet available.
        val displayIdIfNotFound =
                            if (
                                DesktopExperienceFlags.ENABLE_EXTERNAL_DISPLAY_PERSISTENCE_BUGFIX
                                    .isTrue
                            ) {
            if (DesktopExperienceFlags.ENABLE_EXTERNAL_DISPLAY_PERSISTENCE_BUGFIX.isTrue) {
                displayController.getDisplayIdByUniqueIdBlocking(uniqueDisplayId)
            } else {
                DEFAULT_DISPLAY
            }
                        var newDisplayId =
                            uniqueIdToDisplayIdMap?.get(uniqueDisplayId) ?: displayIdIfNotFound
        var newDisplayId = uniqueIdToDisplayIdMap?.get(uniqueDisplayId) ?: displayIdIfNotFound
        val deskId = persistentDesktop.desktopId
        var transientDesk = false
        var preserveDesk = false
@@ -110,7 +148,7 @@ class DesktopRepositoryInitializerImpl(
            // If a desk is somehow going to the default display on an
            // unsupported device, skip it.
            logV("desk=%d is going to the default display, skipping", deskId)
                            continue
            return
        }
        if (newDisplayId == INVALID_DISPLAY) {
            val result =
@@ -119,6 +157,7 @@ class DesktopRepositoryInitializerImpl(
                    uniqueDisplayId,
                    uniqueIdToDisplayIdMap,
                    desktopSupportedOnDefaultDisplay,
                    wasPreservedDisplay,
                )
            newDisplayId = result.newDisplayId
            uniqueDisplayId = result.newUniqueDisplayId
@@ -148,13 +187,12 @@ class DesktopRepositoryInitializerImpl(
        }
        if (newDeskId == null) {
            logW(
                                "Could not re-create desk=%d from uniqueDisplayId=%d " +
                                    "in displayId=%d",
                "Could not re-create desk=%d from uniqueDisplayId=%d " + "in displayId=%d",
                deskId,
                uniqueDisplayId,
                newDisplayId,
            )
                            continue
            return
        }

        // TODO: b/393961770 - [DesktopRepository] doesn't save desks to the
@@ -184,7 +222,7 @@ class DesktopRepositoryInitializerImpl(
                    deskId = newDeskId,
                    taskId = task.taskId,
                    isVisible = false,
                                    taskBounds = null,
                    taskBounds = task.taskBounds.toRect(),
                )

                if (isVisible) {
@@ -197,22 +235,15 @@ class DesktopRepositoryInitializerImpl(
                    )
                }

                                val tilingEnabled =
                                    DesktopExperienceFlags.ENABLE_TILE_RESIZING.isTrue()
                                if (
                                    tilingEnabled &&
                                        task.desktopTaskTilingState == DesktopTaskTilingState.LEFT
                                ) {
                val tilingEnabled = DesktopExperienceFlags.ENABLE_TILE_RESIZING.isTrue()
                if (tilingEnabled && task.desktopTaskTilingState == DesktopTaskTilingState.LEFT) {
                    repository.addLeftTiledTaskToDesk(
                        persistentDesktop.displayId,
                        task.taskId,
                        newDeskId,
                    )
                }
                                if (
                                    tilingEnabled &&
                                        task.desktopTaskTilingState == DesktopTaskTilingState.RIGHT
                                ) {
                if (tilingEnabled && task.desktopTaskTilingState == DesktopTaskTilingState.RIGHT) {
                    repository.addRightTiledTaskToDesk(
                        persistentDesktop.displayId,
                        task.taskId,
@@ -232,12 +263,6 @@ class DesktopRepositoryInitializerImpl(
            repository.preserveDesk(newDeskId, persistentDesktop.uniqueDisplayId)
        }
    }
                }
            } finally {
                _isInitialized.value = true
            }
        }
    }

    private suspend fun getDesksToRestore(
        state: DesktopRepositoryState,
@@ -256,13 +281,23 @@ class DesktopRepositoryInitializerImpl(
                    (!limitToSingleDeskPerDisplay || isValidSingleDesk)
                }
            }
            .toSet()
            .toMutableSet()
    }

    private suspend fun getPreservedDesksToRestore(state: DesktopRepositoryState): Set<Desktop> {
        // Also get all preserved desks. If we end up not finding their displays, we'll
        // preserve them again. This allows us to restore a display during boot.
        val desktopSet = mutableSetOf<Desktop>()
        state.preservedDisplayByUniqueIdMap.values.forEach { preservedDisplay ->
            desktopSet.addAll(preservedDisplay.getPreservedDesktop().values)
        }
        return desktopSet
    }

    /**
     * Handles the case where the initial display ID is invalid. Redirects the desk to
     * DEFAULT_DISPLAY, marking it to be preserved and marking as transient if desktops are not
     * supported on the default display.
     * supported on the default display or if it was originally a preserved desk.
     *
     * @param deskId The ID of the desk being processed.
     * @param currentUniqueDisplayId The current unique display ID.
@@ -274,17 +309,18 @@ class DesktopRepositoryInitializerImpl(
        currentUniqueDisplayId: String?,
        uniqueIdToDisplayIdMap: Map<String, Int>?,
        desktopSupportedOnDefaultDisplay: Boolean,
        wasPreservedDisplay: Boolean,
    ): DisplayRedirectResult {
        logV(
            "desk=%d: displayId for uniqueDisplayId=%s not found, redirecting to default display",
            "desk=%d: displayId for uniqueDisplayId=%s not found; handling",
            deskId,
            currentUniqueDisplayId,
        )
        val newDisplayId = DEFAULT_DISPLAY
        val newUniqueDisplayId =
            uniqueIdToDisplayIdMap?.entries?.firstOrNull { it.value == DEFAULT_DISPLAY }?.key
        val transientDesk = !desktopSupportedOnDefaultDisplay
        // If a desk is being redirected to the default display but desks
        val transientDesk = !desktopSupportedOnDefaultDisplay || wasPreservedDisplay
        // If a desk was preserved or is being redirected to the default display when desks
        // aren't supported there, mark it as transient; we will still create
        // it to mimic the desk as faithfully as possible, but it will
        // only be used for preservation for a future restore. It will be
@@ -301,6 +337,8 @@ class DesktopRepositoryInitializerImpl(
        )
    }

    private fun RectProto.toRect() = Rect(left, top, right, bottom)

    private fun getTaskLimit(persistedDesk: Desktop): Int =
        desktopConfig.maxTaskLimit.takeIf { it > 0 } ?: persistedDesk.zOrderedTasksCount

+65 −8
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.wm.shell.desktopmode.data.persistence

import android.content.Context
import android.graphics.Rect
import android.util.ArrayMap
import android.util.ArraySet
import android.util.Log
import android.view.Display.DEFAULT_DISPLAY
@@ -29,6 +31,8 @@ import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStoreFile
import com.android.framework.protobuf.InvalidProtocolBufferException
import com.android.wm.shell.desktopmode.data.Desk
import com.android.wm.shell.desktopmode.data.DesktopDisplay
import com.android.wm.shell.desktopmode.data.persistence.Rect as RectProto
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import java.io.IOException
import java.io.InputStream
@@ -109,7 +113,12 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
            null
        }

    suspend fun addOrUpdateRepository(userId: Int, desks: List<Desk>, activeDeskId: Int?) {
    suspend fun addOrUpdateRepository(
        userId: Int,
        desks: List<Desk>,
        activeDeskId: Int?,
        preservedDisplays: ArrayMap<String, DesktopDisplay>,
    ) {
        try {
            dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
                val currentRepository =
@@ -132,6 +141,7 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
                                    desk.freeformTasksInZOrder,
                                    desk.leftTiledTaskId,
                                    desk.rightTiledTaskId,
                                    desk.boundsByTaskId,
                                )
                                .updateZOrder(desk.freeformTasksInZOrder)
                                .updateUniqueDisplayId(desk.uniqueDisplayId)
@@ -151,14 +161,15 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
                        }
                    }
                }
                if (
                    DesktopExperienceFlags.ENABLE_EXTERNAL_DISPLAY_PERSISTENCE_BUGFIX.isTrue &&
                        activeDeskId == null
                ) {
                if (DesktopExperienceFlags.ENABLE_EXTERNAL_DISPLAY_PERSISTENCE_BUGFIX.isTrue) {
                    if (activeDeskId == null) {
                        desks.first().uniqueDisplayId.let {
                            currentUserRepoBuilder.removeActiveDeskByUniqueDisplayId(it)
                        }
                    }
                    addOrUpdatePreservedDisplays(currentUserRepoBuilder, preservedDisplays)
                }

                persistentRepositories
                    .toBuilder()
                    .putDesktopRepoByUser(userId, currentUserRepoBuilder.build())
@@ -174,6 +185,41 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
        }
    }

    private fun addOrUpdatePreservedDisplays(
        currentUserRepoBuilder: DesktopRepositoryState.Builder,
        preservedDisplaysByUniqueId: ArrayMap<String, DesktopDisplay>,
    ) {
        currentUserRepoBuilder.clearPreservedDisplayByUniqueId()
        preservedDisplaysByUniqueId.forEach { preservedDisplay ->
            val updatedPreservedDisplayBuilder = PreservedDisplay.newBuilder()
            preservedDisplay.value.activeDeskId?.let {
                updatedPreservedDisplayBuilder.setActiveDeskId(it)
            }
            preservedDisplay.value.orderedDesks.forEach { desk ->
                val updatedDesktop =
                    Desktop.newBuilder()
                        .setDesktopId(desk.deskId)
                        .setDisplayId(desk.displayId)
                        .updateTaskStates(
                            desk.visibleTasks,
                            desk.minimizedTasks,
                            desk.freeformTasksInZOrder,
                            desk.leftTiledTaskId,
                            desk.rightTiledTaskId,
                            desk.boundsByTaskId,
                        )
                        .updateZOrder(desk.freeformTasksInZOrder)
                        .updateUniqueDisplayId(desk.uniqueDisplayId)
                        .build()
                updatedPreservedDisplayBuilder.putPreservedDesktop(desk.deskId, updatedDesktop)
            }
            currentUserRepoBuilder.putPreservedDisplayByUniqueId(
                preservedDisplay.key,
                updatedPreservedDisplayBuilder.build(),
            )
        }
    }

    /** Adds or updates a desktop stored in the datastore */
    @Deprecated("Use addOrUpdateRepository() instead.", ReplaceWith("addOrUpdateRepository()"))
    suspend fun addOrUpdateDesktop(
@@ -323,6 +369,7 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
            freeformTasksInZOrder: ArrayList<Int>,
            leftTiledTask: Int?,
            rightTiledTask: Int?,
            boundsByTaskId: MutableMap<Int, Rect> = mutableMapOf(),
        ): Desktop.Builder {
            clearTasksByTaskId()

@@ -343,12 +390,17 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
                        it,
                        state = DesktopTaskState.VISIBLE,
                        getTilingStateForTask(it, leftTiledTask, rightTiledTask),
                        bounds = boundsByTaskId[it] ?: Rect(),
                    )
                }
            )
            putAllTasksByTaskId(
                minimizedTasks.associateWith {
                    createDesktopTask(it, state = DesktopTaskState.MINIMIZED)
                    createDesktopTask(
                        it,
                        state = DesktopTaskState.MINIMIZED,
                        bounds = boundsByTaskId[it] ?: Rect(),
                    )
                }
            )
            return this
@@ -386,11 +438,16 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
            taskId: Int,
            state: DesktopTaskState = DesktopTaskState.VISIBLE,
            tilingState: DesktopTaskTilingState = DesktopTaskTilingState.NONE,
            bounds: Rect,
        ): DesktopTask =
            DesktopTask.newBuilder()
                .setTaskId(taskId)
                .setDesktopTaskState(state)
                .setDesktopTaskTilingState(tilingState)
                .setTaskBounds(bounds.toRectProto())
                .build()

        private fun Rect.toRectProto() =
            RectProto.newBuilder().setLeft(left).setTop(top).setRight(right).setBottom(bottom)
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -15,10 +15,20 @@ enum DesktopTaskTilingState {
  RIGHT = 3;
}

message Rect {
  optional int32 left = 1;
  optional int32 top = 2;
  optional int32 right = 3;
  optional int32 bottom = 4;
}

message DesktopTask {
  optional int32 task_id = 1;
  optional DesktopTaskState desktop_task_state= 2;
  optional DesktopTaskTilingState desktop_task_tiling_state = 3;
  // Tracks task bounds; used primarily for display restore functionality as
  // core will restore task bounds to the task itself on boot.
  optional Rect task_bounds = 4;
}

message Desktop {
@@ -30,11 +40,19 @@ message Desktop {
  optional string unique_display_id = 5;
}

message PreservedDisplay {
  // Stores a mapping between a preserved display and its desktops. The key is the desktop id.
  map<int32, Desktop> preserved_desktop = 1;
  optional int32 active_desk_id = 2;
}

message DesktopRepositoryState {
  // Stores a mapping between a repository and the desktops in it. The key is the desktop id.
  map<int32, Desktop> desktop = 1;
  // Stores a mapping of the active desk id associated with each display.
  map<string, int32> active_desk_by_unique_display_id = 2;
  // Stores a mapping of preserved displays by their unique display id.
  map<string, PreservedDisplay> preserved_display_by_unique_id = 3;
}

message DesktopPersistentRepositories {
Loading