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

Commit cd7d9512 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Check idle activity from all visible task fragment

The very old legacy logic only handles fullscreen windowing mode case.
If multiple tasks are launching at the same time, the power mode may
be finished too early by the first activity reports idle. That results
in a long startup delay.

Also add a case to handle cold launching activity. Before the process
is attached, the activity record state is INITIALIZING. But if it can
be resumed after its process is attached, its idle state should also
be considered. E.g. launching a split-screen pair with hot launch of
one side and cold launch on another side. When the first one reported
idle, allResumedActivitiesIdle should still return false.

Fix: 356522395
Flag: EXEMPT bugfix
Test: atest RootWindowContainerTests#testAllResumedActivitiesIdle
Change-Id: Ida677ec93916f37cf2aebd33113f9730d8c211ab
parent 62253d6f
Loading
Loading
Loading
Loading
+16 −12
Original line number Diff line number Diff line
@@ -3426,26 +3426,30 @@ class RootWindowContainer extends WindowContainer<DisplayContent>

    boolean allResumedActivitiesIdle() {
        for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
            // TODO(b/117135575): Check resumed activities on all visible root tasks.
            final DisplayContent display = getChildAt(displayNdx);
            if (display.isSleeping()) {
                // No resumed activities while display is sleeping.
                continue;
            }

            // If the focused root task is not null or not empty, there should have some activities
            // resuming or resumed. Make sure these activities are idle.
            final Task rootTask = display.getFocusedRootTask();
            if (rootTask == null || !rootTask.hasActivity()) {
                continue;
            final boolean foundNotIdle = display.forAllLeafTaskFragments(tf -> {
                if (!tf.isVisibleRequested()) {
                    return false;
                }
                // Note that only activities that will be resumed can report idle.
                final ActivityRecord r = tf.topRunningActivity();
                if (r != null && !r.idle && (r.isState(RESUMED)
                        // Its process is not attached yet and it may resume later.
                        || (r.app == null && r.isFocusable()))) {
                    ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: %s not idle", r);
                    return true;
                }
            final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
            if (resumedActivity == null || !resumedActivity.idle) {
                ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
                        + "not idle", rootTask.getRootTaskId(), resumedActivity);
                return false;
            });
            if (foundNotIdle) {
                return false;
            }
            if (mTransitionController.isTransientLaunch(resumedActivity)) {
            if (mTransitionController.hasTransientLaunch(display)) {
                // Not idle if the transient transition animation is running.
                return false;
            }
+22 −0
Original line number Diff line number Diff line
@@ -203,6 +203,28 @@ public class RootWindowContainerTests extends WindowTestsBase {
        assertThat(mWm.mRoot.allPausedActivitiesComplete()).isTrue();
    }

    @Test
    public void testAllResumedActivitiesIdle() {
        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final WindowProcessController proc2 = activity2.app;
        activity1.setState(RESUMED, "test");
        activity2.detachFromProcess();
        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();

        activity1.idle = true;
        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();

        activity2.setProcess(proc2);
        activity2.setState(RESUMED, "test");
        activity2.idle = true;
        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isTrue();

        activity1.idle = false;
        activity1.setVisibleRequested(false);
        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isTrue();
    }

    @Test
    public void testTaskLayerRank() {
        final Task rootTask = new TaskBuilder(mSupervisor).build();
+0 −4
Original line number Diff line number Diff line
@@ -1550,10 +1550,6 @@ public class TransitionTests extends WindowTestsBase {

        // An active transient launch overrides idle state to avoid clearing power mode before the
        // transition is finished.
        spyOn(mRootWindowContainer.mTransitionController);
        doAnswer(invocation -> controller.isTransientLaunch(invocation.getArgument(0))).when(
                mRootWindowContainer.mTransitionController).isTransientLaunch(any());
        activity2.getTask().setResumedActivity(activity2, "test");
        activity2.idle = true;
        assertFalse(mRootWindowContainer.allResumedActivitiesIdle());