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