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

Commit 1fb0593b authored by Orhan Uysal's avatar Orhan Uysal
Browse files

Take snapshots of desktop tasks on disconnect.

When a display disconnects and before the transition is ready, take
snapshots of the tasks in the desk.

Also prevent taking snapshots if there is a bounds change in a
vis to invis transition as the app won't redraw.

Bug: 438439090
Test: Manual testing of snapshots && atest DesktopTasksControllerTest
Flag: com.android.window.flags.enable_display_reconnect_interaction
Change-Id: Iba09c5d419670e6f84653c802ed0700b1d6c37cb
parent 61cd26fb
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshotManager;

import androidx.annotation.OptIn;

@@ -968,7 +969,8 @@ public abstract class WMShellModule {
            DesktopState desktopState,
            DesktopConfig desktopConfig,
            VisualIndicatorUpdateScheduler visualIndicatorUpdateScheduler,
            Optional<DesktopFirstListenerManager> desktopFirstListenerManager) {
            Optional<DesktopFirstListenerManager> desktopFirstListenerManager,
            TaskSnapshotManager taskSnapshotManager) {
        return new DesktopTasksController(
                context,
                shellInit,
@@ -1017,7 +1019,8 @@ public abstract class WMShellModule {
                desktopState,
                desktopConfig,
                visualIndicatorUpdateScheduler,
                desktopFirstListenerManager);
                desktopFirstListenerManager,
                taskSnapshotManager);
    }

    @WMSingleton
+18 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.ActivityManager
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.ActivityTaskManager
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.AppOpsManager
import android.app.KeyguardManager
@@ -44,6 +45,7 @@ import android.os.Binder
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
import android.os.RemoteException
import android.os.Trace
import android.os.UserHandle
import android.os.UserManager
@@ -78,6 +80,7 @@ import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVI
import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
import android.window.RemoteTransition
import android.window.SplashScreen.SPLASH_SCREEN_STYLE_ICON
import android.window.TaskSnapshotManager
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
@@ -255,6 +258,7 @@ class DesktopTasksController(
    private val desktopConfig: DesktopConfig,
    private val visualIndicatorUpdateScheduler: VisualIndicatorUpdateScheduler,
    private val desktopFirstListenerManager: Optional<DesktopFirstListenerManager>,
    private val taskSnapshotManager: TaskSnapshotManager,
) :
    RemoteCallable<DesktopTasksController>,
    Transitions.TransitionHandler,
@@ -792,6 +796,20 @@ 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")
                if (Flags.reduceTaskSnapshotMemoryUsage()) {
                    taskSnapshotManager.takeTaskSnapshot(it, true)
                } else {
                    ActivityTaskManager.getService().getTaskSnapshot(it, false)
                }
            }
        } catch (e: RemoteException) {
            logE("addOnDisplayDisconnect: failed to take task snapshot", e)
        }

        return wct
    }

+8 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import android.widget.Toast
import android.window.DisplayAreaInfo
import android.window.IWindowContainerToken
import android.window.RemoteTransition
import android.window.TaskSnapshotManager
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
@@ -301,6 +302,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    @Mock private lateinit var mockAppOpsManager: AppOpsManager
    @Mock private lateinit var visualIndicatorUpdateScheduler: VisualIndicatorUpdateScheduler
    @Mock private lateinit var desktopFirstListenerManager: DesktopFirstListenerManager
    @Mock private lateinit var taskSnapshotManager: TaskSnapshotManager
    private lateinit var controller: DesktopTasksController
    private lateinit var shellInit: ShellInit
@@ -535,6 +537,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
            desktopConfig,
            visualIndicatorUpdateScheduler,
            Optional.of(desktopFirstListenerManager),
            taskSnapshotManager,
        )
    @After
@@ -11212,6 +11215,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                        this.displayId == SECOND_DISPLAY
                }
            )
        verify(taskSnapshotManager).takeTaskSnapshot(secondDisplayTask.taskId, true)
    }
    @Test
@@ -11255,6 +11259,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                        this.deskId == DISCONNECTED_DESK_ID
                }
            )
        verify(taskSnapshotManager).takeTaskSnapshot(secondDisplayTask.taskId, true)
    }
    @Test
@@ -11296,6 +11301,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                        this.deskId == DISCONNECTED_DESK_ID
                }
            )
        verify(taskSnapshotManager).takeTaskSnapshot(secondDisplayTask.taskId, true)
    }
    @Test
@@ -11327,6 +11333,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                        this.deskId == DISCONNECTED_DESK_ID
                }
            )
        verify(taskSnapshotManager, never()).takeTaskSnapshot(any(), any())
    }
    @Test
@@ -11361,6 +11368,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
                secondDisplayTask = secondDisplayTask,
            )
        assertThat(findBoundsChange(wct, secondDisplayTask)).isEqualTo(Rect(33, 61, 299, 327))
        verify(taskSnapshotManager).takeTaskSnapshot(secondDisplayTask.taskId, true)
    }
    private fun performDisplayDisconnectTransition(
+5 −1
Original line number Diff line number Diff line
@@ -124,8 +124,12 @@ class SnapshotController {
            // Note that if this task is being transiently hidden, the snapshot will be captured at
            // the end of the transient transition (see Transition#finishTransition()), because IME
            // won't move be moved during the transition and the tasks are still live.
            // Also don't take the snapshot if there is a bounds change in a visible to invisible
            // transition as the app won't redraw. This can happen when a task is moving from one
            // display to another.
            if (task != null && !task.mCreatedByOrganizer && !task.isVisibleRequested()
                    && !task.mTransitionController.isTransientHide(task)) {
                    && !task.mTransitionController.isTransientHide(task)
                    && task.getBounds().equals(info.mAbsoluteBounds)) {
                mTaskSnapshotController.recordSnapshot(task, info);
            }
            // Won't need to capture activity snapshot in close transition.