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
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bded98c..8523b71 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3426,26 +3426,30 @@
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 ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
- if (resumedActivity == null || !resumedActivity.idle) {
- ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
- + "not idle", rootTask.getRootTaskId(), resumedActivity);
+ 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;
+ }
+ 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;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d29505f..538cffe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -204,6 +204,28 @@
}
@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();
final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 49e349c..56fca31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1550,10 +1550,6 @@
// 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());