Merge "Don't start app transition when there is empty TaskFragment" into sc-v2-dev

This commit is contained in:
Chris Li
2021-09-24 03:34:03 +00:00
committed by Android (Google) Code Review
5 changed files with 163 additions and 68 deletions

View File

@@ -553,6 +553,12 @@
"group": "WM_SHOW_TRANSACTIONS", "group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java" "at": "com\/android\/server\/wm\/RootWindowContainer.java"
}, },
"-1501564055": {
"message": "Organized TaskFragment is not ready= %s",
"level": "VERBOSE",
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
"-1499134947": { "-1499134947": {
"message": "Removing starting %s from %s", "message": "Removing starting %s from %s",
"level": "VERBOSE", "level": "VERBOSE",

View File

@@ -165,8 +165,8 @@ public class AppTransitionController {
void handleAppTransitionReady() { void handleAppTransitionReady() {
mTempTransitionReasons.clear(); mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
|| !transitionGoodToGo(mDisplayContent.mChangingContainers, || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
mTempTransitionReasons)) { || !transitionGoodToGoForTaskFragments()) {
return; return;
} }
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -599,6 +599,13 @@ public class AppTransitionController {
} }
} }
@Nullable
static Task findRootTaskFromContainer(WindowContainer wc) {
return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask()
: wc.asActivityRecord().getRootTask();
}
@Nullable
static ActivityRecord getAppFromContainer(WindowContainer wc) { static ActivityRecord getAppFromContainer(WindowContainer wc) {
return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity() return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
: wc.asActivityRecord(); : wc.asActivityRecord();
@@ -972,11 +979,12 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
if (mDisplayContent.mAppTransition.isTimeout()) {
return true;
}
final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent( final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
Display.DEFAULT_DISPLAY).getRotationAnimation(); Display.DEFAULT_DISPLAY).getRotationAnimation();
if (!mDisplayContent.mAppTransition.isTimeout()) {
// Imagine the case where we are changing orientation due to an app transition, but a // Imagine the case where we are changing orientation due to an app transition, but a
// previous orientation change is still in progress. We won't process the orientation // previous orientation change is still in progress. We won't process the orientation
// change for our transition because we need to wait for the rotation animation to // change for our transition because we need to wait for the rotation animation to
@@ -1003,7 +1011,6 @@ public class AppTransitionController {
activity.startingMoved, activity.isRelaunching(), activity.startingMoved, activity.isRelaunching(),
activity.mStartingWindow); activity.mStartingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
return false; return false;
@@ -1031,12 +1038,49 @@ public class AppTransitionController {
} }
// If the wallpaper is visible, we need to check it's ready too. // If the wallpaper is visible, we need to check it's ready too.
boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || return !mWallpaperControllerLocked.isWallpaperVisible()
mWallpaperControllerLocked.wallpaperTransitionReady(); || mWallpaperControllerLocked.wallpaperTransitionReady();
if (wallpaperReady) { }
private boolean transitionGoodToGoForTaskFragments() {
if (mDisplayContent.mAppTransition.isTimeout()) {
return true;
}
// Check all Tasks in this transition. This is needed because new TaskFragment created for
// launching activity may not be in the tracking lists, but we still want to wait for the
// activity launch to start the transition.
final ArraySet<Task> rootTasks = new ArraySet<>();
for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask());
}
for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask());
}
for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
rootTasks.add(
findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i)));
}
// Organized TaskFragment can be empty for two situations:
// 1. New created and is waiting for Activity launch. In this case, we want to wait for
// the Activity launch to trigger the transition.
// 2. Last Activity is just removed. In this case, we want to wait for organizer to
// remove the TaskFragment because it may also want to change other TaskFragments in
// the same transition.
for (int i = rootTasks.size() - 1; i >= 0; i--) {
final Task rootTask = rootTasks.valueAt(i);
final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
if (!taskFragment.isReadyToTransit()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
taskFragment);
return true; return true;
} }
return false; return false;
});
if (notReady) {
return false;
}
} }
return true; return true;
} }

View File

@@ -2347,11 +2347,6 @@ class Task extends TaskFragment {
return getRootTask().mTaskId; return getRootTask().mTaskId;
} }
@Nullable
Task getRootTask() {
return getRootTaskFragment().asTask();
}
/** @return the first organized task. */ /** @return the first organized task. */
@Nullable @Nullable
Task getOrganizedTask() { Task getOrganizedTask() {

View File

@@ -459,6 +459,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment(); return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
} }
@Nullable
Task getRootTask() {
return getRootTaskFragment().asTask();
}
@Override @Override
TaskFragment asTaskFragment() { TaskFragment asTaskFragment() {
return this; return this;
@@ -2153,6 +2158,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return mTaskFragmentOrganizer != null; return mTaskFragmentOrganizer != null;
} }
boolean isReadyToTransit() {
// We don't want to start the transition if the organized TaskFragment is empty, unless
// it is requested to be removed.
return !isOrganizedTaskFragment() || getTopNonFinishingActivity() != null
|| mIsRemovalRequested;
}
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */ /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() { void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);

View File

@@ -31,13 +31,17 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.os.Binder; import android.os.Binder;
@@ -791,4 +795,38 @@ public class AppTransitionControllerTest extends WindowTestsBase {
verify(mDisplayContent.mAppTransition) verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */); .overridePendingAppTransitionRemote(adapter, false /* sync */);
} }
@Test
public void testTransitionGoodToGoForTaskFragments() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final Task task = createTask(mDisplayContent);
final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.createActivityCount(1)
.setOrganizer(organizer)
.build();
final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.setOrganizer(organizer)
.build();
changeTaskFragment.getTopMostActivity().allDrawn = true;
mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
mDisplayContent.mChangingContainers.add(changeTaskFragment);
spyOn(mDisplayContent.mAppTransition);
spyOn(emptyTaskFragment);
mDisplayContent.mAppTransitionController.handleAppTransitionReady();
// Transition not ready because there is an empty non-finishing TaskFragment.
verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());
doReturn(true).when(emptyTaskFragment).hasChild();
emptyTaskFragment.remove(false /* withTransition */, "test");
mDisplayContent.mAppTransitionController.handleAppTransitionReady();
// Transition ready because the empty (no running activity) TaskFragment is requested to be
// removed.
verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
}
} }