Merge "Snapshot all tasks in pending recents animation when rotating devices" into sc-v2-dev

This commit is contained in:
Jerry Chang
2021-11-08 10:22:34 +00:00
committed by Android (Google) Code Review
10 changed files with 93 additions and 58 deletions

View File

@@ -25,7 +25,6 @@ import static org.hamcrest.core.Is.is;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.window.TaskSnapshot;
import android.app.IActivityTaskManager; import android.app.IActivityTaskManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -41,6 +40,7 @@ import android.util.Pair;
import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner; import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget; import android.view.RemoteAnimationTarget;
import android.window.TaskSnapshot;
import androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import androidx.test.runner.lifecycle.Stage; import androidx.test.runner.lifecycle.Stage;
@@ -210,7 +210,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
} }
@Override @Override
public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException { public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots)
throws RemoteException {
Assume.assumeNoException( Assume.assumeNoException(
new AssertionError("onAnimationCanceled should not be called")); new AssertionError("onAnimationCanceled should not be called"));
} }

View File

@@ -35,15 +35,17 @@ oneway interface IRecentsAnimationRunner {
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined * wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time. * amount of time.
* *
* @param taskSnapshot If the snapshot is null, the animation will be cancelled and the leash * @param taskIds Indicates tasks with cancelling snapshot.
* will be inactive immediately. Otherwise, the contents of the task will be * @param taskSnapshots If the snapshots is null, the animation will be cancelled and the leash
* replaced with {@param taskSnapshot}, such that the runner's leash is * will be inactive immediately. Otherwise, the contents of the tasks will
* still active. As soon as the runner doesn't need the leash anymore, it * be replaced with {@param taskSnapshots}, such that the runner's leash is
* must call {@link IRecentsAnimationController#cleanupScreenshot). * still active. As soon as the runner doesn't need the leash anymore, it
* must call {@link IRecentsAnimationController#cleanupScreenshot).
* *
* @see {@link RecentsAnimationController#cleanupScreenshot} * @see {@link RecentsAnimationController#cleanupScreenshot}
*/ */
void onAnimationCanceled(in @nullable TaskSnapshot taskSnapshot) = 1; void onAnimationCanceled(in @nullable int[] taskIds,
in @nullable TaskSnapshot[] taskSnapshots) = 1;
/** /**
* Called when the system is ready for the handler to start animating all the visible tasks. * Called when the system is ready for the handler to start animating all the visible tasks.

View File

@@ -22,7 +22,6 @@ import static android.graphics.Bitmap.Config.ARGB_8888;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED; import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
import android.window.TaskSnapshot;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
@@ -30,6 +29,9 @@ import android.graphics.Rect;
import android.hardware.HardwareBuffer; import android.hardware.HardwareBuffer;
import android.util.Log; import android.util.Log;
import android.view.WindowInsetsController.Appearance; import android.view.WindowInsetsController.Appearance;
import android.window.TaskSnapshot;
import java.util.HashMap;
/** /**
* Data for a single thumbnail. * Data for a single thumbnail.
@@ -80,6 +82,18 @@ public class ThumbnailData {
return thumbnail; return thumbnail;
} }
public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) {
HashMap<Integer, ThumbnailData> temp = new HashMap<>();
if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) {
return temp;
}
for (int i = snapshots.length - 1; i >= 0; i--) {
temp.put(taskIds[i], new ThumbnailData(snapshots[i]));
}
return temp;
}
public ThumbnailData(TaskSnapshot snapshot) { public ThumbnailData(TaskSnapshot snapshot) {
thumbnail = makeThumbnail(snapshot); thumbnail = makeThumbnail(snapshot);
insets = new Rect(snapshot.getContentInsets()); insets = new Rect(snapshot.getContentInsets());

View File

@@ -28,7 +28,6 @@ import android.app.ActivityClient;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo;
import android.window.TaskSnapshot;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.AppGlobals; import android.app.AppGlobals;
@@ -50,6 +49,7 @@ import android.util.Log;
import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner; import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget; import android.view.RemoteAnimationTarget;
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService; import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task;
@@ -189,9 +189,9 @@ public class ActivityManagerWrapper {
} }
@Override @Override
public void onAnimationCanceled(TaskSnapshot taskSnapshot) { public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
animationHandler.onAnimationCanceled( animationHandler.onAnimationCanceled(
taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null); ThumbnailData.wrap(taskIds, taskSnapshots));
} }
@Override @Override

View File

@@ -39,12 +39,14 @@ public class RecentsAnimationControllerCompat {
public ThumbnailData screenshotTask(int taskId) { public ThumbnailData screenshotTask(int taskId) {
try { try {
TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId); final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData(); if (snapshot != null) {
return new ThumbnailData(snapshot);
}
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to screenshot task", e); Log.e(TAG, "Failed to screenshot task", e);
return new ThumbnailData();
} }
return new ThumbnailData();
} }
public void setInputConsumerEnabled(boolean enabled) { public void setInputConsumerEnabled(boolean enabled) {

View File

@@ -20,6 +20,8 @@ import android.graphics.Rect;
import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.HashMap;
public interface RecentsAnimationListener { public interface RecentsAnimationListener {
/** /**
* Called when the animation into Recents can start. This call is made on the binder thread. * Called when the animation into Recents can start. This call is made on the binder thread.
@@ -31,7 +33,7 @@ public interface RecentsAnimationListener {
/** /**
* Called when the animation into Recents was canceled. This call is made on the binder thread. * Called when the animation into Recents was canceled. This call is made on the binder thread.
*/ */
void onAnimationCanceled(ThumbnailData thumbnailData); void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas);
/** /**
* Called when the task of an activity that has been started while the recents animation * Called when the task of an activity that has been started while the recents animation

View File

@@ -473,7 +473,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
*/ */
static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
try { try {
recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */); recentsAnimationRunner.onAnimationCanceled(null /* taskIds */,
null /* taskSnapshots */);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation before start", e); Slog.e(TAG, "Failed to cancel recents animation before start", e);
} }

View File

@@ -853,37 +853,37 @@ public class RecentsAnimationController implements DeathRecipient {
mCanceled = true; mCanceled = true;
if (screenshot && !mPendingAnimations.isEmpty()) { if (screenshot && !mPendingAnimations.isEmpty()) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(0); final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
final Task task = adapter.mTask;
// Screen shot previous task when next task starts transition and notify the runner.
// We will actually finish the animation once the runner calls cleanUpScreenshot().
final TaskSnapshot taskSnapshot = screenshotRecentTask(task);
mPendingCancelWithScreenshotReorderMode = reorderMode; mPendingCancelWithScreenshotReorderMode = reorderMode;
try {
mRunner.onAnimationCanceled(taskSnapshot); if (!snapshotMap.isEmpty()) {
} catch (RemoteException e) { try {
Slog.e(TAG, "Failed to cancel recents animation", e); int[] taskIds = new int[snapshotMap.size()];
} TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
if (taskSnapshot != null) { for (int i = snapshotMap.size() - 1; i >= 0; i--) {
// Defer until the runner calls back to cleanupScreenshot() taskIds[i] = snapshotMap.keyAt(i).mTaskId;
adapter.setSnapshotOverlay(taskSnapshot); snapshots[i] = snapshotMap.valueAt(i);
}
mRunner.onAnimationCanceled(taskIds, snapshots);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
// Schedule a new failsafe for if the runner doesn't clean up the screenshot // Schedule a new failsafe for if the runner doesn't clean up the screenshot
scheduleFailsafe(); scheduleFailsafe();
} else { return;
// Do a normal cancel since we couldn't screenshot
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
} }
} else { // Fallback to a normal cancel since we couldn't screenshot
// Otherwise, notify the runner and clean up the animation immediately
// Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
// to the runner if we this actually triggers cancel twice on the caller
try {
mRunner.onAnimationCanceled(null /* taskSnapshot */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
} }
// Notify the runner and clean up the animation immediately
// Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
// to the runner if we this actually triggers cancel twice on the caller
try {
mRunner.onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
} }
} }
@@ -957,13 +957,23 @@ public class RecentsAnimationController implements DeathRecipient {
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot; return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
} }
TaskSnapshot screenshotRecentTask(Task task) { private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController; final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task); final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
snapshotController.snapshotTasks(tasks); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
snapshotController.addSkipClosingAppSnapshotTasks(tasks); final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
return snapshotController.getSnapshot(task.mTaskId, task.mUserId, final Task task = adapter.mTask;
false /* restoreFromDisk */, false /* isLowResolution */); snapshotController.recordTaskSnapshot(task, false /* allowSnapshotHome */);
final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
false /* restoreFromDisk */, false /* isLowResolution */);
if (snapshot != null) {
snapshotMap.put(task, snapshot);
// Defer until the runner calls back to cleanupScreenshot()
adapter.setSnapshotOverlay(snapshot);
}
}
snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet());
return snapshotMap;
} }
void cleanupAnimation(@ReorderMode int reorderMode) { void cleanupAnimation(@ReorderMode int reorderMode) {

View File

@@ -54,6 +54,7 @@ import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets; import com.google.android.collect.Sets;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Set;
/** /**
* When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
@@ -167,7 +168,7 @@ class TaskSnapshotController {
* calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot. * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
*/ */
@VisibleForTesting @VisibleForTesting
void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) { void addSkipClosingAppSnapshotTasks(Set<Task> tasks) {
if (shouldDisableSnapshots()) { if (shouldDisableSnapshots()) {
return; return;
} }

View File

@@ -124,7 +124,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
// Verify that the finish callback to reparent the leash is called // Verify that the finish callback to reparent the leash is called
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter)); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter));
// Verify the animation canceled callback to the app was made // Verify the animation canceled callback to the app was made
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */); verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner); verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
} }
@@ -207,7 +207,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
wallpaperWindowToken.cancelAnimation(); wallpaperWindowToken.cancelAnimation();
assertTrue(mController.isAnimatingTask(activity.getTask())); assertTrue(mController.isAnimatingTask(activity.getTask()));
assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken)); assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */); verify(mMockRunner, never()).onAnimationCanceled(null /* taskIds */,
null /* taskSnapshots */);
} }
@Test @Test
@@ -254,7 +255,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setDeferredCancel(true /* deferred */, false /* screenshot */); mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */); mController.cancelAnimationWithScreenshot(false /* screenshot */);
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */); verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
// Simulate the app transition finishing // Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0); mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
@@ -281,7 +282,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */); anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.setDeferredCancel(true /* deferred */, true /* screenshot */); mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
mController.cancelAnimationWithScreenshot(true /* screenshot */); mController.cancelAnimationWithScreenshot(true /* screenshot */);
verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */); verify(mMockRunner).onAnimationCanceled(any(int[].class) /* taskIds */,
any(TaskSnapshot[].class) /* taskSnapshots */);
// Continue the animation (simulating a call to cleanupScreenshot()) // Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation(); mController.continueDeferredCancelAnimation();
@@ -322,7 +324,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(), doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */); anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.cancelAnimationWithScreenshot(true /* screenshot */); mController.cancelAnimationWithScreenshot(true /* screenshot */);
verify(mMockRunner).onAnimationCanceled(any()); verify(mMockRunner).onAnimationCanceled(any(), any());
// Simulate process crashing and ensure the animation is still canceled // Simulate process crashing and ensure the animation is still canceled
mController.binderDied(); mController.binderDied();
@@ -700,7 +702,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setWillFinishToHome(true); mController.setWillFinishToHome(true);
mController.cancelAnimationForDisplayChange(); mController.cancelAnimationForDisplayChange();
verify(mMockRunner).onAnimationCanceled(any()); verify(mMockRunner).onAnimationCanceled(any(), any());
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false); verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
} }
@@ -715,7 +717,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mController.setWillFinishToHome(false); mController.setWillFinishToHome(false);
mController.cancelAnimationForDisplayChange(); mController.cancelAnimationForDisplayChange();
verify(mMockRunner).onAnimationCanceled(any()); verify(mMockRunner).onAnimationCanceled(any(), any());
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false); verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
} }
@@ -735,7 +737,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(), doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */); anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.cancelAnimationForHomeStart(); mController.cancelAnimationForHomeStart();
verify(mMockRunner).onAnimationCanceled(any()); verify(mMockRunner).onAnimationCanceled(any(), any());
// Continue the animation (simulating a call to cleanupScreenshot()) // Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation(); mController.continueDeferredCancelAnimation();