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