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.ActivityTaskManager;
import android.window.TaskSnapshot;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
@@ -41,6 +40,7 @@ import android.util.Pair;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.window.TaskSnapshot;
import androidx.test.filters.LargeTest;
import androidx.test.runner.lifecycle.Stage;
@@ -210,7 +210,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
}
@Override
public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots)
throws RemoteException {
Assume.assumeNoException(
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
* amount of time.
*
* @param taskSnapshot If the snapshot is null, the animation will be cancelled and the leash
* will be inactive immediately. Otherwise, the contents of the task will be
* replaced with {@param taskSnapshot}, such that the runner's leash is
* still active. As soon as the runner doesn't need the leash anymore, it
* must call {@link IRecentsAnimationController#cleanupScreenshot).
* @param taskIds Indicates tasks with cancelling snapshot.
* @param taskSnapshots If the snapshots is null, the animation will be cancelled and the leash
* will be inactive immediately. Otherwise, the contents of the tasks will
* be replaced with {@param taskSnapshots}, such that the runner's leash is
* still active. As soon as the runner doesn't need the leash anymore, it
* must call {@link IRecentsAnimationController#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.

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 android.window.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
@@ -30,6 +29,9 @@ import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Log;
import android.view.WindowInsetsController.Appearance;
import android.window.TaskSnapshot;
import java.util.HashMap;
/**
* Data for a single thumbnail.
@@ -80,6 +82,18 @@ public class ThumbnailData {
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) {
thumbnail = makeThumbnail(snapshot);
insets = new Rect(snapshot.getContentInsets());

View File

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

View File

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

View File

@@ -20,6 +20,8 @@ import android.graphics.Rect;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.HashMap;
public interface RecentsAnimationListener {
/**
* 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.
*/
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

View File

@@ -473,7 +473,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
*/
static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
try {
recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */);
recentsAnimationRunner.onAnimationCanceled(null /* taskIds */,
null /* taskSnapshots */);
} catch (RemoteException 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;
if (screenshot && !mPendingAnimations.isEmpty()) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(0);
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);
final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
mPendingCancelWithScreenshotReorderMode = reorderMode;
try {
mRunner.onAnimationCanceled(taskSnapshot);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
if (taskSnapshot != null) {
// Defer until the runner calls back to cleanupScreenshot()
adapter.setSnapshotOverlay(taskSnapshot);
if (!snapshotMap.isEmpty()) {
try {
int[] taskIds = new int[snapshotMap.size()];
TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
for (int i = snapshotMap.size() - 1; i >= 0; i--) {
taskIds[i] = snapshotMap.keyAt(i).mTaskId;
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
scheduleFailsafe();
} else {
// Do a normal cancel since we couldn't screenshot
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
return;
}
} else {
// 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 */);
// Fallback to a normal cancel since we couldn't screenshot
}
// 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;
}
TaskSnapshot screenshotRecentTask(Task task) {
private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
snapshotController.addSkipClosingAppSnapshotTasks(tasks);
return snapshotController.getSnapshot(task.mTaskId, task.mUserId,
false /* restoreFromDisk */, false /* isLowResolution */);
final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
final Task task = adapter.mTask;
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) {

View File

@@ -54,6 +54,7 @@ import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.Set;
/**
* 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.
*/
@VisibleForTesting
void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
void addSkipClosingAppSnapshotTasks(Set<Task> tasks) {
if (shouldDisableSnapshots()) {
return;
}

View File

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