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