1 /* 2 * Copyright (C) 2018 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.ActivityManager.START_TASK_TO_FRONT; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 23 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 24 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; 25 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; 26 import static android.view.WindowManager.TRANSIT_NONE; 27 28 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; 29 import static com.android.server.wm.BoundsAnimationController.BOUNDS; 30 import static com.android.server.wm.BoundsAnimationController.FADE_IN; 31 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; 32 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; 33 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; 34 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; 35 36 import android.annotation.Nullable; 37 import android.app.ActivityOptions; 38 import android.content.ComponentName; 39 import android.content.Intent; 40 import android.os.RemoteException; 41 import android.os.Trace; 42 import android.util.Slog; 43 import android.view.IRecentsAnimationRunner; 44 45 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; 46 47 /** 48 * Manages the recents animation, including the reordering of the stacks for the transition and 49 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. 50 */ 51 class RecentsAnimation implements RecentsAnimationCallbacks, 52 ActivityDisplay.OnStackOrderChangedListener { 53 private static final String TAG = RecentsAnimation.class.getSimpleName(); 54 private static final boolean DEBUG = DEBUG_RECENTS_ANIMATIONS; 55 56 private final ActivityTaskManagerService mService; 57 private final ActivityStackSupervisor mStackSupervisor; 58 private final ActivityStartController mActivityStartController; 59 private final WindowManagerService mWindowManager; 60 private final ActivityDisplay mDefaultDisplay; 61 private final Intent mTargetIntent; 62 private final ComponentName mRecentsComponent; 63 private final int mRecentsUid; 64 private final @Nullable WindowProcessController mCaller; 65 private final int mUserId; 66 private final int mTargetActivityType; 67 68 /** 69 * The activity which has been launched behind. We need to remember the activity because the 70 * target stack may have other activities, then we are able to restore the launch-behind state 71 * for the exact activity. 72 */ 73 private ActivityRecord mLaunchedTargetActivity; 74 75 // The stack to restore the target stack behind when the animation is finished 76 private ActivityStack mRestoreTargetBehindStack; 77 RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor, ActivityStartController activityStartController, WindowManagerService wm, Intent targetIntent, ComponentName recentsComponent, int recentsUid, @Nullable WindowProcessController caller)78 RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor, 79 ActivityStartController activityStartController, WindowManagerService wm, 80 Intent targetIntent, ComponentName recentsComponent, int recentsUid, 81 @Nullable WindowProcessController caller) { 82 mService = atm; 83 mStackSupervisor = stackSupervisor; 84 mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay(); 85 mActivityStartController = activityStartController; 86 mWindowManager = wm; 87 mTargetIntent = targetIntent; 88 mRecentsComponent = recentsComponent; 89 mRecentsUid = recentsUid; 90 mCaller = caller; 91 mUserId = atm.getCurrentUserId(); 92 mTargetActivityType = targetIntent.getComponent() != null 93 && recentsComponent.equals(targetIntent.getComponent()) 94 ? ACTIVITY_TYPE_RECENTS 95 : ACTIVITY_TYPE_HOME; 96 } 97 98 /** 99 * Starts the recents activity in background without animation if the record doesn't exist or 100 * the client isn't launched. If the recents activity is already alive, ensure its configuration 101 * is updated to the current one. 102 */ preloadRecentsActivity()103 void preloadRecentsActivity() { 104 if (DEBUG) Slog.d(TAG, "Preload recents with " + mTargetIntent); 105 ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, 106 mTargetActivityType); 107 ActivityRecord targetActivity = getTargetActivity(targetStack); 108 if (targetActivity != null) { 109 if (targetActivity.visible || targetActivity.isTopRunningActivity()) { 110 // The activity is ready. 111 return; 112 } 113 if (targetActivity.attachedToProcess()) { 114 // The activity may be relaunched if it cannot handle the current configuration 115 // changes. The activity will be paused state if it is relaunched, otherwise it 116 // keeps the original stopped state. 117 targetActivity.ensureActivityConfiguration(0 /* globalChanges */, 118 false /* preserveWindow */, true /* ignoreVisibility */); 119 if (DEBUG) Slog.d(TAG, "Updated config=" + targetActivity.getConfiguration()); 120 } 121 } else { 122 // Create the activity record. Because the activity is invisible, this doesn't really 123 // start the client. 124 startRecentsActivityInBackground("preloadRecents"); 125 targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); 126 targetActivity = getTargetActivity(targetStack); 127 if (targetActivity == null) { 128 Slog.w(TAG, "Cannot start " + mTargetIntent); 129 return; 130 } 131 } 132 133 if (!targetActivity.attachedToProcess()) { 134 if (DEBUG) Slog.d(TAG, "Real start recents"); 135 mStackSupervisor.startSpecificActivityLocked(targetActivity, false /* andResume */, 136 false /* checkConfig */); 137 // Make sure the activity won't be involved in transition. 138 if (targetActivity.mAppWindowToken != null) { 139 targetActivity.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController 140 .appRemovedOrHidden(targetActivity.mAppWindowToken); 141 } 142 } 143 144 // Invisible activity should be stopped. If the recents activity is alive and its doesn't 145 // need to relaunch by current configuration, then it may be already in stopped state. 146 if (!targetActivity.isState(ActivityStack.ActivityState.STOPPING, 147 ActivityStack.ActivityState.STOPPED)) { 148 // Add to stopping instead of stop immediately. So the client has the chance to perform 149 // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more 150 // things (e.g. the measure can be done earlier). The actual stop will be performed when 151 // it reports idle. 152 targetStack.addToStopping(targetActivity, true /* scheduleIdle */, 153 true /* idleDelayed */, "preloadRecents"); 154 } 155 } 156 startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner)157 void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) { 158 if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + mTargetIntent); 159 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); 160 161 // TODO(multi-display) currently only support recents animation in default display. 162 final DisplayContent dc = 163 mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; 164 if (!mWindowManager.canStartRecentsAnimation()) { 165 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 166 if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition=" 167 + dc.mAppTransition.getAppTransition()); 168 return; 169 } 170 171 // If the activity is associated with the recents stack, then try and get that first 172 ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, 173 mTargetActivityType); 174 ActivityRecord targetActivity = getTargetActivity(targetStack); 175 final boolean hasExistingActivity = targetActivity != null; 176 if (hasExistingActivity) { 177 final ActivityDisplay display = targetActivity.getDisplay(); 178 mRestoreTargetBehindStack = display.getStackAbove(targetStack); 179 if (mRestoreTargetBehindStack == null) { 180 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 181 if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack); 182 return; 183 } 184 } 185 186 // Send launch hint if we are actually launching the target. If it's already visible 187 // (shouldn't happen in general) we don't need to send it. 188 if (targetActivity == null || !targetActivity.visible) { 189 mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( 190 true /* forceSend */, targetActivity); 191 } 192 193 mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent); 194 195 if (mCaller != null) { 196 mCaller.setRunningRecentsAnimation(true); 197 } 198 199 mWindowManager.deferSurfaceLayout(); 200 try { 201 if (hasExistingActivity) { 202 // Move the recents activity into place for the animation if it is not top most 203 mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack); 204 if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack=" 205 + mDefaultDisplay.getStackAbove(targetStack)); 206 207 // If there are multiple tasks in the target stack (ie. the home stack, with 3p 208 // and default launchers coexisting), then move the task to the top as a part of 209 // moving the stack to the front 210 if (targetStack.topTask() != targetActivity.getTaskRecord()) { 211 targetStack.addTask(targetActivity.getTaskRecord(), true /* toTop */, 212 "startRecentsActivity"); 213 } 214 } else { 215 // No recents activity, create the new recents activity bottom most 216 startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); 217 218 // Move the recents activity into place for the animation 219 targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, 220 mTargetActivityType); 221 targetActivity = getTargetActivity(targetStack); 222 mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack); 223 if (DEBUG) { 224 Slog.d(TAG, "Moved stack=" + targetStack + " behind stack=" 225 + mDefaultDisplay.getStackAbove(targetStack)); 226 } 227 228 mWindowManager.prepareAppTransition(TRANSIT_NONE, false); 229 mWindowManager.executeAppTransition(); 230 231 // TODO: Maybe wait for app to draw in this particular case? 232 233 if (DEBUG) Slog.d(TAG, "Started intent=" + mTargetIntent); 234 } 235 236 // Mark the target activity as launch-behind to bump its visibility for the 237 // duration of the gesture that is driven by the recents component 238 targetActivity.mLaunchTaskBehind = true; 239 mLaunchedTargetActivity = targetActivity; 240 241 // Fetch all the surface controls and pass them to the client to get the animation 242 // started. Cancel any existing recents animation running synchronously (do not hold the 243 // WM lock) 244 mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION, 245 "startRecentsActivity"); 246 mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, 247 this, mDefaultDisplay.mDisplayId, 248 mStackSupervisor.mRecentTasks.getRecentTaskIds()); 249 250 // If we updated the launch-behind state, update the visibility of the activities after 251 // we fetch the visible tasks to be controlled by the animation 252 mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); 253 254 mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT, 255 targetActivity); 256 257 // Register for stack order changes 258 mDefaultDisplay.registerStackOrderChangedListener(this); 259 } catch (Exception e) { 260 Slog.e(TAG, "Failed to start recents activity", e); 261 throw e; 262 } finally { 263 mWindowManager.continueSurfaceLayout(); 264 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); 265 } 266 } 267 finishAnimation(@ecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint)268 private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode, 269 boolean sendUserLeaveHint) { 270 synchronized (mService.mGlobalLock) { 271 if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller=" 272 + mWindowManager.getRecentsAnimationController() 273 + " reorderMode=" + reorderMode); 274 275 // Unregister for stack order changes 276 mDefaultDisplay.unregisterStackOrderChangedListener(this); 277 278 final RecentsAnimationController controller = 279 mWindowManager.getRecentsAnimationController(); 280 if (controller == null) return; 281 282 // Just to be sure end the launch hint in case the target activity was never launched. 283 // However, if we're keeping the activity and making it visible, we can leave it on. 284 if (reorderMode != REORDER_KEEP_IN_PLACE) { 285 mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded(); 286 } 287 288 // Once the target is shown, prevent spurious background app switches 289 if (reorderMode == REORDER_MOVE_TO_TOP) { 290 mService.stopAppSwitches(); 291 } 292 293 if (mCaller != null) { 294 mCaller.setRunningRecentsAnimation(false); 295 } 296 297 mWindowManager.inSurfaceTransaction(() -> { 298 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, 299 "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); 300 mWindowManager.deferSurfaceLayout(); 301 try { 302 mWindowManager.cleanupRecentsAnimation(reorderMode); 303 304 final ActivityStack targetStack = mDefaultDisplay.getStack( 305 WINDOWING_MODE_UNDEFINED, mTargetActivityType); 306 // Prefer to use the original target activity instead of top activity because 307 // we may have moved another task to top (starting 3p launcher). 308 final ActivityRecord targetActivity = targetStack != null 309 ? targetStack.isInStackLocked(mLaunchedTargetActivity) 310 : null; 311 if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack 312 + " targetActivity=" + targetActivity 313 + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack); 314 if (targetActivity == null) { 315 return; 316 } 317 318 // Restore the launched-behind state 319 targetActivity.mLaunchTaskBehind = false; 320 321 if (reorderMode == REORDER_MOVE_TO_TOP) { 322 // Bring the target stack to the front 323 mStackSupervisor.mNoAnimActivities.add(targetActivity); 324 325 if (sendUserLeaveHint) { 326 // Setting this allows the previous app to PiP. 327 mStackSupervisor.mUserLeaving = true; 328 targetStack.moveTaskToFrontLocked(targetActivity.getTaskRecord(), 329 true /* noAnimation */, null /* activityOptions */, 330 targetActivity.appTimeTracker, 331 "RecentsAnimation.onAnimationFinished()"); 332 } else { 333 targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); 334 } 335 336 if (DEBUG) { 337 final ActivityStack topStack = getTopNonAlwaysOnTopStack(); 338 if (topStack != targetStack) { 339 Slog.w(TAG, "Expected target stack=" + targetStack 340 + " to be top most but found stack=" + topStack); 341 } 342 } 343 } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){ 344 // Restore the target stack to its previous position 345 final ActivityDisplay display = targetActivity.getDisplay(); 346 display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack); 347 if (DEBUG) { 348 final ActivityStack aboveTargetStack = 349 mDefaultDisplay.getStackAbove(targetStack); 350 if (mRestoreTargetBehindStack != null 351 && aboveTargetStack != mRestoreTargetBehindStack) { 352 Slog.w(TAG, "Expected target stack=" + targetStack 353 + " to restored behind stack=" + mRestoreTargetBehindStack 354 + " but it is behind stack=" + aboveTargetStack); 355 } 356 } 357 } else { 358 // If there is no recents screenshot animation, we can update the visibility 359 // of target stack immediately because it is visually invisible and the 360 // launch-behind state is restored. That also prevents the next transition 361 // type being disturbed if the visibility is updated after setting the next 362 // transition (the target activity will be one of closing apps). 363 if (!controller.shouldDeferCancelWithScreenshot() 364 && !targetStack.isFocusedStackOnDisplay()) { 365 targetStack.ensureActivitiesVisibleLocked(null /* starting */, 366 0 /* starting */, false /* preserveWindows */); 367 } 368 // Keep target stack in place, nothing changes, so ignore the transition 369 // logic below 370 return; 371 } 372 373 mWindowManager.prepareAppTransition(TRANSIT_NONE, false); 374 mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false); 375 mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); 376 377 // No reason to wait for the pausing activity in this case, as the hiding of 378 // surfaces needs to be done immediately. 379 mWindowManager.executeAppTransition(); 380 381 // After reordering the stacks, reset the minimized state. At this point, either 382 // the target activity is now top-most and we will stay minimized (if in 383 // split-screen), or we will have returned to the app, and the minimized state 384 // should be reset 385 mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */); 386 } catch (Exception e) { 387 Slog.e(TAG, "Failed to clean up recents activity", e); 388 throw e; 389 } finally { 390 mWindowManager.continueSurfaceLayout(); 391 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); 392 } 393 }); 394 } 395 } 396 397 @Override onAnimationFinished(@ecentsAnimationController.ReorderMode int reorderMode, boolean runSychronously, boolean sendUserLeaveHint)398 public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, 399 boolean runSychronously, boolean sendUserLeaveHint) { 400 if (runSychronously) { 401 finishAnimation(reorderMode, sendUserLeaveHint); 402 } else { 403 mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint)); 404 } 405 } 406 407 @Override onStackOrderChanged(ActivityStack stack)408 public void onStackOrderChanged(ActivityStack stack) { 409 if (DEBUG) Slog.d(TAG, "onStackOrderChanged(): stack=" + stack); 410 if (mDefaultDisplay.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) { 411 // The stack is not visible, so ignore this change 412 return; 413 } 414 final RecentsAnimationController controller = 415 mWindowManager.getRecentsAnimationController(); 416 if (controller == null) { 417 return; 418 } 419 420 final DisplayContent dc = 421 mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; 422 dc.mBoundsAnimationController.setAnimationType( 423 controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS); 424 425 // We defer canceling the recents animation until the next app transition in the following 426 // cases: 427 // 1) The next launching task is not being animated by the recents animation 428 // 2) The next task is home activity. (i.e. pressing home key to back home in recents). 429 if ((!controller.isAnimatingTask(stack.getTaskStack().getTopChild()) 430 || controller.isTargetApp(stack.getTopActivity().mAppWindowToken)) 431 && controller.shouldDeferCancelUntilNextTransition()) { 432 // Always prepare an app transition since we rely on the transition callbacks to cleanup 433 mWindowManager.prepareAppTransition(TRANSIT_NONE, false); 434 controller.setCancelOnNextTransitionStart(); 435 } else { 436 // Just cancel directly to unleash from launcher when the next launching task is the 437 // current top task. 438 mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE, 439 "stackOrderChanged"); 440 } 441 } 442 startRecentsActivityInBackground(String reason)443 private void startRecentsActivityInBackground(String reason) { 444 final ActivityOptions options = ActivityOptions.makeBasic(); 445 options.setLaunchActivityType(mTargetActivityType); 446 options.setAvoidMoveToFront(); 447 mTargetIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); 448 449 mActivityStartController 450 .obtainStarter(mTargetIntent, reason) 451 .setCallingUid(mRecentsUid) 452 .setCallingPackage(mRecentsComponent.getPackageName()) 453 .setActivityOptions(new SafeActivityOptions(options)) 454 .setMayWait(mUserId) 455 .execute(); 456 } 457 458 /** 459 * Called only when the animation should be canceled prior to starting. 460 */ notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner)461 static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { 462 try { 463 recentsAnimationRunner.onAnimationCanceled(false /* deferredWithScreenshot */); 464 } catch (RemoteException e) { 465 Slog.e(TAG, "Failed to cancel recents animation before start", e); 466 } 467 } 468 469 /** 470 * @return The top stack that is not always-on-top. 471 */ getTopNonAlwaysOnTopStack()472 private ActivityStack getTopNonAlwaysOnTopStack() { 473 for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) { 474 final ActivityStack s = mDefaultDisplay.getChildAt(i); 475 if (s.getWindowConfiguration().isAlwaysOnTop()) { 476 continue; 477 } 478 return s; 479 } 480 return null; 481 } 482 483 /** 484 * @return the top activity in the {@param targetStack} matching the {@param component}, or just 485 * the top activity of the top task if no task matches the component. 486 */ getTargetActivity(ActivityStack targetStack)487 private ActivityRecord getTargetActivity(ActivityStack targetStack) { 488 if (targetStack == null) { 489 return null; 490 } 491 492 for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { 493 final TaskRecord task = targetStack.getChildAt(i); 494 if (task.userId == mUserId 495 && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) { 496 return task.getTopActivity(); 497 } 498 } 499 return null; 500 } 501 } 502