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.PROCESS_STATE_CACHED_ACTIVITY; 20 import static android.app.ActivityManager.START_TASK_TO_FRONT; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 24 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 25 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; 26 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 27 28 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; 29 import static com.android.server.wm.ActivityRecord.State.STOPPED; 30 import static com.android.server.wm.ActivityRecord.State.STOPPING; 31 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; 32 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; 33 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; 34 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; 35 import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; 36 37 import android.annotation.Nullable; 38 import android.app.ActivityOptions; 39 import android.content.ComponentName; 40 import android.content.Intent; 41 import android.os.RemoteException; 42 import android.os.Trace; 43 import android.util.Slog; 44 import android.view.IRecentsAnimationRunner; 45 46 import com.android.internal.protolog.common.ProtoLog; 47 import com.android.internal.util.function.pooled.PooledLambda; 48 import com.android.internal.util.function.pooled.PooledPredicate; 49 import com.android.server.wm.ActivityMetricsLogger.LaunchingState; 50 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; 51 import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener; 52 53 /** 54 * Manages the recents animation, including the reordering of the root tasks for the transition and 55 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. 56 */ 57 class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChangedListener { 58 private static final String TAG = RecentsAnimation.class.getSimpleName(); 59 60 private final ActivityTaskManagerService mService; 61 private final ActivityTaskSupervisor mTaskSupervisor; 62 private final ActivityStartController mActivityStartController; 63 private final WindowManagerService mWindowManager; 64 private final TaskDisplayArea mDefaultTaskDisplayArea; 65 private final Intent mTargetIntent; 66 private final ComponentName mRecentsComponent; 67 private final @Nullable String mRecentsFeatureId; 68 private final int mRecentsUid; 69 private final @Nullable WindowProcessController mCaller; 70 private final int mUserId; 71 private final int mTargetActivityType; 72 73 /** 74 * The activity which has been launched behind. We need to remember the activity because the 75 * target root task may have other activities, then we are able to restore the launch-behind 76 * state for the exact activity. 77 */ 78 private ActivityRecord mLaunchedTargetActivity; 79 80 // The root task to restore the target root task behind when the animation is finished 81 private Task mRestoreTargetBehindRootTask; 82 RecentsAnimation(ActivityTaskManagerService atm, ActivityTaskSupervisor taskSupervisor, ActivityStartController activityStartController, WindowManagerService wm, Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId, int recentsUid, @Nullable WindowProcessController caller)83 RecentsAnimation(ActivityTaskManagerService atm, ActivityTaskSupervisor taskSupervisor, 84 ActivityStartController activityStartController, WindowManagerService wm, 85 Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId, 86 int recentsUid, @Nullable WindowProcessController caller) { 87 mService = atm; 88 mTaskSupervisor = taskSupervisor; 89 mDefaultTaskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); 90 mActivityStartController = activityStartController; 91 mWindowManager = wm; 92 mTargetIntent = targetIntent; 93 mRecentsComponent = recentsComponent; 94 mRecentsFeatureId = recentsFeatureId; 95 mRecentsUid = recentsUid; 96 mCaller = caller; 97 mUserId = atm.getCurrentUserId(); 98 mTargetActivityType = targetIntent.getComponent() != null 99 && recentsComponent.equals(targetIntent.getComponent()) 100 ? ACTIVITY_TYPE_RECENTS 101 : ACTIVITY_TYPE_HOME; 102 } 103 104 /** 105 * Starts the recents activity in background without animation if the record doesn't exist or 106 * the client isn't launched. If the recents activity is already alive, ensure its configuration 107 * is updated to the current one. 108 */ preloadRecentsActivity()109 void preloadRecentsActivity() { 110 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s", 111 mTargetIntent); 112 Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 113 mTargetActivityType); 114 ActivityRecord targetActivity = getTargetActivity(targetRootTask); 115 if (targetActivity != null) { 116 if (targetActivity.isVisibleRequested() || targetActivity.isTopRunningActivity()) { 117 // The activity is ready. 118 return; 119 } 120 if (targetActivity.attachedToProcess()) { 121 if (targetActivity.app.getCurrentProcState() >= PROCESS_STATE_CACHED_ACTIVITY) { 122 Slog.v(TAG, "Skip preload recents for cached proc " + targetActivity.app); 123 // The process may be frozen that cannot receive binder call. 124 return; 125 } 126 // The activity may be relaunched if it cannot handle the current configuration 127 // changes. The activity will be paused state if it is relaunched, otherwise it 128 // keeps the original stopped state. 129 targetActivity.ensureActivityConfiguration(0 /* globalChanges */, 130 false /* preserveWindow */, true /* ignoreVisibility */); 131 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s", 132 targetActivity.getConfiguration()); 133 } 134 } else if (mDefaultTaskDisplayArea.getActivity( 135 ActivityRecord::occludesParent, false /* traverseTopToBottom */) == null) { 136 // Skip because none of above activities can occlude the target activity. The preload 137 // should be done silently in background without being visible. 138 return; 139 } else { 140 // Create the activity record. Because the activity is invisible, this doesn't really 141 // start the client. 142 startRecentsActivityInBackground("preloadRecents"); 143 targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 144 mTargetActivityType); 145 targetActivity = getTargetActivity(targetRootTask); 146 if (targetActivity == null) { 147 Slog.w(TAG, "Cannot start " + mTargetIntent); 148 return; 149 } 150 } 151 152 if (!targetActivity.attachedToProcess()) { 153 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Real start recents"); 154 mTaskSupervisor.startSpecificActivity(targetActivity, false /* andResume */, 155 false /* checkConfig */); 156 // Make sure the activity won't be involved in transition. 157 if (targetActivity.getDisplayContent() != null) { 158 targetActivity.getDisplayContent().mUnknownAppVisibilityController 159 .appRemovedOrHidden(targetActivity); 160 } 161 } 162 163 // Invisible activity should be stopped. If the recents activity is alive and its doesn't 164 // need to relaunch by current configuration, then it may be already in stopped state. 165 if (!targetActivity.isState(STOPPING, STOPPED)) { 166 // Add to stopping instead of stop immediately. So the client has the chance to perform 167 // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more 168 // things (e.g. the measure can be done earlier). The actual stop will be performed when 169 // it reports idle. 170 targetActivity.addToStopping(true /* scheduleIdle */, true /* idleDelayed */, 171 "preloadRecents"); 172 } 173 } 174 startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime)175 void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) { 176 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent); 177 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); 178 179 // Cancel any existing recents animation running synchronously (do not hold the 180 // WM lock) before starting the newly requested recents animation as they can not coexist 181 if (mWindowManager.getRecentsAnimationController() != null) { 182 mWindowManager.getRecentsAnimationController().forceCancelAnimation( 183 REORDER_MOVE_TO_ORIGINAL_POSITION, "startRecentsActivity"); 184 } 185 186 // If the activity is associated with the root recents task, then try and get that first 187 Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 188 mTargetActivityType); 189 ActivityRecord targetActivity = getTargetActivity(targetRootTask); 190 final boolean hasExistingActivity = targetActivity != null; 191 if (hasExistingActivity) { 192 mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask); 193 if (mRestoreTargetBehindRootTask == null 194 && targetRootTask.getTopMostTask() == targetActivity.getTask()) { 195 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 196 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 197 "No root task above target root task=%s", targetRootTask); 198 return; 199 } 200 } 201 202 // Send launch hint if we are actually launching the target. If it's already visible 203 // (shouldn't happen in general) we don't need to send it. 204 if (targetActivity == null || !targetActivity.isVisibleRequested()) { 205 mService.mRootWindowContainer.startPowerModeLaunchIfNeeded( 206 true /* forceSend */, targetActivity); 207 } 208 209 final LaunchingState launchingState = 210 mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent); 211 212 setProcessAnimating(true); 213 214 mService.deferWindowLayout(); 215 try { 216 if (hasExistingActivity) { 217 // Move the recents activity into place for the animation if it is not top most 218 mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask); 219 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s", 220 targetRootTask, getRootTaskAbove(targetRootTask)); 221 222 // If there are multiple tasks in the target root task (ie. the root home task, 223 // with 3p and default launchers coexisting), then move the task to the top as a 224 // part of moving the root task to the front 225 final Task task = targetActivity.getTask(); 226 if (targetRootTask.getTopMostTask() != task) { 227 targetRootTask.positionChildAtTop(task); 228 } 229 } else { 230 // No recents activity, create the new recents activity bottom most 231 startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); 232 233 // Move the recents activity into place for the animation 234 targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 235 mTargetActivityType); 236 targetActivity = getTargetActivity(targetRootTask); 237 mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask); 238 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s", 239 targetRootTask, getRootTaskAbove(targetRootTask)); 240 241 mWindowManager.prepareAppTransitionNone(); 242 mWindowManager.executeAppTransition(); 243 244 // TODO: Maybe wait for app to draw in this particular case? 245 246 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Started intent=%s", mTargetIntent); 247 } 248 249 // Mark the target activity as launch-behind to bump its visibility for the 250 // duration of the gesture that is driven by the recents component 251 targetActivity.mLaunchTaskBehind = true; 252 mLaunchedTargetActivity = targetActivity; 253 // TODO(b/156772625): Evaluate to send new intents vs. replacing the intent extras. 254 targetActivity.intent.replaceExtras(mTargetIntent); 255 256 // Fetch all the surface controls and pass them to the client to get the animation 257 // started 258 mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, 259 this, mDefaultTaskDisplayArea.getDisplayId(), 260 mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity); 261 262 // If we updated the launch-behind state, update the visibility of the activities after 263 // we fetch the visible tasks to be controlled by the animation 264 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); 265 266 ActivityOptions options = null; 267 if (eventTime > 0) { 268 options = ActivityOptions.makeBasic(); 269 options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); 270 } 271 mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, 272 START_TASK_TO_FRONT, !hasExistingActivity, targetActivity, options); 273 274 // Register for root task order changes 275 mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this); 276 } catch (Exception e) { 277 Slog.e(TAG, "Failed to start recents activity", e); 278 throw e; 279 } finally { 280 mService.continueWindowLayout(); 281 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 282 } 283 } 284 finishAnimation(@ecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint)285 private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode, 286 boolean sendUserLeaveHint) { 287 synchronized (mService.mGlobalLock) { 288 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 289 "onAnimationFinished(): controller=%s reorderMode=%d", 290 mWindowManager.getRecentsAnimationController(), reorderMode); 291 292 // Unregister for root task order changes 293 mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(this); 294 295 final RecentsAnimationController controller = 296 mWindowManager.getRecentsAnimationController(); 297 if (controller == null) return; 298 299 // Just to be sure end the launch hint in case the target activity was never launched. 300 // However, if we're keeping the activity and making it visible, we can leave it on. 301 if (reorderMode != REORDER_KEEP_IN_PLACE) { 302 mService.endLaunchPowerMode( 303 ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY); 304 } 305 306 // Once the target is shown, prevent spurious background app switches 307 if (reorderMode == REORDER_MOVE_TO_TOP) { 308 mService.stopAppSwitches(); 309 } 310 311 mWindowManager.inSurfaceTransaction(() -> { 312 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, 313 "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); 314 mService.deferWindowLayout(); 315 try { 316 mWindowManager.cleanupRecentsAnimation(reorderMode); 317 318 final Task targetRootTask = mDefaultTaskDisplayArea.getRootTask( 319 WINDOWING_MODE_UNDEFINED, mTargetActivityType); 320 // Prefer to use the original target activity instead of top activity because 321 // we may have moved another task to top (starting 3p launcher). 322 final ActivityRecord targetActivity = targetRootTask != null 323 ? targetRootTask.isInTask(mLaunchedTargetActivity) 324 : null; 325 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 326 "onAnimationFinished(): targetRootTask=%s targetActivity=%s " 327 + "mRestoreTargetBehindRootTask=%s", 328 targetRootTask, targetActivity, mRestoreTargetBehindRootTask); 329 if (targetActivity == null) { 330 return; 331 } 332 333 // Restore the launched-behind state 334 targetActivity.mLaunchTaskBehind = false; 335 336 if (reorderMode == REORDER_MOVE_TO_TOP) { 337 // Bring the target root task to the front 338 mTaskSupervisor.mNoAnimActivities.add(targetActivity); 339 340 if (sendUserLeaveHint) { 341 // Setting this allows the previous app to PiP. 342 mTaskSupervisor.mUserLeaving = true; 343 targetRootTask.moveTaskToFront(targetActivity.getTask(), 344 true /* noAnimation */, null /* activityOptions */, 345 targetActivity.appTimeTracker, 346 "RecentsAnimation.onAnimationFinished()"); 347 } else { 348 targetRootTask.moveToFront("RecentsAnimation.onAnimationFinished()"); 349 } 350 351 if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) { 352 final Task topRootTask = getTopNonAlwaysOnTopRootTask(); 353 if (topRootTask != targetRootTask) { 354 ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS, 355 "Expected target rootTask=%s" 356 + " to be top most but found rootTask=%s", 357 targetRootTask, topRootTask); 358 } 359 } 360 } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){ 361 // Restore the target root task to its previous position 362 final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea(); 363 taskDisplayArea.moveRootTaskBehindRootTask(targetRootTask, 364 mRestoreTargetBehindRootTask); 365 if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) { 366 final Task aboveTargetRootTask = getRootTaskAbove(targetRootTask); 367 if (mRestoreTargetBehindRootTask != null 368 && aboveTargetRootTask != mRestoreTargetBehindRootTask) { 369 ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS, 370 "Expected target rootTask=%s to restored behind " 371 + "rootTask=%s but it is behind rootTask=%s", 372 targetRootTask, mRestoreTargetBehindRootTask, 373 aboveTargetRootTask); 374 } 375 } 376 } else { 377 // If there is no recents screenshot animation, we can update the visibility 378 // of target root task immediately because it is visually invisible and the 379 // launch-behind state is restored. That also prevents the next transition 380 // type being disturbed if the visibility is updated after setting the next 381 // transition (the target activity will be one of closing apps). 382 if (!controller.shouldDeferCancelWithScreenshot() 383 && !targetRootTask.isFocusedRootTaskOnDisplay()) { 384 targetRootTask.ensureActivitiesVisible(null /* starting */, 385 0 /* starting */, false /* preserveWindows */); 386 } 387 // Keep target root task in place, nothing changes, so ignore the transition 388 // logic below 389 return; 390 } 391 392 mWindowManager.prepareAppTransitionNone(); 393 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false); 394 mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); 395 396 // No reason to wait for the pausing activity in this case, as the hiding of 397 // surfaces needs to be done immediately. 398 mWindowManager.executeAppTransition(); 399 400 final Task rootTask = targetRootTask.getRootTask(); 401 // Client state may have changed during the recents animation, so force 402 // send task info so the client can synchronize its state. 403 rootTask.dispatchTaskInfoChangedIfNeeded(true /* force */); 404 } catch (Exception e) { 405 Slog.e(TAG, "Failed to clean up recents activity", e); 406 throw e; 407 } finally { 408 mTaskSupervisor.mUserLeaving = false; 409 mService.continueWindowLayout(); 410 // Make sure the surfaces are updated with the latest state. Sometimes the 411 // surface placement may be skipped if display configuration is changed (i.e. 412 // {@link DisplayContent#mWaitingForConfig} is true). 413 if (mWindowManager.mRoot.isLayoutNeeded()) { 414 mWindowManager.mRoot.performSurfacePlacement(); 415 } 416 setProcessAnimating(false); 417 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 418 } 419 }); 420 } 421 } 422 423 /** Gives the owner of recents animation higher priority. */ setProcessAnimating(boolean animating)424 private void setProcessAnimating(boolean animating) { 425 if (mCaller == null) return; 426 // Apply the top-app scheduling group to who runs the animation. 427 mCaller.setRunningRecentsAnimation(animating); 428 int demoteReasons = mService.mDemoteTopAppReasons; 429 if (animating) { 430 demoteReasons |= ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS; 431 } else { 432 demoteReasons &= ~ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS; 433 } 434 mService.mDemoteTopAppReasons = demoteReasons; 435 // Make the demotion of the real top app take effect. No need to restore top app state for 436 // finishing recents because addToStopping -> scheduleIdle -> activityIdleInternal -> 437 // trimApplications will have a full update. 438 if (animating && mService.mTopApp != null) { 439 mService.mTopApp.scheduleUpdateOomAdj(); 440 } 441 } 442 443 @Override onAnimationFinished(@ecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint)444 public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, 445 boolean sendUserLeaveHint) { 446 finishAnimation(reorderMode, sendUserLeaveHint); 447 } 448 449 @Override onRootTaskOrderChanged(Task rootTask)450 public void onRootTaskOrderChanged(Task rootTask) { 451 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onRootTaskOrderChanged(): rootTask=%s", rootTask); 452 if (mDefaultTaskDisplayArea.getRootTask(t -> t == rootTask) == null 453 || !rootTask.shouldBeVisible(null)) { 454 // The root task is not visible, so ignore this change 455 return; 456 } 457 final RecentsAnimationController controller = 458 mWindowManager.getRecentsAnimationController(); 459 if (controller == null) { 460 return; 461 } 462 463 // We defer canceling the recents animation until the next app transition in the following 464 // cases: 465 // 1) The next launching task is not being animated by the recents animation 466 // 2) The next task is home activity. (i.e. pressing home key to back home in recents). 467 if ((!controller.isAnimatingTask(rootTask.getTopMostTask()) 468 || controller.isTargetApp(rootTask.getTopNonFinishingActivity())) 469 && controller.shouldDeferCancelUntilNextTransition()) { 470 // Always prepare an app transition since we rely on the transition callbacks to cleanup 471 mWindowManager.prepareAppTransitionNone(); 472 controller.setCancelOnNextTransitionStart(); 473 } 474 } 475 startRecentsActivityInBackground(String reason)476 private void startRecentsActivityInBackground(String reason) { 477 final ActivityOptions options = ActivityOptions.makeBasic(); 478 options.setLaunchActivityType(mTargetActivityType); 479 options.setAvoidMoveToFront(); 480 mTargetIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); 481 482 mActivityStartController 483 .obtainStarter(mTargetIntent, reason) 484 .setCallingUid(mRecentsUid) 485 .setCallingPackage(mRecentsComponent.getPackageName()) 486 .setCallingFeatureId(mRecentsFeatureId) 487 .setActivityOptions(new SafeActivityOptions(options)) 488 .setUserId(mUserId) 489 .execute(); 490 } 491 492 /** 493 * Called only when the animation should be canceled prior to starting. 494 */ notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner)495 static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { 496 try { 497 recentsAnimationRunner.onAnimationCanceled(null /* taskIds */, 498 null /* taskSnapshots */); 499 } catch (RemoteException e) { 500 Slog.e(TAG, "Failed to cancel recents animation before start", e); 501 } 502 } 503 504 /** 505 * @return The top root task that is not always-on-top. 506 */ getTopNonAlwaysOnTopRootTask()507 private Task getTopNonAlwaysOnTopRootTask() { 508 return mDefaultTaskDisplayArea.getRootTask(task -> 509 !task.getWindowConfiguration().isAlwaysOnTop()); 510 } 511 512 /** 513 * @return the top activity in the {@param targetRootTask} matching the {@param component}, 514 * or just the top activity of the top task if no task matches the component. 515 */ getTargetActivity(Task targetRootTask)516 private ActivityRecord getTargetActivity(Task targetRootTask) { 517 if (targetRootTask == null) { 518 return null; 519 } 520 521 final PooledPredicate p = PooledLambda.obtainPredicate(RecentsAnimation::matchesTarget, 522 this, PooledLambda.__(Task.class)); 523 final Task task = targetRootTask.getTask(p); 524 p.recycle(); 525 return task != null ? task.getTopNonFinishingActivity() : null; 526 } 527 matchesTarget(Task task)528 private boolean matchesTarget(Task task) { 529 return task.getNonFinishingActivityCount() > 0 && task.mUserId == mUserId 530 && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent()); 531 } 532 } 533