Loading data/etc/services.core.protolog.json +6 −0 Original line number Diff line number Diff line Loading @@ -553,6 +553,12 @@ "group": "WM_SHOW_TRANSACTIONS", "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": { "message": "Removing starting %s from %s", "level": "VERBOSE", Loading services/core/java/com/android/server/wm/AppTransitionController.java +100 −56 Original line number Diff line number Diff line Loading @@ -165,8 +165,8 @@ public class AppTransitionController { void handleAppTransitionReady() { mTempTransitionReasons.clear(); if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)) { || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons) || !transitionGoodToGoForTaskFragments()) { return; } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); Loading Loading @@ -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) { return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity() : wc.asActivityRecord(); Loading Loading @@ -972,11 +979,12 @@ public class AppTransitionController { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); if (mDisplayContent.mAppTransition.isTimeout()) { return true; } final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent( Display.DEFAULT_DISPLAY).getRotationAnimation(); if (!mDisplayContent.mAppTransition.isTimeout()) { // 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 // change for our transition because we need to wait for the rotation animation to Loading @@ -1003,7 +1011,6 @@ public class AppTransitionController { activity.startingMoved, activity.isRelaunching(), activity.mStartingWindow); final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { return false; Loading Loading @@ -1031,13 +1038,50 @@ public class AppTransitionController { } // If the wallpaper is visible, we need to check it's ready too. boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || mWallpaperControllerLocked.wallpaperTransitionReady(); if (wallpaperReady) { return !mWallpaperControllerLocked.isWallpaperVisible() || mWallpaperControllerLocked.wallpaperTransitionReady(); } 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 false; }); if (notReady) { return false; } } return true; } Loading services/core/java/com/android/server/wm/Task.java +0 −5 Original line number Diff line number Diff line Loading @@ -2347,11 +2347,6 @@ class Task extends TaskFragment { return getRootTask().mTaskId; } @Nullable Task getRootTask() { return getRootTaskFragment().asTask(); } /** @return the first organized task. */ @Nullable Task getOrganizedTask() { Loading services/core/java/com/android/server/wm/TaskFragment.java +12 −0 Original line number Diff line number Diff line Loading @@ -459,6 +459,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment(); } @Nullable Task getRootTask() { return getRootTaskFragment().asTask(); } @Override TaskFragment asTaskFragment() { return this; Loading Loading @@ -2153,6 +2158,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { 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 */ void clearLastPausedActivity() { forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); Loading services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -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_TO_FRONT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; 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.never; import static org.mockito.Mockito.verify; import android.os.Binder; Loading Loading @@ -791,4 +795,38 @@ public class AppTransitionControllerTest extends WindowTestsBase { verify(mDisplayContent.mAppTransition) .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()); } } No newline at end of file Loading
data/etc/services.core.protolog.json +6 −0 Original line number Diff line number Diff line Loading @@ -553,6 +553,12 @@ "group": "WM_SHOW_TRANSACTIONS", "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": { "message": "Removing starting %s from %s", "level": "VERBOSE", Loading
services/core/java/com/android/server/wm/AppTransitionController.java +100 −56 Original line number Diff line number Diff line Loading @@ -165,8 +165,8 @@ public class AppTransitionController { void handleAppTransitionReady() { mTempTransitionReasons.clear(); if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)) { || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons) || !transitionGoodToGoForTaskFragments()) { return; } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); Loading Loading @@ -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) { return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity() : wc.asActivityRecord(); Loading Loading @@ -972,11 +979,12 @@ public class AppTransitionController { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); if (mDisplayContent.mAppTransition.isTimeout()) { return true; } final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent( Display.DEFAULT_DISPLAY).getRotationAnimation(); if (!mDisplayContent.mAppTransition.isTimeout()) { // 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 // change for our transition because we need to wait for the rotation animation to Loading @@ -1003,7 +1011,6 @@ public class AppTransitionController { activity.startingMoved, activity.isRelaunching(), activity.mStartingWindow); final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { return false; Loading Loading @@ -1031,13 +1038,50 @@ public class AppTransitionController { } // If the wallpaper is visible, we need to check it's ready too. boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || mWallpaperControllerLocked.wallpaperTransitionReady(); if (wallpaperReady) { return !mWallpaperControllerLocked.isWallpaperVisible() || mWallpaperControllerLocked.wallpaperTransitionReady(); } 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 false; }); if (notReady) { return false; } } return true; } Loading
services/core/java/com/android/server/wm/Task.java +0 −5 Original line number Diff line number Diff line Loading @@ -2347,11 +2347,6 @@ class Task extends TaskFragment { return getRootTask().mTaskId; } @Nullable Task getRootTask() { return getRootTaskFragment().asTask(); } /** @return the first organized task. */ @Nullable Task getOrganizedTask() { Loading
services/core/java/com/android/server/wm/TaskFragment.java +12 −0 Original line number Diff line number Diff line Loading @@ -459,6 +459,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment(); } @Nullable Task getRootTask() { return getRootTaskFragment().asTask(); } @Override TaskFragment asTaskFragment() { return this; Loading Loading @@ -2153,6 +2158,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { 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 */ void clearLastPausedActivity() { forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); Loading
services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -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_TO_FRONT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; 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.never; import static org.mockito.Mockito.verify; import android.os.Binder; Loading Loading @@ -791,4 +795,38 @@ public class AppTransitionControllerTest extends WindowTestsBase { verify(mDisplayContent.mAppTransition) .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()); } } No newline at end of file