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