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

Commit 30d057ed authored by Devarshi Bhatt's avatar Devarshi Bhatt
Browse files

Add instrumentation for window minimize with app open CUJ.

Desktop windowing currently allows 4 active windows. Once a 5th window
is opened, the least recently used window is minimized.
See go/windowing-jank-coverage-one-pager for more details.

Bug: 339583422
Test: perfetto trace(https://screenshot.googleplex.com/BnXUCP6uaY9rGkV)
Flag: NONE new perfetto instrumentation
Change-Id: Id014e326b187d4de0d478e8adef6c30387a57f26
parent 604556c6
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -566,7 +566,8 @@ public abstract class WMShellModule {
            Context context,
            Transitions transitions,
            @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
            ShellTaskOrganizer shellTaskOrganizer) {
            ShellTaskOrganizer shellTaskOrganizer,
            InteractionJankMonitor interactionJankMonitor) {
        int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
        if (!DesktopModeStatus.canEnterDesktopMode(context)
                || !DESKTOP_WINDOWING_MODE.isEnabled(context)
@@ -575,7 +576,13 @@ public abstract class WMShellModule {
        }
        return Optional.of(
                new DesktopTasksLimiter(
                        transitions, desktopModeTaskRepository, shellTaskOrganizer, maxTaskLimit));
                        transitions,
                        desktopModeTaskRepository,
                        shellTaskOrganizer,
                        maxTaskLimit,
                        interactionJankMonitor,
                        context)
        );
    }


+35 −3
Original line number Diff line number Diff line
@@ -17,12 +17,15 @@
package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
@@ -40,6 +43,8 @@ class DesktopTasksLimiter (
        private val taskRepository: DesktopModeTaskRepository,
        private val shellTaskOrganizer: ShellTaskOrganizer,
        private val maxTasksLimit: Int,
        private val interactionJankMonitor: InteractionJankMonitor,
        private val context: Context
) {
    private val minimizeTransitionObserver = MinimizeTransitionObserver()
    @VisibleForTesting
@@ -54,11 +59,16 @@ class DesktopTasksLimiter (
        taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover)
    }

    private data class TaskDetails (val displayId: Int, val taskId: Int)
    private data class TaskDetails(
        val displayId: Int,
        val taskId: Int,
        var transitionInfo: TransitionInfo?
    )

    // TODO(b/333018485): replace this observer when implementing the minimize-animation
    private inner class MinimizeTransitionObserver : TransitionObserver {
        private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
        private val mActiveTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()

        fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
            mPendingTransitionTokensAndTasks[transition] = taskDetails
@@ -71,6 +81,8 @@ class DesktopTasksLimiter (
            finishTransaction: SurfaceControl.Transaction
        ) {
            val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
            taskToMinimize.transitionInfo = info
            mActiveTransitionTokensAndTasks[transition] = taskToMinimize

            if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return

@@ -81,6 +93,7 @@ class DesktopTasksLimiter (
                        taskToMinimize.taskId)
                return
            }

            this@DesktopTasksLimiter.markTaskMinimized(
                    taskToMinimize.displayId, taskToMinimize.taskId)
        }
@@ -104,9 +117,21 @@ class DesktopTasksLimiter (
            return taskChange.mode == TRANSIT_TO_BACK
        }

        override fun onTransitionStarting(transition: IBinder) {}
        override fun onTransitionStarting(transition: IBinder) {
            val mActiveTaskDetails = mActiveTransitionTokensAndTasks[transition]
            if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
                // Begin minimize window CUJ instrumentation.
                interactionJankMonitor.begin(
                    mActiveTaskDetails.transitionInfo?.rootLeash, context,
                    CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
                )
            }
        }

        override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
            if (mActiveTransitionTokensAndTasks.remove(merged) != null) {
                interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
            }
            mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
                mPendingTransitionTokensAndTasks[playing] = taskToTransfer
            }
@@ -116,6 +141,13 @@ class DesktopTasksLimiter (
            ProtoLog.v(
                    ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                    "DesktopTasksLimiter: transition %s finished", transition)
            if (mActiveTransitionTokensAndTasks.remove(transition) != null) {
                if (aborted) {
                    interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
                } else {
                    interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
                }
            }
            mPendingTransitionTokensAndTasks.remove(transition)
        }
    }
