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