• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
21 import static android.view.RemoteAnimationTarget.MODE_OPENING;
22 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
23 
24 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
25 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
26 
27 import android.annotation.NonNull;
28 import android.graphics.Point;
29 import android.graphics.Rect;
30 import android.os.Binder;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.os.SystemClock;
34 import android.util.Slog;
35 import android.util.proto.ProtoOutputStream;
36 import android.view.RemoteAnimationTarget;
37 import android.view.SurfaceControl;
38 import android.view.WindowInsets;
39 import android.window.BackNavigationInfo;
40 import android.window.IBackAnimationRunner;
41 import android.window.IBackNaviAnimationController;
42 
43 import com.android.server.wm.utils.InsetUtils;
44 
45 import java.io.PrintWriter;
46 import java.util.ArrayList;
47 
48 /**
49  * Controls the back navigation animation.
50  * This is throw-away code and should only be used for Android T, most code is duplicated from
51  * RecentsAnimationController which should be stable to handle animation leash resources/flicker/
52  * fixed rotation, etc. Remove this class at U and migrate to shell transition.
53  */
54 public class BackNaviAnimationController implements IBinder.DeathRecipient {
55     private static final String TAG = BackNavigationController.TAG;
56     // Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state
57     private static final int MODE_UNKNOWN = -1;
58 
59     // The activity which host this animation
60     private ActivityRecord mTargetActivityRecord;
61     // The original top activity
62     private ActivityRecord mTopActivity;
63 
64     private final DisplayContent mDisplayContent;
65     private final WindowManagerService mWindowManagerService;
66     private final BackNavigationController mBackNavigationController;
67 
68     // We start the BackAnimationController in a pending-start state since we need to wait for
69     // the wallpaper/activity to draw before we can give control to the handler to start animating
70     // the visible task surfaces
71     private boolean mPendingStart;
72     private IBackAnimationRunner mRunner;
73     final IBackNaviAnimationController mRemoteController;
74     private boolean mLinkedToDeathOfRunner;
75 
76     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
77 
BackNaviAnimationController(IBackAnimationRunner runner, BackNavigationController backNavigationController, int displayId)78     BackNaviAnimationController(IBackAnimationRunner runner,
79             BackNavigationController backNavigationController, int displayId) {
80         mRunner = runner;
81         mBackNavigationController = backNavigationController;
82         mWindowManagerService = mBackNavigationController.mWindowManagerService;
83         mDisplayContent = mWindowManagerService.mRoot.getDisplayContent(displayId);
84 
85         mRemoteController = new IBackNaviAnimationController.Stub() {
86             @Override
87             public void finish(boolean triggerBack) {
88                 synchronized (mWindowManagerService.getWindowManagerLock()) {
89                     final long token = Binder.clearCallingIdentity();
90                     try {
91                         mWindowManagerService.inSurfaceTransaction(() -> {
92                             mWindowManagerService.mAtmService.deferWindowLayout();
93                             try {
94                                 if (triggerBack) {
95                                     mDisplayContent.mFixedRotationTransitionListener
96                                             .notifyRecentsWillBeTop();
97                                     if (mTopActivity != null) {
98                                         mWindowManagerService.mTaskSnapshotController
99                                                 .recordTaskSnapshot(mTopActivity.getTask(), false);
100                                         // TODO consume moveTaskToBack?
101                                         mTopActivity.commitVisibility(false, false, true);
102                                     }
103                                 } else {
104                                     mTargetActivityRecord.mTaskSupervisor
105                                             .scheduleLaunchTaskBehindComplete(
106                                                     mTargetActivityRecord.token);
107                                 }
108                                 cleanupAnimation();
109                             } finally {
110                                 mWindowManagerService.mAtmService.continueWindowLayout();
111                             }
112                         });
113                     } finally {
114                         Binder.restoreCallingIdentity(token);
115                     }
116                 }
117             }
118         };
119     }
120 
121     /**
122      * @param targetActivity The home or opening activity which should host the wallpaper
123      * @param topActivity The current top activity before animation start.
124      */
initialize(ActivityRecord targetActivity, ActivityRecord topActivity)125     void initialize(ActivityRecord targetActivity, ActivityRecord topActivity) {
126         mTargetActivityRecord = targetActivity;
127         mTopActivity = topActivity;
128         final Task topTask = mTopActivity.getTask();
129 
130         createAnimationAdapter(topTask, (type, anim) -> topTask.forAllWindows(
131                 win -> {
132                     win.onAnimationFinished(type, anim);
133                 }, true));
134         final Task homeTask = mTargetActivityRecord.getRootTask();
135         createAnimationAdapter(homeTask, (type, anim) -> homeTask.forAllWindows(
136                 win -> {
137                     win.onAnimationFinished(type, anim);
138                 }, true));
139         try {
140             linkToDeathOfRunner();
141         } catch (RemoteException e) {
142             cancelAnimation();
143             return;
144         }
145 
146         if (targetActivity.windowsCanBeWallpaperTarget()) {
147             mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
148             mDisplayContent.setLayoutNeeded();
149         }
150 
151         mWindowManagerService.mWindowPlacerLocked.performSurfacePlacement();
152 
153         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);
154         mPendingStart = true;
155     }
156 
cleanupAnimation()157     void cleanupAnimation() {
158         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
159             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
160 
161             removeAnimationAdapter(taskAdapter);
162             taskAdapter.onCleanup();
163         }
164         mTargetActivityRecord.mLaunchTaskBehind = false;
165         // Clear references to the runner
166         unlinkToDeathOfRunner();
167         mRunner = null;
168 
169         // Update the input windows after the animation is complete
170         final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
171         inputMonitor.updateInputWindowsLw(true /*force*/);
172 
173         mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
174         mBackNavigationController.finishAnimation();
175     }
176 
removeAnimationAdapter(TaskAnimationAdapter taskAdapter)177     void removeAnimationAdapter(TaskAnimationAdapter taskAdapter) {
178         taskAdapter.onRemove();
179         mPendingAnimations.remove(taskAdapter);
180     }
181 
checkAnimationReady(WallpaperController wallpaperController)182     void checkAnimationReady(WallpaperController wallpaperController) {
183         if (mPendingStart) {
184             final boolean wallpaperReady = !isTargetOverWallpaper()
185                     || (wallpaperController.getWallpaperTarget() != null
186                     && wallpaperController.wallpaperTransitionReady());
187             if (wallpaperReady) {
188                 startAnimation();
189             }
190         }
191     }
192 
isWallpaperVisible(WindowState w)193     boolean isWallpaperVisible(WindowState w) {
194         return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION
195                 && ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
196                         || isAnimatingTask(w.getTask()))
197                 && isTargetOverWallpaper() && w.isOnScreen();
198     }
199 
isAnimatingTask(Task task)200     boolean isAnimatingTask(Task task) {
201         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
202             if (task == mPendingAnimations.get(i).mTask) {
203                 return true;
204             }
205         }
206         return false;
207     }
208 
linkFixedRotationTransformIfNeeded(@onNull WindowToken wallpaper)209     void linkFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
210         if (mTargetActivityRecord == null) {
211             return;
212         }
213         wallpaper.linkFixedRotationTransform(mTargetActivityRecord);
214     }
215 
linkToDeathOfRunner()216     private void linkToDeathOfRunner() throws RemoteException {
217         if (!mLinkedToDeathOfRunner) {
218             mRunner.asBinder().linkToDeath(this, 0);
219             mLinkedToDeathOfRunner = true;
220         }
221     }
222 
unlinkToDeathOfRunner()223     private void unlinkToDeathOfRunner() {
224         if (mLinkedToDeathOfRunner) {
225             mRunner.asBinder().unlinkToDeath(this, 0);
226             mLinkedToDeathOfRunner = false;
227         }
228     }
229 
startAnimation()230     void startAnimation() {
231         if (!mPendingStart) {
232             // Skip starting if we've already started or canceled the animation
233             return;
234         }
235         // Create the app targets
236         final RemoteAnimationTarget[] appTargets = createAppAnimations();
237 
238         // Skip the animation if there is nothing to animate
239         if (appTargets.length == 0) {
240             cancelAnimation();
241             return;
242         }
243 
244         mPendingStart = false;
245 
246         try {
247             mRunner.onAnimationStart(mRemoteController, BackNavigationInfo.TYPE_RETURN_TO_HOME,
248                     appTargets, null /* wallpapers */, null /*nonApps*/);
249         } catch (RemoteException e) {
250             cancelAnimation();
251         }
252     }
253 
254     @Override
binderDied()255     public void binderDied() {
256         cancelAnimation();
257     }
258 
createAnimationAdapter(Task task, SurfaceAnimator.OnAnimationFinishedCallback finishedCallback)259     TaskAnimationAdapter createAnimationAdapter(Task task,
260             SurfaceAnimator.OnAnimationFinishedCallback finishedCallback) {
261         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
262                 mTargetActivityRecord, this::cancelAnimation);
263         // borrow from recents since we cannot start back animation if recents is playing
264         task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */,
265                 ANIMATION_TYPE_RECENTS, finishedCallback);
266         task.commitPendingTransaction();
267         mPendingAnimations.add(taskAdapter);
268         return taskAdapter;
269     }
270 
createAppAnimations()271     private RemoteAnimationTarget[] createAppAnimations() {
272         final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
273         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
274             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
275             final RemoteAnimationTarget target =
276                     taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID, MODE_UNKNOWN);
277             if (target != null) {
278                 targets.add(target);
279             } else {
280                 removeAnimationAdapter(taskAdapter);
281             }
282         }
283         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
284     }
285 
cancelAnimation()286     private void cancelAnimation() {
287         synchronized (mWindowManagerService.getWindowManagerLock()) {
288             // Notify the runner and clean up the animation immediately
289             // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
290             // to the runner if we this actually triggers cancel twice on the caller
291             try {
292                 mRunner.onAnimationCancelled();
293             } catch (RemoteException e) {
294                 Slog.e(TAG, "Failed to cancel recents animation", e);
295             }
296             cleanupAnimation();
297         }
298     }
299 
isTargetOverWallpaper()300     private boolean isTargetOverWallpaper() {
301         if (mTargetActivityRecord == null) {
302             return false;
303         }
304         return mTargetActivityRecord.windowsCanBeWallpaperTarget();
305     }
306 
307     private static class TaskAnimationAdapter implements AnimationAdapter {
308         private final Task mTask;
309         private SurfaceControl mCapturedLeash;
310         private SurfaceAnimator.OnAnimationFinishedCallback mCapturedFinishCallback;
311         @SurfaceAnimator.AnimationType private int mLastAnimationType;
312         private RemoteAnimationTarget mTarget;
313         private final ActivityRecord mTargetActivityRecord;
314         private final Runnable mCancelCallback;
315 
316         private final Rect mBounds = new Rect();
317         // The bounds of the target relative to its parent.
318         private final Rect mLocalBounds = new Rect();
319 
TaskAnimationAdapter(Task task, ActivityRecord target, Runnable cancelCallback)320         TaskAnimationAdapter(Task task, ActivityRecord target, Runnable cancelCallback) {
321             mTask = task;
322             mBounds.set(mTask.getBounds());
323 
324             mLocalBounds.set(mBounds);
325             Point tmpPos = new Point();
326             mTask.getRelativePosition(tmpPos);
327             mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
328             mTargetActivityRecord = target;
329             mCancelCallback = cancelCallback;
330         }
331 
332         // Keep overrideTaskId and overrideMode now, if we need to add other type of back animation
333         // on legacy transition system then they can be useful.
createRemoteAnimationTarget(int overrideTaskId, int overrideMode)334         RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId, int overrideMode) {
335             ActivityRecord topApp = mTask.getTopRealVisibleActivity();
336             if (topApp == null) {
337                 topApp = mTask.getTopVisibleActivity();
338             }
339             final WindowState mainWindow = topApp != null
340                     ? topApp.findMainWindow()
341                     : null;
342             if (mainWindow == null) {
343                 return null;
344             }
345             final Rect insets =
346                     mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
347                             mBounds, WindowInsets.Type.systemBars(),
348                             false /* ignoreVisibility */).toRect();
349             InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
350             final int mode = overrideMode != MODE_UNKNOWN
351                     ? overrideMode
352                     : topApp.getActivityType() == mTargetActivityRecord.getActivityType()
353                             ? MODE_OPENING
354                             : MODE_CLOSING;
355             if (overrideTaskId < 0) {
356                 overrideTaskId = mTask.mTaskId;
357             }
358             mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash,
359                     !topApp.fillsParent(), new Rect(),
360                     insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
361                     mLocalBounds, mBounds, mTask.getWindowConfiguration(),
362                     true /* isNotInRecents */, null, null, mTask.getTaskInfo(),
363                     topApp.checkEnterPictureInPictureAppOpsState());
364             return mTarget;
365         }
366         @Override
getShowWallpaper()367         public boolean getShowWallpaper() {
368             return false;
369         }
370         @Override
startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, @SurfaceAnimator.AnimationType int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback)371         public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
372                 @SurfaceAnimator.AnimationType int type,
373                 @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
374             t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top);
375             final Rect tmpRect = new Rect();
376             tmpRect.set(mLocalBounds);
377             tmpRect.offsetTo(0, 0);
378             t.setWindowCrop(animationLeash, tmpRect);
379             mCapturedLeash = animationLeash;
380             mCapturedFinishCallback = finishCallback;
381             mLastAnimationType = type;
382         }
383 
384         @Override
onAnimationCancelled(SurfaceControl animationLeash)385         public void onAnimationCancelled(SurfaceControl animationLeash) {
386             mCancelCallback.run();
387         }
388 
onRemove()389         void onRemove() {
390             mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this);
391         }
392 
onCleanup()393         void onCleanup() {
394             final SurfaceControl.Transaction pendingTransaction = mTask.getPendingTransaction();
395             if (!mTask.isAttached()) {
396                 // Apply the task's pending transaction in case it is detached and its transaction
397                 // is not reachable.
398                 pendingTransaction.apply();
399             }
400         }
401 
402         @Override
getDurationHint()403         public long getDurationHint() {
404             return 0;
405         }
406 
407         @Override
getStatusBarTransitionsStartTime()408         public long getStatusBarTransitionsStartTime() {
409             return SystemClock.uptimeMillis();
410         }
411 
412         @Override
dump(PrintWriter pw, String prefix)413         public void dump(PrintWriter pw, String prefix) { }
414 
415         @Override
dumpDebug(ProtoOutputStream proto)416         public void dumpDebug(ProtoOutputStream proto) { }
417     }
418 }
419