@@ -193,7 +225,7 @@ class DesktopTasksLimiter (
     */
    fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
        minimizeTransitionObserver.addPendingTransitionToken(
                transition, TaskDetails(displayId, taskId))
                transition, TaskDetails(displayId, taskId, transitionInfo = null))
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -211,7 +211,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
            desktopModeTaskRepository,
            shellTaskOrganizer,
            MAX_TASK_LIMIT,
        )
            mockInteractionJankMonitor,
            mContext)

    whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
    whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+98 −4
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -47,6 +49,8 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.`when`
import org.mockito.kotlin.eq
import org.mockito.kotlin.verify
import org.mockito.quality.Strictness


@@ -65,6 +69,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {

    @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
    @Mock lateinit var transitions: Transitions
    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor

    private lateinit var mockitoSession: StaticMockitoSession
    private lateinit var desktopTasksLimiter: DesktopTasksLimiter
@@ -79,7 +84,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
        desktopTaskRepo = DesktopModeTaskRepository()

        desktopTasksLimiter =
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT)
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
                interactionJankMonitor, mContext)
    }

    @After
@@ -90,14 +96,16 @@ class DesktopTasksLimiterTest : ShellTestCase() {
    @Test
    fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
        assertFailsWith<IllegalArgumentException> {
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, 0)
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, 0,
                interactionJankMonitor, mContext)
        }
    }

    @Test
    fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
        assertFailsWith<IllegalArgumentException> {
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, -5)
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, -5,
                interactionJankMonitor, mContext)
        }
    }

@@ -325,7 +333,8 @@ class DesktopTasksLimiterTest : ShellTestCase() {
    @Test
    fun getTaskToMinimizeIfNeeded_tasksAboveLimit_otherLimit_returnsBackTask() {
        desktopTasksLimiter =
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2)
            DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2,
                interactionJankMonitor, mContext)
        val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }

        val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded(
@@ -347,6 +356,91 @@ class DesktopTasksLimiterTest : ShellTestCase() {
        assertThat(minimizedTask).isEqualTo(tasks.last())
    }

    @Test
    fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
        val transition = Binder()
        val task = setUpFreeformTask()
        desktopTasksLimiter.addPendingMinimizeChange(
            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)

        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
            transition,
            TransitionInfoBuilder(TRANSIT_OPEN).build(),
            StubTransaction() /* startTransaction */,
            StubTransaction() /* finishTransaction */)

        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)

        verify(interactionJankMonitor).begin(
            any(),
            eq(mContext),
            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))

        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
            transition,
            /* aborted = */ false)

        verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
    }

    @Test
    fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
        val transition = Binder()
        val task = setUpFreeformTask()
        desktopTasksLimiter.addPendingMinimizeChange(
            transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)

        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
            transition,
            TransitionInfoBuilder(TRANSIT_OPEN).build(),
            StubTransaction() /* startTransaction */,
            StubTransaction() /* finishTransaction */)

        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)

        verify(interactionJankMonitor).begin(
            any(),
            eq(mContext),
            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))

        desktopTasksLimiter.getTransitionObserver().onTransitionFinished(
            transition,
            /* aborted = */ true)

        verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
    }

    @Test
    fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
        val mergedTransition = Binder()
        val newTransition = Binder()
        val task = setUpFreeformTask()
        desktopTasksLimiter.addPendingMinimizeChange(
            mergedTransition, displayId = DEFAULT_DISPLAY, taskId = task.taskId)

        desktopTasksLimiter.getTransitionObserver().onTransitionReady(
            mergedTransition,
            TransitionInfoBuilder(TRANSIT_OPEN).build(),
            StubTransaction() /* startTransaction */,
            StubTransaction() /* finishTransaction */)

        desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)

        verify(interactionJankMonitor).begin(
            any(),
            eq(mContext),
            eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))

        desktopTasksLimiter.getTransitionObserver().onTransitionMerged(
            mergedTransition,
            newTransition)

        verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
    }

    private fun setUpFreeformTask(
            displayId: Int = DEFAULT_DISPLAY,
    ): RunningTaskInfo {