1 /* 2 * Copyright (C) 2021 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.wm.shell.back; 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.TRANSIT_CHANGE; 23 import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION; 24 import static android.window.BackEvent.EDGE_NONE; 25 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; 26 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; 27 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; 28 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; 29 30 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME; 31 import static com.android.systemui.Flags.predictiveBackDelayWmTransition; 32 import static com.android.window.flags.Flags.unifyBackNavigationTransition; 33 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.annotation.SuppressLint; 38 import android.app.ActivityManager; 39 import android.app.ActivityTaskManager; 40 import android.app.IActivityTaskManager; 41 import android.app.TaskInfo; 42 import android.content.ComponentName; 43 import android.content.Context; 44 import android.content.res.Configuration; 45 import android.graphics.Point; 46 import android.graphics.Rect; 47 import android.hardware.input.InputManager; 48 import android.os.Bundle; 49 import android.os.Handler; 50 import android.os.IBinder; 51 import android.os.RemoteCallback; 52 import android.os.RemoteException; 53 import android.os.SystemClock; 54 import android.util.Log; 55 import android.view.IRemoteAnimationRunner; 56 import android.view.InputDevice; 57 import android.view.KeyCharacterMap; 58 import android.view.KeyEvent; 59 import android.view.MotionEvent; 60 import android.view.RemoteAnimationTarget; 61 import android.view.SurfaceControl; 62 import android.view.WindowManager; 63 import android.window.BackAnimationAdapter; 64 import android.window.BackEvent; 65 import android.window.BackMotionEvent; 66 import android.window.BackNavigationInfo; 67 import android.window.BackTouchTracker; 68 import android.window.IBackAnimationFinishedCallback; 69 import android.window.IBackAnimationHandoffHandler; 70 import android.window.IBackAnimationRunner; 71 import android.window.IOnBackInvokedCallback; 72 import android.window.TransitionInfo; 73 import android.window.TransitionRequestInfo; 74 import android.window.WindowAnimationState; 75 import android.window.WindowContainerToken; 76 import android.window.WindowContainerTransaction; 77 78 import com.android.internal.annotations.VisibleForTesting; 79 import com.android.internal.protolog.ProtoLog; 80 import com.android.internal.util.LatencyTracker; 81 import com.android.internal.view.AppearanceRegion; 82 import com.android.systemui.animation.TransitionAnimator; 83 import com.android.window.flags.Flags; 84 import com.android.wm.shell.R; 85 import com.android.wm.shell.common.ExternalInterfaceBinder; 86 import com.android.wm.shell.common.RemoteCallable; 87 import com.android.wm.shell.common.ShellExecutor; 88 import com.android.wm.shell.shared.TransitionUtil; 89 import com.android.wm.shell.shared.annotations.ShellMainThread; 90 import com.android.wm.shell.sysui.ConfigurationChangeListener; 91 import com.android.wm.shell.sysui.ShellCommandHandler; 92 import com.android.wm.shell.sysui.ShellController; 93 import com.android.wm.shell.sysui.ShellInit; 94 import com.android.wm.shell.transition.Transitions; 95 96 import java.io.PrintWriter; 97 import java.util.ArrayList; 98 import java.util.function.Predicate; 99 100 /** 101 * Controls the window animation run when a user initiates a back gesture. 102 */ 103 public class BackAnimationController implements RemoteCallable<BackAnimationController>, 104 ConfigurationChangeListener { 105 private static final String TAG = "ShellBackPreview"; 106 107 /** 108 * Max duration to wait for an animation to finish before triggering the real back. 109 */ 110 private static final long MAX_ANIMATION_DURATION = 2000; 111 private final LatencyTracker mLatencyTracker; 112 @ShellMainThread private final Handler mHandler; 113 114 /** True when a back gesture is ongoing */ 115 @VisibleForTesting public boolean mBackGestureStarted = false; 116 117 /** Tracks if an uninterruptible animation is in progress */ 118 private boolean mPostCommitAnimationInProgress = false; 119 private boolean mRealCallbackInvoked = false; 120 121 /** Tracks if we should start the back gesture on the next motion move event */ 122 private boolean mShouldStartOnNextMoveEvent = false; 123 private boolean mOnBackStartDispatched = false; 124 private boolean mThresholdCrossed = false; 125 private boolean mPointersPilfered = false; 126 private final boolean mRequirePointerPilfer; 127 128 /** Registry for the back animations */ 129 private final ShellBackAnimationRegistry mShellBackAnimationRegistry; 130 131 @Nullable 132 private BackNavigationInfo mBackNavigationInfo; 133 private boolean mReceivedNullNavigationInfo = false; 134 private final IActivityTaskManager mActivityTaskManager; 135 private final Context mContext; 136 private final ShellController mShellController; 137 private final ShellCommandHandler mShellCommandHandler; 138 private final ShellExecutor mShellExecutor; 139 private final WindowManager mWindowManager; 140 private final Transitions mTransitions; 141 @VisibleForTesting 142 final BackTransitionHandler mBackTransitionHandler; 143 @VisibleForTesting 144 final Rect mTouchableArea = new Rect(); 145 146 /** 147 * Tracks the current user back gesture. 148 */ 149 private BackTouchTracker mCurrentTracker = new BackTouchTracker(); 150 151 /** 152 * Tracks the next back gesture in case a new user gesture has started while the back animation 153 * (and navigation) associated with {@link #mCurrentTracker} have not yet finished. 154 */ 155 private BackTouchTracker mQueuedTracker = new BackTouchTracker(); 156 157 private final BackTransitionObserver mBackTransitionObserver = 158 new BackTransitionObserver(); 159 160 private final Runnable mAnimationTimeoutRunnable = () -> { 161 ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...", 162 MAX_ANIMATION_DURATION); 163 finishBackAnimation(); 164 }; 165 166 private IBackAnimationFinishedCallback mBackAnimationFinishedCallback; 167 @VisibleForTesting 168 BackAnimationAdapter mBackAnimationAdapter; 169 170 @Nullable 171 private IOnBackInvokedCallback mActiveCallback; 172 @Nullable 173 @VisibleForTesting 174 RemoteAnimationTarget[] mApps; 175 176 @VisibleForTesting 177 final RemoteCallback mNavigationObserver = new RemoteCallback( 178 new RemoteCallback.OnResultListener() { 179 @Override 180 public void onResult(@Nullable Bundle result) { 181 mShellExecutor.execute(() -> { 182 if (mBackGestureStarted && result != null && result.getBoolean( 183 BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED)) { 184 // Host app won't able to process motion event anymore, so pilfer 185 // pointers anyway. 186 if (mBackNavigationInfo != null) { 187 mBackNavigationInfo.disableAppProgressGenerationAllowed(); 188 } 189 tryPilferPointers(); 190 return; 191 } 192 if (!mBackGestureStarted || mPostCommitAnimationInProgress) { 193 // If an uninterruptible animation is already in progress, we should 194 // ignore this due to it may cause focus lost. (alpha = 0) 195 return; 196 } 197 ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone."); 198 setTriggerBack(false); 199 // Trigger close transition if necessary. 200 mBackTransitionHandler.onAnimationFinished(); 201 resetTouchTracker(); 202 // Don't wait for animation start 203 mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); 204 }); 205 } 206 }); 207 208 private final BackAnimationBackground mAnimationBackground; 209 private StatusBarCustomizer mCustomizer; 210 private boolean mTrackingLatency; 211 212 // Keep previous navigation type before remove mBackNavigationInfo. 213 @BackNavigationInfo.BackTargetType 214 private int mPreviousNavigationType; 215 private Runnable mPilferPointerCallback; 216 private BackAnimation.TopUiRequest mRequestTopUiCallback; 217 218 private final IBackAnimationHandoffHandler mHandoffHandler = 219 new IBackAnimationHandoffHandler.Stub() { 220 @Override 221 public void handOffAnimation( 222 RemoteAnimationTarget[] targets, WindowAnimationState[] states) { 223 mBackTransitionHandler.handOffAnimation(targets, states); 224 } 225 }; 226 BackAnimationController( @onNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, Context context, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, Transitions transitions, @ShellMainThread Handler handler)227 public BackAnimationController( 228 @NonNull ShellInit shellInit, 229 @NonNull ShellController shellController, 230 @NonNull @ShellMainThread ShellExecutor shellExecutor, 231 Context context, 232 @NonNull BackAnimationBackground backAnimationBackground, 233 ShellBackAnimationRegistry shellBackAnimationRegistry, 234 ShellCommandHandler shellCommandHandler, 235 Transitions transitions, 236 @ShellMainThread Handler handler) { 237 this( 238 shellInit, 239 shellController, 240 shellExecutor, 241 ActivityTaskManager.getService(), 242 context, 243 backAnimationBackground, 244 shellBackAnimationRegistry, 245 shellCommandHandler, 246 transitions, 247 handler); 248 } 249 250 @VisibleForTesting BackAnimationController( @onNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull IActivityTaskManager activityTaskManager, Context context, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, Transitions transitions, @NonNull @ShellMainThread Handler handler)251 BackAnimationController( 252 @NonNull ShellInit shellInit, 253 @NonNull ShellController shellController, 254 @NonNull @ShellMainThread ShellExecutor shellExecutor, 255 @NonNull IActivityTaskManager activityTaskManager, 256 Context context, 257 @NonNull BackAnimationBackground backAnimationBackground, 258 ShellBackAnimationRegistry shellBackAnimationRegistry, 259 ShellCommandHandler shellCommandHandler, 260 Transitions transitions, 261 @NonNull @ShellMainThread Handler handler) { 262 mShellController = shellController; 263 mShellExecutor = shellExecutor; 264 mActivityTaskManager = activityTaskManager; 265 mContext = context; 266 mRequirePointerPilfer = 267 context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer); 268 shellInit.addInitCallback(this::onInit, this); 269 mAnimationBackground = backAnimationBackground; 270 mShellBackAnimationRegistry = shellBackAnimationRegistry; 271 mLatencyTracker = LatencyTracker.getInstance(mContext); 272 mShellCommandHandler = shellCommandHandler; 273 mWindowManager = context.getSystemService(WindowManager.class); 274 mTransitions = transitions; 275 mBackTransitionHandler = new BackTransitionHandler(mTransitions); 276 mTransitions.addHandler(mBackTransitionHandler); 277 mHandler = handler; 278 mTransitions.registerObserver(mBackTransitionObserver); 279 mBackTransitionObserver.setBackTransitionHandler(mBackTransitionHandler); 280 updateTouchableArea(); 281 } 282 onInit()283 private void onInit() { 284 createAdapter(); 285 mShellController.addExternalInterface(IBackAnimation.DESCRIPTOR, 286 this::createExternalInterface, this); 287 mShellCommandHandler.addDumpCallback(this::dump, this); 288 mShellController.addConfigurationChangeListener(this); 289 registerBackGestureDelegate(); 290 } 291 getBackAnimationImpl()292 public BackAnimation getBackAnimationImpl() { 293 return mBackAnimation; 294 } 295 createExternalInterface()296 private ExternalInterfaceBinder createExternalInterface() { 297 return new IBackAnimationImpl(this); 298 } 299 300 private final BackAnimationImpl mBackAnimation = new BackAnimationImpl(); 301 302 @Override onConfigurationChanged(Configuration newConfig)303 public void onConfigurationChanged(Configuration newConfig) { 304 mShellBackAnimationRegistry.onConfigurationChanged(newConfig); 305 updateTouchableArea(); 306 } 307 updateTouchableArea()308 private void updateTouchableArea() { 309 mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds()); 310 } 311 312 @Override getContext()313 public Context getContext() { 314 return mContext; 315 } 316 317 @Override getRemoteCallExecutor()318 public ShellExecutor getRemoteCallExecutor() { 319 return mShellExecutor; 320 } 321 322 private class BackAnimationImpl implements BackAnimation { 323 @Override onBackMotion( float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge )324 public void onBackMotion( 325 float touchX, 326 float touchY, 327 int keyAction, 328 @BackEvent.SwipeEdge int swipeEdge 329 ) { 330 mShellExecutor.execute( 331 () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge)); 332 } 333 334 @Override onThresholdCrossed()335 public void onThresholdCrossed() { 336 if (predictiveBackDelayWmTransition()) { 337 mShellExecutor.execute(BackAnimationController.this::onThresholdCrossed); 338 } else { 339 BackAnimationController.this.onThresholdCrossed(); 340 } 341 } 342 343 @Override setTriggerBack(boolean triggerBack)344 public void setTriggerBack(boolean triggerBack) { 345 mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack)); 346 } 347 348 @Override setSwipeThresholds( float linearDistance, float maxDistance, float nonLinearFactor)349 public void setSwipeThresholds( 350 float linearDistance, 351 float maxDistance, 352 float nonLinearFactor) { 353 mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds( 354 linearDistance, maxDistance, nonLinearFactor)); 355 } 356 357 @Override setStatusBarCustomizer(StatusBarCustomizer customizer)358 public void setStatusBarCustomizer(StatusBarCustomizer customizer) { 359 mCustomizer = customizer; 360 mAnimationBackground.setStatusBarCustomizer(customizer); 361 } 362 363 @Override setPilferPointerCallback(Runnable callback)364 public void setPilferPointerCallback(Runnable callback) { 365 mShellExecutor.execute(() -> { 366 mPilferPointerCallback = callback; 367 }); 368 } 369 370 @Override setTopUiRequestCallback(TopUiRequest topUiRequest)371 public void setTopUiRequestCallback(TopUiRequest topUiRequest) { 372 mShellExecutor.execute(() -> mRequestTopUiCallback = topUiRequest); 373 } 374 } 375 376 private class IBackAnimationImpl extends IBackAnimation.Stub 377 implements ExternalInterfaceBinder { 378 private BackAnimationController mController; 379 IBackAnimationImpl(BackAnimationController controller)380 IBackAnimationImpl(BackAnimationController controller) { 381 mController = controller; 382 } 383 384 @Override setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner)385 public void setBackToLauncherCallback(IOnBackInvokedCallback callback, 386 IRemoteAnimationRunner runner) { 387 executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback", 388 (controller) -> controller.registerAnimation( 389 BackNavigationInfo.TYPE_RETURN_TO_HOME, 390 new BackAnimationRunner( 391 callback, 392 runner, 393 controller.mContext, 394 CUJ_PREDICTIVE_BACK_HOME, 395 mHandler))); 396 } 397 398 @Override clearBackToLauncherCallback()399 public void clearBackToLauncherCallback() { 400 executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback", 401 (controller) -> controller.unregisterAnimation( 402 BackNavigationInfo.TYPE_RETURN_TO_HOME)); 403 } 404 customizeStatusBarAppearance(AppearanceRegion appearance)405 public void customizeStatusBarAppearance(AppearanceRegion appearance) { 406 executeRemoteCallWithTaskPermission(mController, "useLauncherSysBarFlags", 407 (controller) -> controller.customizeStatusBarAppearance(appearance)); 408 } 409 410 @Override invalidate()411 public void invalidate() { 412 mController = null; 413 } 414 } 415 customizeStatusBarAppearance(AppearanceRegion appearance)416 private void customizeStatusBarAppearance(AppearanceRegion appearance) { 417 if (mCustomizer != null) { 418 mCustomizer.customizeStatusBarAppearance(appearance); 419 } 420 } 421 registerAnimation(@ackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner)422 void registerAnimation(@BackNavigationInfo.BackTargetType int type, 423 @NonNull BackAnimationRunner runner) { 424 mShellBackAnimationRegistry.registerAnimation(type, runner); 425 } 426 unregisterAnimation(@ackNavigationInfo.BackTargetType int type)427 void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) { 428 mShellBackAnimationRegistry.unregisterAnimation(type); 429 } 430 getActiveTracker()431 private BackTouchTracker getActiveTracker() { 432 if (mCurrentTracker.isActive()) return mCurrentTracker; 433 if (mQueuedTracker.isActive()) return mQueuedTracker; 434 return null; 435 } 436 437 @VisibleForTesting onThresholdCrossed()438 public void onThresholdCrossed() { 439 mThresholdCrossed = true; 440 BackTouchTracker activeTracker = getActiveTracker(); 441 if (predictiveBackDelayWmTransition() && activeTracker != null && mActiveCallback == null 442 && mBackGestureStarted) { 443 startBackNavigation(activeTracker); 444 } 445 // There was no focus window when calling startBackNavigation, still pilfer pointers so 446 // the next focus window won't receive motion events. 447 if (mBackNavigationInfo == null && mReceivedNullNavigationInfo) { 448 tryPilferPointers(); 449 return; 450 } 451 // Dispatch onBackStarted, only to app callbacks. 452 // System callbacks will receive onBackStarted when the remote animation starts. 453 final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(); 454 if (!shouldDispatchToAnimator && mActiveCallback != null) { 455 mCurrentTracker.updateStartLocation(); 456 tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null)); 457 if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) { 458 tryPilferPointers(); 459 } 460 } else if (shouldDispatchToAnimator) { 461 tryPilferPointers(); 462 } 463 } 464 isAppProgressGenerationAllowed()465 private boolean isAppProgressGenerationAllowed() { 466 return mBackNavigationInfo.isAppProgressGenerationAllowed() 467 && mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea); 468 } 469 470 /** 471 * Called when a new motion event needs to be transferred to this 472 * {@link BackAnimationController} 473 */ onMotionEvent( float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge)474 public void onMotionEvent( 475 float touchX, 476 float touchY, 477 int keyAction, 478 @BackEvent.SwipeEdge int swipeEdge) { 479 480 BackTouchTracker activeTouchTracker = getActiveTracker(); 481 if (activeTouchTracker != null) { 482 activeTouchTracker.update(touchX, touchY); 483 } 484 485 // two gestures are waiting to be processed at the moment, skip any further user touches 486 if (mCurrentTracker.isFinished() && mQueuedTracker.isFinished()) { 487 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 488 "Ignoring MotionEvent because two gestures are already being queued."); 489 return; 490 } else if (mBackGestureStarted && mCurrentTracker.isInInitialState() 491 && mQueuedTracker.isInInitialState()) { 492 ProtoLog.e(WM_SHELL_BACK_PREVIEW, 493 "Both touch trackers in initial state and mBackGestureStarted=true"); 494 mBackGestureStarted = false; 495 } 496 497 if (keyAction == MotionEvent.ACTION_DOWN) { 498 if (!mBackGestureStarted) { 499 if (swipeEdge == EDGE_NONE) { 500 // start animation immediately for non-gestural sources (without ACTION_MOVE 501 // events) 502 if (!predictiveBackDelayWmTransition()) { 503 mThresholdCrossed = true; 504 } 505 mPointersPilfered = true; 506 onGestureStarted(touchX, touchY, swipeEdge); 507 if (predictiveBackDelayWmTransition()) { 508 onThresholdCrossed(); 509 } 510 mShouldStartOnNextMoveEvent = false; 511 } else { 512 if (predictiveBackDelayWmTransition()) { 513 onGestureStarted(touchX, touchY, swipeEdge); 514 } else { 515 mShouldStartOnNextMoveEvent = true; 516 } 517 } 518 } 519 } else if (keyAction == MotionEvent.ACTION_MOVE) { 520 if (!mBackGestureStarted && mShouldStartOnNextMoveEvent) { 521 // Let the animation initialized here to make sure the onPointerDownOutsideFocus 522 // could be happened when ACTION_DOWN, it may change the current focus that we 523 // would access it when startBackNavigation. 524 onGestureStarted(touchX, touchY, swipeEdge); 525 mShouldStartOnNextMoveEvent = false; 526 } 527 onMove(); 528 } else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) { 529 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 530 "Finishing gesture with event action: %d", keyAction); 531 if (keyAction == MotionEvent.ACTION_CANCEL) { 532 setTriggerBack(false); 533 } 534 onGestureFinished(); 535 } 536 } 537 onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge)538 private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) { 539 boolean interruptCancelPostCommitAnimation = mPostCommitAnimationInProgress 540 && mCurrentTracker.isFinished() && !mCurrentTracker.getTriggerBack() 541 && mQueuedTracker.isInInitialState(); 542 if (interruptCancelPostCommitAnimation) { 543 // If a system animation is currently in the post-commit phase animating an 544 // onBackCancelled event, let's interrupt it and start animating a new back gesture 545 resetTouchTracker(); 546 } 547 BackTouchTracker touchTracker; 548 if (mCurrentTracker.isInInitialState()) { 549 touchTracker = mCurrentTracker; 550 } else if (mQueuedTracker.isInInitialState()) { 551 touchTracker = mQueuedTracker; 552 } else { 553 ProtoLog.w(WM_SHELL_BACK_PREVIEW, 554 "Cannot start tracking new gesture with neither tracker in initial state."); 555 return; 556 } 557 touchTracker.setGestureStartLocation(touchX, touchY, swipeEdge); 558 touchTracker.setState(BackTouchTracker.TouchTrackerState.ACTIVE); 559 mBackGestureStarted = true; 560 561 if (interruptCancelPostCommitAnimation) { 562 // post-commit cancel is currently running. let's interrupt it and dispatch a new 563 // onBackStarted event. 564 mPostCommitAnimationInProgress = false; 565 mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); 566 startSystemAnimation(); 567 } else if (!predictiveBackDelayWmTransition()) { 568 startBackNavigation(touchTracker); 569 } 570 } 571 startBackNavigation(@onNull BackTouchTracker touchTracker)572 private void startBackNavigation(@NonNull BackTouchTracker touchTracker) { 573 if (touchTracker != mCurrentTracker) { 574 // Only start the back navigation if no other gesture is being processed. Otherwise, 575 // the back navigation will fall back to legacy back event injection. 576 return; 577 } 578 try { 579 startLatencyTracking(); 580 if (mBackAnimationAdapter != null 581 && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) { 582 mBackAnimationAdapter.updateSupportedAnimators( 583 mShellBackAnimationRegistry.getSupportedAnimators()); 584 } 585 mBackNavigationInfo = mActivityTaskManager.startBackNavigation( 586 mNavigationObserver, mBackAnimationAdapter); 587 onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker); 588 } catch (RemoteException remoteException) { 589 Log.e(TAG, "Failed to initAnimation", remoteException); 590 finishBackNavigation(touchTracker.getTriggerBack()); 591 } 592 } 593 onBackNavigationInfoReceived(@ullable BackNavigationInfo backNavigationInfo, @NonNull BackTouchTracker touchTracker)594 private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo, 595 @NonNull BackTouchTracker touchTracker) { 596 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo); 597 if (backNavigationInfo == null) { 598 ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null."); 599 mReceivedNullNavigationInfo = true; 600 cancelLatencyTracking(); 601 tryPilferPointers(); 602 return; 603 } 604 final int backType = backNavigationInfo.getType(); 605 final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(); 606 if (shouldDispatchToAnimator) { 607 if (!mShellBackAnimationRegistry.startGesture(backType)) { 608 mActiveCallback = null; 609 } 610 requestTopUi(true, backType); 611 tryPilferPointers(); 612 } else { 613 mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback(); 614 // App is handling back animation. Cancel system animation latency tracking. 615 cancelLatencyTracking(); 616 tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); 617 if (!isAppProgressGenerationAllowed()) { 618 tryPilferPointers(); 619 } 620 } 621 } 622 onMove()623 private void onMove() { 624 if (!mBackGestureStarted 625 || mBackNavigationInfo == null 626 || mActiveCallback == null 627 || !mOnBackStartDispatched) { 628 return; 629 } 630 // Skip dispatching if the move corresponds to the queued instead of the current gesture 631 if (mQueuedTracker.isActive()) return; 632 final BackMotionEvent backEvent = mCurrentTracker.createProgressEvent(); 633 dispatchOnBackProgressed(mActiveCallback, backEvent); 634 } 635 injectBackKey()636 private void injectBackKey() { 637 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "injectBackKey"); 638 sendBackEvent(KeyEvent.ACTION_DOWN); 639 sendBackEvent(KeyEvent.ACTION_UP); 640 } 641 642 @SuppressLint("MissingPermission") sendBackEvent(int action)643 private void sendBackEvent(int action) { 644 final long when = SystemClock.uptimeMillis(); 645 final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */, 646 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, 647 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 648 InputDevice.SOURCE_KEYBOARD); 649 650 ev.setDisplayId(mContext.getDisplay().getDisplayId()); 651 if (!mContext.getSystemService(InputManager.class) 652 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) { 653 ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Inject input event fail"); 654 } 655 } 656 shouldDispatchToAnimator()657 private boolean shouldDispatchToAnimator() { 658 return mBackNavigationInfo != null && mBackNavigationInfo.isPrepareRemoteAnimation(); 659 } 660 tryPilferPointers()661 private void tryPilferPointers() { 662 if (mPointersPilfered || !mThresholdCrossed) { 663 return; 664 } 665 if (mPilferPointerCallback != null) { 666 mPilferPointerCallback.run(); 667 } 668 mPointersPilfered = true; 669 } 670 tryDispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent)671 private void tryDispatchOnBackStarted( 672 IOnBackInvokedCallback callback, 673 BackMotionEvent backEvent) { 674 if (mOnBackStartDispatched 675 || callback == null 676 || (!mThresholdCrossed && mRequirePointerPilfer)) { 677 return; 678 } 679 dispatchOnBackStarted(callback, backEvent); 680 } 681 dispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent)682 private void dispatchOnBackStarted( 683 IOnBackInvokedCallback callback, 684 BackMotionEvent backEvent) { 685 if (callback == null) { 686 return; 687 } 688 try { 689 callback.onBackStarted(backEvent); 690 if (mBackTransitionHandler.canHandOffAnimation()) { 691 callback.setHandoffHandler(mHandoffHandler); 692 } else { 693 callback.setHandoffHandler(null); 694 } 695 mOnBackStartDispatched = true; 696 } catch (RemoteException e) { 697 Log.e(TAG, "dispatchOnBackStarted error: ", e); 698 } 699 } 700 dispatchOnBackInvoked(IOnBackInvokedCallback callback)701 private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) { 702 if (callback == null) { 703 return; 704 } 705 try { 706 callback.onBackInvoked(); 707 } catch (RemoteException e) { 708 Log.e(TAG, "dispatchOnBackInvoked error: ", e); 709 } 710 } 711 tryDispatchOnBackCancelled(IOnBackInvokedCallback callback)712 private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) { 713 if (!mOnBackStartDispatched) { 714 Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched."); 715 return; 716 } 717 if (callback == null) { 718 return; 719 } 720 try { 721 callback.onBackCancelled(); 722 } catch (RemoteException e) { 723 Log.e(TAG, "dispatchOnBackCancelled error: ", e); 724 } 725 } 726 dispatchOnBackProgressed(IOnBackInvokedCallback callback, BackMotionEvent backEvent)727 private void dispatchOnBackProgressed(IOnBackInvokedCallback callback, 728 BackMotionEvent backEvent) { 729 if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null 730 && isAppProgressGenerationAllowed())) { 731 return; 732 } 733 try { 734 callback.onBackProgressed(backEvent); 735 } catch (RemoteException e) { 736 Log.e(TAG, "dispatchOnBackProgressed error: ", e); 737 } 738 } 739 740 /** 741 * @return Latest task id which back gesture has occurred on it. 742 */ getLatestTriggerBackTask()743 public int getLatestTriggerBackTask() { 744 return mBackTransitionObserver.mFocusedTaskId; 745 } 746 747 /** 748 * Sets to true when the back gesture has passed the triggering threshold, false otherwise. 749 */ setTriggerBack(boolean triggerBack)750 public void setTriggerBack(boolean triggerBack) { 751 if (mActiveCallback != null) { 752 try { 753 mActiveCallback.setTriggerBack(triggerBack); 754 } catch (RemoteException e) { 755 Log.e(TAG, "remote setTriggerBack error: ", e); 756 } 757 } 758 BackTouchTracker activeBackGestureInfo = getActiveTracker(); 759 if (activeBackGestureInfo != null) { 760 activeBackGestureInfo.setTriggerBack(triggerBack); 761 } 762 } 763 setSwipeThresholds( float linearDistance, float maxDistance, float nonLinearFactor)764 private void setSwipeThresholds( 765 float linearDistance, 766 float maxDistance, 767 float nonLinearFactor) { 768 mCurrentTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor); 769 mQueuedTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor); 770 } 771 invokeOrCancelBack(@onNull BackTouchTracker touchTracker)772 private void invokeOrCancelBack(@NonNull BackTouchTracker touchTracker) { 773 // Make a synchronized call to core before dispatch back event to client side. 774 // If the close transition happens before the core receives onAnimationFinished, there will 775 // play a second close animation for that transition. 776 if (mBackAnimationFinishedCallback != null) { 777 try { 778 mBackAnimationFinishedCallback.onAnimationFinished(touchTracker.getTriggerBack()); 779 } catch (RemoteException e) { 780 Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e); 781 } 782 mBackAnimationFinishedCallback = null; 783 } 784 785 if (mBackNavigationInfo != null && !mRealCallbackInvoked) { 786 final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback(); 787 if (touchTracker.getTriggerBack()) { 788 dispatchOnBackInvoked(callback); 789 } else { 790 tryDispatchOnBackCancelled(callback); 791 } 792 } 793 mRealCallbackInvoked = false; 794 finishBackNavigation(touchTracker.getTriggerBack()); 795 } 796 797 /** 798 * Called when the gesture is released, then it could start the post commit animation. 799 */ onGestureFinished()800 private void onGestureFinished() { 801 BackTouchTracker activeTouchTracker = getActiveTracker(); 802 if (!mBackGestureStarted || activeTouchTracker == null) { 803 // This can happen when an unfinished gesture has been reset in resetTouchTracker 804 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 805 "onGestureFinished called while no gesture is started"); 806 return; 807 } 808 boolean triggerBack = activeTouchTracker.getTriggerBack(); 809 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", triggerBack); 810 811 if (triggerBack) { 812 mBackTransitionObserver.update(mBackNavigationInfo != null 813 ? mBackNavigationInfo.getFocusedTaskId() 814 : INVALID_TASK_ID); 815 } 816 // Reset gesture states. 817 mThresholdCrossed = false; 818 mPointersPilfered = false; 819 mBackGestureStarted = false; 820 activeTouchTracker.setState(BackTouchTracker.TouchTrackerState.FINISHED); 821 822 if (mPostCommitAnimationInProgress) { 823 ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running"); 824 return; 825 } 826 827 if (mBackNavigationInfo == null) { 828 // No focus window found or core are running recents animation, inject back key as 829 // legacy behavior, or new back gesture was started while previous has not finished yet 830 if (!mQueuedTracker.isInInitialState()) { 831 ProtoLog.e(WM_SHELL_BACK_PREVIEW, "mBackNavigationInfo is null AND there is " 832 + "another back animation in progress"); 833 } 834 mCurrentTracker.reset(); 835 if (triggerBack) { 836 injectBackKey(); 837 } 838 finishBackNavigation(triggerBack); 839 return; 840 } 841 842 final int backType = mBackNavigationInfo.getType(); 843 // Simply trigger and finish back navigation when no animator defined. 844 if (!shouldDispatchToAnimator() 845 || mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) { 846 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Trigger back without dispatching to animator."); 847 invokeOrCancelBack(mCurrentTracker); 848 mCurrentTracker.reset(); 849 return; 850 } else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) { 851 ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready."); 852 // Supposed it is in post commit animation state, and start the timeout to watch 853 // if the animation is ready. 854 mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION); 855 return; 856 } 857 startPostCommitAnimation(); 858 } 859 860 /** 861 * Start the phase 2 animation when gesture is released. 862 * Callback to {@link #onBackAnimationFinished} when it is finished or timeout. 863 */ startPostCommitAnimation()864 private void startPostCommitAnimation() { 865 if (mPostCommitAnimationInProgress) { 866 return; 867 } 868 869 mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); 870 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()"); 871 mPostCommitAnimationInProgress = true; 872 mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION); 873 874 // The next callback should be {@link #onBackAnimationFinished}. 875 if (mCurrentTracker.getTriggerBack()) { 876 // notify core gesture is commit 877 if (shouldTriggerCloseTransition()) { 878 mBackTransitionHandler.mCloseTransitionRequested = true; 879 final IOnBackInvokedCallback callback = 880 mBackNavigationInfo.getOnBackInvokedCallback(); 881 // invoked client side onBackInvoked 882 dispatchOnBackInvoked(callback); 883 mRealCallbackInvoked = true; 884 } 885 // start post animation 886 dispatchOnBackInvoked(mActiveCallback); 887 } else { 888 tryDispatchOnBackCancelled(mActiveCallback); 889 } 890 } 891 892 // Close window won't create any transition shouldTriggerCloseTransition()893 private boolean shouldTriggerCloseTransition() { 894 if (mBackNavigationInfo == null) { 895 return false; 896 } 897 int type = mBackNavigationInfo.getType(); 898 return type == BackNavigationInfo.TYPE_RETURN_TO_HOME 899 || type == BackNavigationInfo.TYPE_CROSS_TASK 900 || type == BackNavigationInfo.TYPE_CROSS_ACTIVITY; 901 } 902 /** 903 * Called when the post commit animation is completed or timeout. 904 * This will trigger the real {@link IOnBackInvokedCallback} behavior. 905 */ 906 @VisibleForTesting onBackAnimationFinished()907 void onBackAnimationFinished() { 908 if (!mPostCommitAnimationInProgress) { 909 // This can happen when a post-commit cancel animation was interrupted by a new back 910 // gesture but the timing of interruption was bad such that the back-callback 911 // implementation finished in between the time of the new gesture having started and 912 // the time of the back-callback receiving the new onBackStarted event. Due to the 913 // asynchronous APIs this isn't an unlikely case. To handle this, let's return early. 914 // The back-callback implementation will call onBackAnimationFinished again when it is 915 // done with animating the second gesture. 916 return; 917 } 918 finishBackAnimation(); 919 } 920 finishBackAnimation()921 private void finishBackAnimation() { 922 // Stop timeout runner. 923 mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable); 924 mPostCommitAnimationInProgress = false; 925 926 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()"); 927 928 if (mCurrentTracker.isActive() || mCurrentTracker.isFinished()) { 929 // Trigger the real back. 930 invokeOrCancelBack(mCurrentTracker); 931 } else { 932 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 933 "mCurrentBackGestureInfo was null when back animation finished"); 934 } 935 resetTouchTracker(); 936 mBackTransitionHandler.onAnimationFinished(); 937 } 938 939 /** 940 * Resets the BackTouchTracker and potentially starts a new back navigation in case one 941 * is queued. 942 */ resetTouchTracker()943 private void resetTouchTracker() { 944 BackTouchTracker temp = mCurrentTracker; 945 mCurrentTracker = mQueuedTracker; 946 temp.reset(); 947 mQueuedTracker = temp; 948 949 if (mCurrentTracker.isInInitialState()) { 950 if (mBackGestureStarted) { 951 mBackGestureStarted = false; 952 tryDispatchOnBackCancelled(mActiveCallback); 953 finishBackNavigation(false); 954 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 955 "resetTouchTracker -> reset an unfinished gesture"); 956 } else { 957 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> no queued gesture"); 958 } 959 return; 960 } 961 962 if (mCurrentTracker.isFinished() && mCurrentTracker.getTriggerBack()) { 963 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> start queued back navigation " 964 + "AND post commit animation"); 965 injectBackKey(); 966 finishBackNavigation(true); 967 mCurrentTracker.reset(); 968 } else if (!mCurrentTracker.isFinished()) { 969 ProtoLog.d(WM_SHELL_BACK_PREVIEW, 970 "resetTouchTracker -> queued gesture not finished; do nothing"); 971 } else { 972 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> reset queued gesture"); 973 mCurrentTracker.reset(); 974 } 975 } 976 977 /** 978 * This should be called after the whole back navigation is completed. 979 */ 980 @VisibleForTesting finishBackNavigation(boolean triggerBack)981 void finishBackNavigation(boolean triggerBack) { 982 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); 983 mActiveCallback = null; 984 mApps = null; 985 mOnBackStartDispatched = false; 986 mThresholdCrossed = false; 987 mPointersPilfered = false; 988 mShellBackAnimationRegistry.resetDefaultCrossActivity(); 989 cancelLatencyTracking(); 990 mReceivedNullNavigationInfo = false; 991 if (mBackNavigationInfo != null) { 992 mPreviousNavigationType = mBackNavigationInfo.getType(); 993 mBackNavigationInfo.onBackNavigationFinished(triggerBack); 994 mBackNavigationInfo = null; 995 requestTopUi(false, mPreviousNavigationType); 996 } 997 } 998 startLatencyTracking()999 private void startLatencyTracking() { 1000 if (mTrackingLatency) { 1001 cancelLatencyTracking(); 1002 } 1003 mLatencyTracker.onActionStart(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); 1004 mTrackingLatency = true; 1005 } 1006 cancelLatencyTracking()1007 private void cancelLatencyTracking() { 1008 if (!mTrackingLatency) { 1009 return; 1010 } 1011 mLatencyTracker.onActionCancel(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); 1012 mTrackingLatency = false; 1013 } 1014 endLatencyTracking()1015 private void endLatencyTracking() { 1016 if (!mTrackingLatency) { 1017 return; 1018 } 1019 mLatencyTracker.onActionEnd(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); 1020 mTrackingLatency = false; 1021 } 1022 startSystemAnimation()1023 private void startSystemAnimation() { 1024 if (mBackNavigationInfo == null) { 1025 ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Lack of navigation info to start animation."); 1026 return; 1027 } 1028 if (!validateAnimationTargets(mApps)) { 1029 ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Not starting animation due to mApps being null."); 1030 return; 1031 } 1032 1033 final BackAnimationRunner runner = 1034 mShellBackAnimationRegistry.getAnimationRunnerAndInit(mBackNavigationInfo); 1035 if (runner == null) { 1036 if (mBackAnimationFinishedCallback != null) { 1037 try { 1038 mBackAnimationFinishedCallback.onAnimationFinished(false); 1039 } catch (RemoteException e) { 1040 Log.w(TAG, "Failed call IBackNaviAnimationController", e); 1041 } 1042 } 1043 return; 1044 } 1045 mActiveCallback = runner.getCallback(); 1046 1047 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()"); 1048 1049 runner.startAnimation(mApps, /*wallpapers*/ null, /*nonApps*/ null, 1050 () -> mShellExecutor.execute(this::onBackAnimationFinished)); 1051 1052 if (mApps.length >= 1) { 1053 BackMotionEvent startEvent = mCurrentTracker.createStartEvent( 1054 Flags.removeDepartTargetFromMotion() ? null : mApps[0]); 1055 dispatchOnBackStarted(mActiveCallback, startEvent); 1056 if (startEvent.getSwipeEdge() == EDGE_NONE) { 1057 // TODO(b/373544911): onBackStarted is dispatched here so that 1058 // WindowOnBackInvokedDispatcher knows about the back navigation and intercepts 1059 // touch events while it's active. It would be cleaner and safer to disable 1060 // multitouch altogether (same as in gesture-nav). 1061 dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent); 1062 } 1063 } 1064 } 1065 requestTopUi(boolean hasTopUi, int backType)1066 private void requestTopUi(boolean hasTopUi, int backType) { 1067 if (mRequestTopUiCallback != null && (backType == BackNavigationInfo.TYPE_CROSS_TASK 1068 || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY)) { 1069 mRequestTopUiCallback.requestTopUi(hasTopUi, TAG); 1070 } 1071 } 1072 1073 /** 1074 * Validate animation targets. 1075 */ validateAnimationTargets(RemoteAnimationTarget[] apps)1076 static boolean validateAnimationTargets(RemoteAnimationTarget[] apps) { 1077 if (apps == null || apps.length == 0) { 1078 return false; 1079 } 1080 for (int i = apps.length - 1; i >= 0; --i) { 1081 if (!apps[i].leash.isValid()) { 1082 return false; 1083 } 1084 } 1085 return true; 1086 } 1087 kickStartAnimation()1088 private void kickStartAnimation() { 1089 startSystemAnimation(); 1090 1091 // Dispatch the first progress after animation start for 1092 // smoothing the initial animation, instead of waiting for next 1093 // onMove. 1094 final BackMotionEvent backFinish = mCurrentTracker 1095 .createProgressEvent(); 1096 dispatchOnBackProgressed(mActiveCallback, backFinish); 1097 if (mCurrentTracker.isFinished()) { 1098 // if the down -> up gesture happened before animation 1099 // start, we have to trigger the uninterruptible transition 1100 // to finish the back animation. 1101 startPostCommitAnimation(); 1102 } 1103 } 1104 createAdapter()1105 private void createAdapter() { 1106 IBackAnimationRunner runner = 1107 new IBackAnimationRunner.Stub() { 1108 @Override 1109 public void onAnimationStart( 1110 RemoteAnimationTarget[] apps, 1111 IBinder token, 1112 IBackAnimationFinishedCallback finishedCallback) { 1113 mShellExecutor.execute( 1114 () -> { 1115 endLatencyTracking(); 1116 if (!validateAnimationTargets(apps)) { 1117 Log.e(TAG, "Invalid animation targets!"); 1118 return; 1119 } 1120 mBackAnimationFinishedCallback = finishedCallback; 1121 mApps = apps; 1122 // app only visible after transition ready, break for now. 1123 if (token != null) { 1124 return; 1125 } 1126 kickStartAnimation(); 1127 }); 1128 } 1129 1130 @Override 1131 public void onAnimationCancelled() { 1132 mShellExecutor.execute( 1133 () -> { 1134 if (!mShellBackAnimationRegistry.cancel( 1135 mBackNavigationInfo != null 1136 ? mBackNavigationInfo.getType() 1137 : mPreviousNavigationType)) { 1138 return; 1139 } 1140 if (!mBackGestureStarted) { 1141 invokeOrCancelBack(mCurrentTracker); 1142 } 1143 }); 1144 } 1145 }; 1146 mBackAnimationAdapter = new BackAnimationAdapter(runner); 1147 } 1148 registerBackGestureDelegate()1149 private void registerBackGestureDelegate() { 1150 if (!Flags.delegateBackGestureToShell()) { 1151 return; 1152 } 1153 final RemoteCallback requestBackMonitor = new RemoteCallback( 1154 new RemoteCallback.OnResultListener() { 1155 @Override 1156 public void onResult(@Nullable Bundle result) { 1157 mShellExecutor.execute(() -> { 1158 if (mBackGestureStarted) { 1159 Log.w(TAG, "Back gesture is running, ignore request"); 1160 return; 1161 } 1162 onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE); 1163 setTriggerBack(true); 1164 onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE); 1165 }); 1166 } 1167 }); 1168 try { 1169 mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor); 1170 } catch (RemoteException remoteException) { 1171 Log.w(TAG, "Failed register back gesture request ", remoteException); 1172 } 1173 } 1174 1175 /** 1176 * Description of current BackAnimationController state. 1177 */ dump(PrintWriter pw, String prefix)1178 private void dump(PrintWriter pw, String prefix) { 1179 pw.println(prefix + "BackAnimationController state:"); 1180 pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted); 1181 pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress); 1182 pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent); 1183 pw.println(prefix + " mPointerPilfered=" + mThresholdCrossed); 1184 pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer); 1185 pw.println(prefix + " mCurrentTracker state:"); 1186 mCurrentTracker.dump(pw, prefix + " "); 1187 pw.println(prefix + " mQueuedTracker state:"); 1188 mQueuedTracker.dump(pw, prefix + " "); 1189 } 1190 1191 class BackTransitionHandler implements Transitions.TransitionHandler { 1192 private final Transitions mTransitions; 1193 1194 Runnable mOnAnimationFinishCallback; 1195 boolean mCloseTransitionRequested; 1196 SurfaceControl.Transaction mFinishOpenTransaction; 1197 Transitions.TransitionFinishCallback mFinishOpenTransitionCallback; 1198 // The Transition to make behindActivity become visible 1199 IBinder mPrepareOpenTransition; 1200 // The Transition to make behindActivity become invisible, if prepare open exist and 1201 // animation is canceled, start a close prepare transition to finish the whole transition. 1202 IBinder mClosePrepareTransition; 1203 TransitionInfo mOpenTransitionInfo; 1204 Transitions.TransitionHandler mTakeoverHandler; 1205 BackTransitionHandler(Transitions transitions)1206 BackTransitionHandler(Transitions transitions) { 1207 mTransitions = transitions; 1208 } 1209 onAnimationFinished()1210 void onAnimationFinished() { 1211 if (!mCloseTransitionRequested && mPrepareOpenTransition != null) { 1212 createClosePrepareTransition(); 1213 } 1214 if (mOnAnimationFinishCallback != null) { 1215 mOnAnimationFinishCallback.run(); 1216 mOnAnimationFinishCallback = null; 1217 } 1218 } 1219 applyFinishOpenTransition()1220 private void applyFinishOpenTransition() { 1221 if (mFinishOpenTransaction != null) { 1222 final SurfaceControl.Transaction t = mFinishOpenTransaction; 1223 t.apply(); 1224 } 1225 if (mFinishOpenTransitionCallback != null) { 1226 final Transitions.TransitionFinishCallback callback = mFinishOpenTransitionCallback; 1227 callback.onTransitionFinished(null); 1228 } 1229 cleanUpInternalState(); 1230 } 1231 cleanUpInternalState()1232 private void cleanUpInternalState() { 1233 mOpenTransitionInfo = null; 1234 mPrepareOpenTransition = null; 1235 mFinishOpenTransaction = null; 1236 mFinishOpenTransitionCallback = null; 1237 mTakeoverHandler = null; 1238 } 1239 applyAndFinish(@onNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1240 private void applyAndFinish(@NonNull SurfaceControl.Transaction st, 1241 @NonNull SurfaceControl.Transaction ft, 1242 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1243 applyFinishOpenTransition(); 1244 st.apply(); 1245 ft.apply(); 1246 finishCallback.onTransitionFinished(null); 1247 mCloseTransitionRequested = false; 1248 } 1249 1250 @Override startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1251 public boolean startAnimation(@NonNull IBinder transition, 1252 @NonNull TransitionInfo info, 1253 @NonNull SurfaceControl.Transaction st, 1254 @NonNull SurfaceControl.Transaction ft, 1255 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1256 // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't 1257 // need to post to ShellExecutor when called. 1258 if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) { 1259 // only consume it if this transition hasn't being processed. 1260 if (mClosePrepareTransition != null) { 1261 mClosePrepareTransition = null; 1262 applyAndFinish(st, ft, finishCallback); 1263 return true; 1264 } 1265 return false; 1266 } 1267 1268 if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION 1269 && isNotGestureBackTransition(info)) { 1270 return false; 1271 } 1272 1273 if (shouldCancelAnimation(info)) { 1274 mPrepareOpenTransition = null; 1275 return false; 1276 } 1277 1278 if (mApps == null || mApps.length == 0) { 1279 if (mCloseTransitionRequested) { 1280 // animation never start, consume directly 1281 applyAndFinish(st, ft, finishCallback); 1282 return true; 1283 } else if (mClosePrepareTransition == null 1284 && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) { 1285 // Gesture animation was cancelled before prepare transition ready, create 1286 // the close prepare transition 1287 createClosePrepareTransition(); 1288 } 1289 } 1290 1291 if (handlePrepareTransition(transition, info, st, ft, finishCallback)) { 1292 if (checkTakeoverFlags()) { 1293 mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info); 1294 } 1295 kickStartAnimation(); 1296 return true; 1297 } 1298 return handleCloseTransition(info, st, ft, finishCallback); 1299 } 1300 canHandOffAnimation()1301 private boolean canHandOffAnimation() { 1302 if (!checkTakeoverFlags()) { 1303 return false; 1304 } 1305 1306 return mTakeoverHandler != null; 1307 } 1308 handOffAnimation( RemoteAnimationTarget[] targets, WindowAnimationState[] states)1309 private void handOffAnimation( 1310 RemoteAnimationTarget[] targets, WindowAnimationState[] states) { 1311 if (!checkTakeoverFlags()) { 1312 ProtoLog.e(WM_SHELL_BACK_PREVIEW, 1313 "Trying to hand off the animation, but the required flags are disabled."); 1314 return; 1315 } else if (mTakeoverHandler == null) { 1316 ProtoLog.e(WM_SHELL_BACK_PREVIEW, 1317 "Missing takeover handler when trying to hand off animation."); 1318 return; 1319 } else if (targets.length != states.length) { 1320 ProtoLog.e(WM_SHELL_BACK_PREVIEW, 1321 "Targets passed for takeover don't match the window states."); 1322 return; 1323 } 1324 1325 // The states passed to this method are paired with the targets, but they need to be 1326 // paired with the changes inside the TransitionInfo. So for each change we find its 1327 // matching target, and leave the state for any change missing a matching target blank. 1328 WindowAnimationState[] updatedStates = 1329 new WindowAnimationState[mOpenTransitionInfo.getChanges().size()]; 1330 for (int i = 0; i < mOpenTransitionInfo.getChanges().size(); i++) { 1331 ActivityManager.RunningTaskInfo taskInfo = 1332 mOpenTransitionInfo.getChanges().get(i).getTaskInfo(); 1333 if (taskInfo == null) { 1334 continue; 1335 } 1336 1337 for (int j = 0; j < targets.length; j++) { 1338 if (taskInfo.taskId == targets[j].taskId) { 1339 updatedStates[i] = states[j]; 1340 break; 1341 } 1342 } 1343 } 1344 1345 mTakeoverHandler.takeOverAnimation( 1346 mPrepareOpenTransition, mOpenTransitionInfo, new SurfaceControl.Transaction(), 1347 mFinishOpenTransitionCallback, updatedStates); 1348 1349 cleanUpInternalState(); 1350 } 1351 1352 @Override onTransitionConsumed(@onNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishTransaction)1353 public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, 1354 @Nullable SurfaceControl.Transaction finishTransaction) { 1355 if (transition == mClosePrepareTransition && aborted) { 1356 mClosePrepareTransition = null; 1357 applyFinishOpenTransition(); 1358 } else if (!aborted && unifyBackNavigationTransition()) { 1359 // Since the closing target participates in the predictive back transition, the 1360 // merged transition must be applied with the first transition to ensure a seamless 1361 // animation. 1362 if (mFinishOpenTransaction != null && finishTransaction != null) { 1363 mFinishOpenTransaction.merge(finishTransaction); 1364 } 1365 } 1366 } 1367 createClosePrepareTransition()1368 void createClosePrepareTransition() { 1369 if (mClosePrepareTransition != null) { 1370 Log.e(TAG, "Re-create close prepare transition"); 1371 return; 1372 } 1373 final WindowContainerTransaction wct = new WindowContainerTransaction(); 1374 wct.restoreBackNavi(); 1375 mClosePrepareTransition = mTransitions.startTransition( 1376 TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, wct, mBackTransitionHandler); 1377 } mergePendingTransitions(TransitionInfo info)1378 private void mergePendingTransitions(TransitionInfo info) { 1379 if (mOpenTransitionInfo == null) { 1380 return; 1381 } 1382 // Copy initial changes to final transition 1383 final TransitionInfo init = mOpenTransitionInfo; 1384 // Find prepare open target 1385 boolean openShowWallpaper = false; 1386 final ArrayList<SurfaceControl> openSurfaces = new ArrayList<>(); 1387 int tmpSize; 1388 for (int j = init.getChanges().size() - 1; j >= 0; --j) { 1389 final TransitionInfo.Change change = init.getChanges().get(j); 1390 if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED) 1391 && TransitionUtil.isOpeningMode(change.getMode())) { 1392 final ComponentName openComponent = findComponentName(change); 1393 final int openTaskId = findTaskId(change); 1394 final WindowContainerToken openToken = findToken(change); 1395 if (openComponent == null && openTaskId == INVALID_TASK_ID 1396 && openToken == null) { 1397 continue; 1398 } 1399 openSurfaces.add(change.getLeash()); 1400 if (change.hasFlags(FLAG_SHOW_WALLPAPER)) { 1401 openShowWallpaper = true; 1402 } 1403 } 1404 } 1405 if (openSurfaces.isEmpty()) { 1406 // This shouldn't happen, but if that happen, consume the initial transition anyway. 1407 Log.e(TAG, "Unable to merge following transition, cannot find the gesture " 1408 + "animated target from the open transition=" + mOpenTransitionInfo); 1409 mOpenTransitionInfo = null; 1410 return; 1411 } 1412 // Find first non-prepare open target 1413 boolean isOpen = false; 1414 tmpSize = info.getChanges().size(); 1415 for (int j = 0; j < tmpSize; ++j) { 1416 final TransitionInfo.Change change = info.getChanges().get(j); 1417 if (isOpenSurfaceMatched(openSurfaces, change)) { 1418 // This is original close target, potential be close, but cannot determine 1419 // from it. 1420 if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { 1421 isOpen = !TransitionUtil.isClosingMode(change.getMode()); 1422 } else { 1423 isOpen = TransitionUtil.isOpeningMode(change.getMode()); 1424 break; 1425 } 1426 } 1427 } 1428 if (!isOpen) { 1429 // Close transition, the transition info should be: 1430 // init info(open A & wallpaper) => init info(open A & change B & wallpaper) 1431 // current info(close B target) => current info(change A & close B) 1432 // remove init info(open/change A target & wallpaper) 1433 boolean moveToTop = false; 1434 boolean excludeOpenTarget = false; 1435 boolean mergePredictive = false; 1436 for (int j = info.getChanges().size() - 1; j >= 0; --j) { 1437 final TransitionInfo.Change change = info.getChanges().get(j); 1438 if (isOpenSurfaceMatched(openSurfaces, change)) { 1439 if (TransitionUtil.isClosingMode(change.getMode())) { 1440 excludeOpenTarget = true; 1441 } 1442 moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP); 1443 info.getChanges().remove(j); 1444 } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) { 1445 info.getChanges().remove(j); 1446 } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) { 1447 mergePredictive = true; 1448 } 1449 } 1450 // Ignore merge if there is no close target 1451 if (!info.getChanges().isEmpty() && mergePredictive) { 1452 tmpSize = init.getChanges().size(); 1453 for (int i = 0; i < tmpSize; ++i) { 1454 final TransitionInfo.Change change = init.getChanges().get(i); 1455 if (change.hasFlags(FLAG_IS_WALLPAPER)) { 1456 continue; 1457 } 1458 if (isOpenSurfaceMatched(openSurfaces, change)) { 1459 if (excludeOpenTarget) { 1460 // App has triggered another change during predictive back 1461 // transition, filter out predictive back target. 1462 continue; 1463 } 1464 if (moveToTop) { 1465 change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP); 1466 } 1467 } else if (Flags.unifyBackNavigationTransition() 1468 && change.hasFlags(FLAG_BACK_GESTURE_ANIMATED) 1469 && change.getMode() == TRANSIT_CHANGE 1470 && isCloseChangeExist(info, change)) { 1471 // This is the original top target, don't add it into current transition 1472 // if it is closing. 1473 continue; 1474 } 1475 info.getChanges().add(i, change); 1476 } 1477 } 1478 } else { 1479 // Open transition, the transition info should be: 1480 // init info(open A & wallpaper) 1481 // current info(open C target + close B target + close A & wallpaper) 1482 1483 // If close target isn't back navigated, filter out close A & wallpaper because the 1484 // (open C + close B) pair didn't participant prepare close 1485 boolean nonBackOpen = false; 1486 boolean nonBackClose = false; 1487 tmpSize = info.getChanges().size(); 1488 for (int j = 0; j < tmpSize; ++j) { 1489 final TransitionInfo.Change change = info.getChanges().get(j); 1490 if (!change.hasFlags(FLAG_BACK_GESTURE_ANIMATED) 1491 && canBeTransitionTarget(change)) { 1492 final int mode = change.getMode(); 1493 nonBackOpen |= TransitionUtil.isOpeningMode(mode); 1494 nonBackClose |= TransitionUtil.isClosingMode(mode); 1495 } 1496 } 1497 if (nonBackClose && nonBackOpen) { 1498 for (int j = info.getChanges().size() - 1; j >= 0; --j) { 1499 final TransitionInfo.Change change = info.getChanges().get(j); 1500 if (isOpenSurfaceMatched(openSurfaces, change)) { 1501 info.getChanges().remove(j); 1502 } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) { 1503 info.getChanges().remove(j); 1504 } 1505 } 1506 } 1507 } 1508 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending " 1509 + "transitions result=%s", info); 1510 // Only handle one merge transition request. 1511 mOpenTransitionInfo = null; 1512 } 1513 1514 @Override mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)1515 public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1516 @NonNull SurfaceControl.Transaction startT, 1517 @NonNull SurfaceControl.Transaction finishT, 1518 @NonNull IBinder mergeTarget, 1519 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1520 if (mClosePrepareTransition == transition) { 1521 mClosePrepareTransition = null; 1522 } 1523 // try to handle unexpected transition 1524 if (mOpenTransitionInfo != null) { 1525 mergePendingTransitions(info); 1526 } 1527 1528 if (info.getType() == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION 1529 && !mCloseTransitionRequested && info.getChanges().isEmpty() && mApps == null) { 1530 finishCallback.onTransitionFinished(null); 1531 startT.apply(); 1532 applyFinishOpenTransition(); 1533 return; 1534 } 1535 if (isNotGestureBackTransition(info) || shouldCancelAnimation(info) 1536 || !mCloseTransitionRequested) { 1537 if (mPrepareOpenTransition != null) { 1538 applyFinishOpenTransition(); 1539 } 1540 return; 1541 } 1542 // Handle the commit transition if this handler is running the open transition. 1543 finishCallback.onTransitionFinished(null); 1544 startT.apply(); 1545 if (mCloseTransitionRequested) { 1546 if (mApps == null || mApps.length == 0) { 1547 // animation was done 1548 applyFinishOpenTransition(); 1549 mCloseTransitionRequested = false; 1550 } else { 1551 // we are animating, wait until animation finish 1552 mOnAnimationFinishCallback = () -> { 1553 applyFinishOpenTransition(); 1554 mCloseTransitionRequested = false; 1555 }; 1556 } 1557 } 1558 } 1559 1560 // Cancel close animation if something happen unexpected, let another handler to handle shouldCancelAnimation(@onNull TransitionInfo info)1561 private boolean shouldCancelAnimation(@NonNull TransitionInfo info) { 1562 final boolean noCloseAllowed = !mCloseTransitionRequested 1563 && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION; 1564 boolean unableToHandle = false; 1565 boolean filterTargets = false; 1566 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1567 final TransitionInfo.Change c = info.getChanges().get(i); 1568 final boolean backGestureAnimated = c.hasFlags(FLAG_BACK_GESTURE_ANIMATED); 1569 if (!backGestureAnimated && !c.hasFlags(FLAG_IS_WALLPAPER)) { 1570 // something we cannot handle? 1571 unableToHandle = true; 1572 filterTargets = true; 1573 } else if (noCloseAllowed && backGestureAnimated 1574 && TransitionUtil.isClosingMode(c.getMode())) { 1575 // Prepare back navigation shouldn't contain close change, unless top app 1576 // request close. 1577 unableToHandle = true; 1578 } 1579 } 1580 if (!unableToHandle) { 1581 return false; 1582 } 1583 if (!filterTargets) { 1584 return true; 1585 } 1586 if (TransitionUtil.isOpeningType(info.getType()) 1587 || TransitionUtil.isClosingType(info.getType())) { 1588 boolean removeWallpaper = false; 1589 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1590 final TransitionInfo.Change c = info.getChanges().get(i); 1591 // filter out opening target, keep original closing target in this transition 1592 if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) 1593 && TransitionUtil.isOpeningMode(c.getMode())) { 1594 info.getChanges().remove(i); 1595 removeWallpaper |= c.hasFlags(FLAG_SHOW_WALLPAPER); 1596 } 1597 } 1598 if (removeWallpaper) { 1599 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1600 final TransitionInfo.Change c = info.getChanges().get(i); 1601 if (c.hasFlags(FLAG_IS_WALLPAPER)) { 1602 info.getChanges().remove(i); 1603 } 1604 } 1605 } 1606 } 1607 return true; 1608 } 1609 1610 /** 1611 * Check whether this transition is prepare for predictive back animation, which could 1612 * happen when core make an activity become visible. 1613 */ 1614 @VisibleForTesting handlePrepareTransition(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1615 boolean handlePrepareTransition(@NonNull IBinder transition, 1616 @NonNull TransitionInfo info, 1617 @NonNull SurfaceControl.Transaction st, 1618 @NonNull SurfaceControl.Transaction ft, 1619 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1620 if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) { 1621 return false; 1622 } 1623 // Must have open target, must not have close target. 1624 if (hasAnimationInMode(info, TransitionUtil::isClosingMode) 1625 || !hasAnimationInMode(info, TransitionUtil::isOpeningMode)) { 1626 return false; 1627 } 1628 SurfaceControl openingLeash = null; 1629 SurfaceControl closingLeash = null; 1630 if (mApps != null) { 1631 for (int i = mApps.length - 1; i >= 0; --i) { 1632 if (mApps[i].mode == MODE_OPENING) { 1633 openingLeash = mApps[i].leash; 1634 } else if (mApps[i].mode == MODE_CLOSING) { 1635 closingLeash = mApps[i].leash; 1636 } 1637 } 1638 } 1639 if (openingLeash != null && closingLeash != null) { 1640 int rootIdx = -1; 1641 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1642 final TransitionInfo.Change c = info.getChanges().get(i); 1643 if (TransitionUtil.isOpeningMode(c.getMode())) { 1644 final Point offset = c.getEndRelOffset(); 1645 st.setPosition(c.getLeash(), offset.x, offset.y); 1646 st.reparent(c.getLeash(), openingLeash); 1647 st.setAlpha(c.getLeash(), 1.0f); 1648 rootIdx = TransitionUtil.rootIndexFor(c, info); 1649 } else if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) 1650 && c.getMode() == TRANSIT_CHANGE) { 1651 st.reparent(c.getLeash(), closingLeash); 1652 } 1653 } 1654 // The root leash and the leash of opening target should actually in the same level, 1655 // but since the root leash is created after opening target, it will have higher 1656 // layer in surface flinger. Move the root leash to lower level, so it won't affect 1657 // the playing animation. 1658 if (rootIdx >= 0 && info.getRootCount() > 0) { 1659 st.setLayer(info.getRoot(rootIdx).getLeash(), -1); 1660 } 1661 } 1662 st.apply(); 1663 // In case other transition handler took the handleRequest before this class. 1664 mPrepareOpenTransition = transition; 1665 mFinishOpenTransaction = ft; 1666 mFinishOpenTransitionCallback = finishCallback; 1667 mOpenTransitionInfo = info; 1668 return true; 1669 } 1670 1671 /** 1672 * Check whether this transition is triggered from back gesture commitment. 1673 * Reparent the transition targets to animation leashes, so the animation won't be broken. 1674 */ 1675 @VisibleForTesting handleCloseTransition(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1676 boolean handleCloseTransition(@NonNull TransitionInfo info, 1677 @NonNull SurfaceControl.Transaction st, 1678 @NonNull SurfaceControl.Transaction ft, 1679 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1680 if (!mCloseTransitionRequested) { 1681 return false; 1682 } 1683 // must have close target 1684 if (!hasAnimationInMode(info, TransitionUtil::isClosingMode)) { 1685 return false; 1686 } 1687 if (mApps == null) { 1688 // animation is done 1689 applyAndFinish(st, ft, finishCallback); 1690 return true; 1691 } 1692 SurfaceControl openingLeash = null; 1693 SurfaceControl closingLeash = null; 1694 for (int i = mApps.length - 1; i >= 0; --i) { 1695 if (mApps[i].mode == MODE_OPENING) { 1696 openingLeash = mApps[i].leash; 1697 } 1698 if (mApps[i].mode == MODE_CLOSING) { 1699 closingLeash = mApps[i].leash; 1700 } 1701 } 1702 if (openingLeash != null && closingLeash != null) { 1703 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1704 final TransitionInfo.Change c = info.getChanges().get(i); 1705 if (c.hasFlags(FLAG_IS_WALLPAPER)) { 1706 st.setAlpha(c.getLeash(), 1.0f); 1707 continue; 1708 } 1709 if (TransitionUtil.isOpeningMode(c.getMode())) { 1710 final Point offset = c.getEndRelOffset(); 1711 st.setPosition(c.getLeash(), offset.x, offset.y); 1712 st.reparent(c.getLeash(), openingLeash); 1713 st.setAlpha(c.getLeash(), 1.0f); 1714 } else if (TransitionUtil.isClosingMode(c.getMode())) { 1715 st.reparent(c.getLeash(), closingLeash); 1716 } 1717 } 1718 } 1719 st.apply(); 1720 // mApps must exists 1721 mOnAnimationFinishCallback = () -> { 1722 ft.apply(); 1723 finishCallback.onTransitionFinished(null); 1724 mCloseTransitionRequested = false; 1725 }; 1726 return true; 1727 } 1728 1729 @Nullable 1730 @Override handleRequest( @onNull IBinder transition, @NonNull TransitionRequestInfo request)1731 public WindowContainerTransaction handleRequest( 1732 @NonNull IBinder transition, 1733 @NonNull TransitionRequestInfo request) { 1734 final int type = request.getType(); 1735 if (type == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) { 1736 mPrepareOpenTransition = transition; 1737 return new WindowContainerTransaction(); 1738 } 1739 if (type == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) { 1740 return new WindowContainerTransaction(); 1741 } 1742 if (TransitionUtil.isClosingType(request.getType()) && mCloseTransitionRequested) { 1743 return new WindowContainerTransaction(); 1744 } 1745 return null; 1746 } 1747 checkTakeoverFlags()1748 private static boolean checkTakeoverFlags() { 1749 return TransitionAnimator.Companion.longLivedReturnAnimationsEnabled() 1750 && Flags.unifyBackNavigationTransition(); 1751 } 1752 } 1753 isNotGestureBackTransition(@onNull TransitionInfo info)1754 private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) { 1755 return !hasAnimationInMode(info, TransitionUtil::isOpenOrCloseMode); 1756 } 1757 hasAnimationInMode(@onNull TransitionInfo info, Predicate<Integer> mode)1758 private static boolean hasAnimationInMode(@NonNull TransitionInfo info, 1759 Predicate<Integer> mode) { 1760 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1761 final TransitionInfo.Change c = info.getChanges().get(i); 1762 if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) && mode.test(c.getMode())) { 1763 return true; 1764 } 1765 } 1766 return false; 1767 } 1768 findToken(TransitionInfo.Change change)1769 private static WindowContainerToken findToken(TransitionInfo.Change change) { 1770 return change.getContainer(); 1771 } 1772 findComponentName(TransitionInfo.Change change)1773 private static ComponentName findComponentName(TransitionInfo.Change change) { 1774 final ComponentName componentName = change.getActivityComponent(); 1775 if (componentName != null) { 1776 return componentName; 1777 } 1778 final TaskInfo taskInfo = change.getTaskInfo(); 1779 if (taskInfo != null) { 1780 return taskInfo.topActivity; 1781 } 1782 return null; 1783 } 1784 findTaskId(TransitionInfo.Change change)1785 private static int findTaskId(TransitionInfo.Change change) { 1786 final TaskInfo taskInfo = change.getTaskInfo(); 1787 if (taskInfo != null) { 1788 return taskInfo.taskId; 1789 } 1790 return INVALID_TASK_ID; 1791 } 1792 isOpenSurfaceMatched(@onNull ArrayList<SurfaceControl> openSurfaces, TransitionInfo.Change change)1793 static boolean isOpenSurfaceMatched(@NonNull ArrayList<SurfaceControl> openSurfaces, 1794 TransitionInfo.Change change) { 1795 for (int i = openSurfaces.size() - 1; i >= 0; --i) { 1796 if (openSurfaces.get(i).isSameSurface(change.getLeash())) { 1797 return true; 1798 } 1799 } 1800 return false; 1801 } 1802 canBeTransitionTarget(TransitionInfo.Change change)1803 private static boolean canBeTransitionTarget(TransitionInfo.Change change) { 1804 return findComponentName(change) != null || findTaskId(change) != INVALID_TASK_ID; 1805 } 1806 isCloseChangeExist(TransitionInfo info, TransitionInfo.Change change)1807 private static boolean isCloseChangeExist(TransitionInfo info, TransitionInfo.Change change) { 1808 for (int j = info.getChanges().size() - 1; j >= 0; --j) { 1809 final TransitionInfo.Change current = info.getChanges().get(j); 1810 if (TransitionUtil.isClosingMode(current.getMode()) 1811 && change.getLeash().isSameSurface(current.getLeash())) { 1812 return true; 1813 } 1814 } 1815 return false; 1816 } 1817 1818 // Record the latest back gesture happen on which task. 1819 static class BackTransitionObserver implements Transitions.TransitionObserver { 1820 int mFocusedTaskId = INVALID_TASK_ID; 1821 IBinder mFocusTaskMonitorToken; 1822 private BackTransitionHandler mBackTransitionHandler; setBackTransitionHandler(BackTransitionHandler handler)1823 void setBackTransitionHandler(BackTransitionHandler handler) { 1824 mBackTransitionHandler = handler; 1825 } 1826 update(int focusedTaskId)1827 void update(int focusedTaskId) { 1828 mFocusedTaskId = focusedTaskId; 1829 } 1830 1831 @Override onTransitionReady(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction)1832 public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info, 1833 @NonNull SurfaceControl.Transaction startTransaction, 1834 @NonNull SurfaceControl.Transaction finishTransaction) { 1835 if (mFocusedTaskId == INVALID_TASK_ID) { 1836 return; 1837 } 1838 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 1839 final TransitionInfo.Change c = info.getChanges().get(i); 1840 if (c.getTaskInfo() != null && c.getTaskInfo().taskId == mFocusedTaskId) { 1841 mFocusTaskMonitorToken = transition; 1842 break; 1843 } 1844 } 1845 // Transition happen but the task isn't involved, reset. 1846 if (mFocusTaskMonitorToken == null) { 1847 mFocusedTaskId = INVALID_TASK_ID; 1848 } 1849 } 1850 1851 @Override onTransitionMerged(@onNull IBinder merged, @NonNull IBinder playing)1852 public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { 1853 if (mFocusTaskMonitorToken == merged) { 1854 mFocusTaskMonitorToken = playing; 1855 } 1856 if (mBackTransitionHandler.mClosePrepareTransition == merged) { 1857 mBackTransitionHandler.mClosePrepareTransition = null; 1858 } 1859 } 1860 1861 @Override onTransitionFinished(@onNull IBinder transition, boolean aborted)1862 public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { 1863 if (mFocusTaskMonitorToken == transition) { 1864 mFocusedTaskId = INVALID_TASK_ID; 1865 } 1866 if (mBackTransitionHandler.mClosePrepareTransition == transition) { 1867 mBackTransitionHandler.mClosePrepareTransition = null; 1868 } 1869 } 1870 } 1871 } 1872