Revert "Revert "2/ Add support for remote Recents animation""
This reverts commit 9f8518e532e41ba57916afc49bba72bc23ad3eda.
Reason for revert: Testing relanding changes with ag/3515280
Change-Id: I410bd752c815a5b998a719453def01e00a9d47c8
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
new file mode 100644
index 0000000..c7ad17b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.WindowConfiguration;
+import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls a single instance of the remote driven recents animation. In particular, this allows
+ * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
+ * runner is provided an animation controller which allows it to take screenshots and to notify
+ * window manager when the animation is completed. In addition, window manager may also notify the
+ * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
+ */
+public class RecentsAnimationController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
+ private static final boolean DEBUG = false;
+
+ private final WindowManagerService mService;
+ private final IRecentsAnimationRunner mRunner;
+ private final RecentsAnimationCallbacks mCallbacks;
+ private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+
+ // The recents component app token that is shown behind the visibile tasks
+ private AppWindowToken mHomeAppToken;
+
+ // We start the RecentsAnimationController in a pending-start state since we need to wait for
+ // the wallpaper/activity to draw before we can give control to the handler to start animating
+ // the visible task surfaces
+ private boolean mPendingStart = true;
+
+ // Set when the animation has been canceled
+ private boolean mCanceled = false;
+
+ // Whether or not the input consumer is enabled. The input consumer must be both registered and
+ // enabled for it to start intercepting touch events.
+ private boolean mInputConsumerEnabled;
+
+ public interface RecentsAnimationCallbacks {
+ void onAnimationFinished(boolean moveHomeToTop);
+ }
+
+ private final IRecentsAnimationController mController =
+ new IRecentsAnimationController.Stub() {
+
+ @Override
+ public TaskSnapshot screenshotTask(int taskId) {
+ if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ return null;
+ }
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+ final Task task = adapter.mTask;
+ if (task.mTaskId == taskId) {
+ // TODO: Save this screenshot as the task snapshot?
+ final Rect taskFrame = new Rect();
+ task.getBounds(taskFrame);
+ final GraphicBuffer buffer = SurfaceControl.captureLayers(
+ task.getSurfaceControl().getHandle(), taskFrame, 1f);
+ final AppWindowToken topChild = task.getTopChild();
+ final WindowState mainWindow = topChild.findMainWindow();
+ return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
+ mainWindow.mStableInsets,
+ ActivityManager.isLowRamDeviceStatic() /* reduced */,
+ 1.0f /* scale */);
+ }
+ }
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void finish(boolean moveHomeToTop) {
+ if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ return;
+ }
+ }
+
+ // Note, the callback will handle its own synchronization, do not lock on WM lock
+ // prior to calling the callback
+ mCallbacks.onAnimationFinished(moveHomeToTop);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setInputConsumerEnabled(boolean enabled) {
+ if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
+ + mCanceled);
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService.getWindowManagerLock()) {
+ if (mCanceled) {
+ return;
+ }
+
+ mInputConsumerEnabled = enabled;
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+ mService.scheduleAnimationLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ /**
+ * Initializes a new RecentsAnimationController.
+ *
+ * @param remoteAnimationRunner The remote runner which should be notified when the animation is
+ * ready to start or has been canceled
+ * @param callbacks Callbacks to be made when the animation finishes
+ * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
+ * animation is complete. Will be passed to the callback.
+ */
+ RecentsAnimationController(WindowManagerService service,
+ IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
+ int displayId) {
+ mService = service;
+ mRunner = remoteAnimationRunner;
+ mCallbacks = callbacks;
+
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
+ if (visibleTasks.isEmpty()) {
+ cancelAnimation();
+ return;
+ }
+
+ // Make leashes for each of the visible tasks and add it to the recents animation to be
+ // started
+ final int taskCount = visibleTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ final Task task = visibleTasks.get(i);
+ final WindowConfiguration config = task.getWindowConfiguration();
+ if (config.tasksAreFloating()
+ || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || config.getActivityType() == ACTIVITY_TYPE_HOME) {
+ continue;
+ }
+ addAnimation(task);
+ }
+
+ // Adjust the wallpaper visibility for the showing home activity
+ final AppWindowToken recentsComponentAppToken =
+ dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
+ if (recentsComponentAppToken != null) {
+ if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
+ mHomeAppToken = recentsComponentAppToken;
+ final WallpaperController wc = dc.mWallpaperController;
+ if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
+ dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ dc.setLayoutNeeded();
+ }
+ }
+
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+
+ private void addAnimation(Task task) {
+ if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+ final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
+ mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
+ final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
+ anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
+ task.commitPendingTransaction();
+ mPendingAnimations.add(taskAdapter);
+ }
+
+ void startAnimation() {
+ if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
+ if (!mPendingStart) {
+ return;
+ }
+ try {
+ final RemoteAnimationTarget[] appAnimations =
+ new RemoteAnimationTarget[mPendingAnimations.size()];
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
+ }
+ mPendingStart = false;
+ mRunner.onAnimationStart(mController, appAnimations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start recents animation", e);
+ }
+ }
+
+ void cancelAnimation() {
+ if (DEBUG) Log.d(TAG, "cancelAnimation()");
+ if (mCanceled) {
+ // We've already canceled the animation
+ return;
+ }
+ mCanceled = true;
+ try {
+ mRunner.onAnimationCanceled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation", e);
+ }
+
+ // Clean up and return to the previous app
+ mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
+ }
+
+ void cleanupAnimation() {
+ if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
+ + mPendingAnimations.size());
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+ adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+ }
+ mPendingAnimations.clear();
+
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+ mService.scheduleAnimationLocked();
+ }
+
+ void checkAnimationReady(WallpaperController wallpaperController) {
+ if (mPendingStart) {
+ final boolean wallpaperReady = !isHomeAppOverWallpaper()
+ || (wallpaperController.getWallpaperTarget() != null
+ && wallpaperController.wallpaperTransitionReady());
+ if (wallpaperReady) {
+ mService.getRecentsAnimationController().startAnimation();
+ }
+ }
+ }
+
+ boolean isWallpaperVisible(WindowState w) {
+ return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken
+ && isHomeAppOverWallpaper();
+ }
+
+ boolean isHomeAppOverWallpaper() {
+ if (mHomeAppToken == null) {
+ return false;
+ }
+ return mHomeAppToken.windowsCanBeWallpaperTarget();
+ }
+
+ WindowState getHomeAppMainWindow() {
+ if (mHomeAppToken == null) {
+ return null;
+ }
+ return mHomeAppToken.findMainWindow();
+ }
+
+ boolean isAnimatingApp(AppWindowToken appToken) {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final Task task = mPendingAnimations.get(i).mTask;
+ for (int j = task.getChildCount() - 1; j >= 0; j--) {
+ final AppWindowToken app = task.getChildAt(j);
+ if (app == appToken) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean isInputConsumerEnabled() {
+ return mInputConsumerEnabled;
+ }
+
+ private class TaskAnimationAdapter implements AnimationAdapter {
+
+ private Task mTask;
+ private SurfaceControl mCapturedLeash;
+ private OnAnimationFinishedCallback mCapturedFinishCallback;
+
+ TaskAnimationAdapter(Task task) {
+ mTask = task;
+ }
+
+ RemoteAnimationTarget createRemoteAnimationApp() {
+ // TODO: Do we need position and stack bounds?
+ return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+ !mTask.fillsParent(),
+ mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
+ mTask.getPrefixOrderIndex(), new Point(), new Rect(),
+ mTask.getWindowConfiguration());
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return false;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ mCapturedFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ cancelAnimation();
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis();
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
+ pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+ pw.print(innerPrefix); pw.println("mHomeAppToken=" + mHomeAppToken);
+ }
+}