RecentsAnimation.java revision 82389a9333718fd24ab1d7bc046b696074d65956
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
17package com.android.server.am;
18
19import static android.app.ActivityManager.START_TASK_TO_FRONT;
20import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
21import static android.app.AppOpsManager.OP_NONE;
22import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
23import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
24import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
25import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
26import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
27import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
28import static android.view.WindowManager.TRANSIT_NONE;
29import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
30import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
31import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
32import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
33
34import android.app.ActivityOptions;
35import android.app.AppOpsManager;
36import android.app.IAssistDataReceiver;
37import android.content.ComponentName;
38import android.content.Context;
39import android.content.Intent;
40import android.os.RemoteException;
41import android.os.Trace;
42import android.util.Slog;
43import android.view.IRecentsAnimationRunner;
44import com.android.server.wm.RecentsAnimationController;
45import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
46import com.android.server.wm.WindowManagerService;
47
48/**
49 * Manages the recents animation, including the reordering of the stacks for the transition and
50 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
51 */
52class RecentsAnimation implements RecentsAnimationCallbacks {
53    private static final String TAG = RecentsAnimation.class.getSimpleName();
54    // TODO (b/73188263): Reset debugging flags
55    private static final boolean DEBUG = true;
56
57    private final ActivityManagerService mService;
58    private final ActivityStackSupervisor mStackSupervisor;
59    private final ActivityStartController mActivityStartController;
60    private final WindowManagerService mWindowManager;
61    private final UserController mUserController;
62    private final ActivityDisplay mDefaultDisplay;
63    private final int mCallingPid;
64
65    private int mTargetActivityType;
66    private AssistDataRequester mAssistDataRequester;
67
68    // The stack to restore the target stack behind when the animation is finished
69    private ActivityStack mRestoreTargetBehindStack;
70
71    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
72            ActivityStartController activityStartController, WindowManagerService wm,
73            UserController userController, int callingPid) {
74        mService = am;
75        mStackSupervisor = stackSupervisor;
76        mDefaultDisplay = stackSupervisor.getDefaultDisplay();
77        mActivityStartController = activityStartController;
78        mWindowManager = wm;
79        mUserController = userController;
80        mCallingPid = callingPid;
81    }
82
83    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
84            ComponentName recentsComponent, int recentsUid,
85            IAssistDataReceiver assistDataReceiver) {
86        if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent
87                + " assistDataReceiver=" + assistDataReceiver);
88        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
89
90        if (!mWindowManager.canStartRecentsAnimation()) {
91            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
92            if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
93                        + mWindowManager.getPendingAppTransition());
94            return;
95        }
96
97        // If the activity is associated with the recents stack, then try and get that first
98        mTargetActivityType = intent.getComponent() != null
99                && recentsComponent.equals(intent.getComponent())
100                        ? ACTIVITY_TYPE_RECENTS
101                        : ACTIVITY_TYPE_HOME;
102        final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
103                mTargetActivityType);
104        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
105        final boolean hasExistingActivity = targetActivity != null;
106        if (hasExistingActivity) {
107            final ActivityDisplay display = targetActivity.getDisplay();
108            mRestoreTargetBehindStack = display.getStackAbove(targetStack);
109            if (mRestoreTargetBehindStack == null) {
110                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
111                if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
112                return;
113            }
114        }
115
116        // Send launch hint if we are actually launching the target. If it's already visible
117        // (shouldn't happen in general) we don't need to send it.
118        if (targetActivity == null || !targetActivity.visible) {
119            mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
120                    targetActivity);
121        }
122
123        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
124
125        mService.setRunningRemoteAnimation(mCallingPid, true);
126
127        mWindowManager.deferSurfaceLayout();
128        try {
129            // Kick off the assist data request in the background before showing the target activity
130            if (assistDataReceiver != null) {
131                final AppOpsManager appOpsManager = (AppOpsManager)
132                        mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
133                final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
134                        assistDataReceiver, recentsComponent.getPackageName());
135                mAssistDataRequester = new AssistDataRequester(mService.mContext, mService,
136                        mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
137                mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
138                        true /* fetchData */, false /* fetchScreenshots */,
139                        true /* allowFetchData */, false /* allowFetchScreenshots */,
140                        recentsUid, recentsComponent.getPackageName());
141            }
142
143            final ActivityDisplay display;
144            if (hasExistingActivity) {
145                // Move the recents activity into place for the animation if it is not top most
146                display = targetActivity.getDisplay();
147                display.moveStackBehindBottomMostVisibleStack(targetStack);
148                if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
149                            + display.getStackAbove(targetStack));
150
151                // If there are multiple tasks in the target stack (ie. the home stack, with 3p
152                // and default launchers coexisting), then move the task to the top as a part of
153                // moving the stack to the front
154                if (targetStack.topTask() != targetActivity.getTask()) {
155                    targetStack.addTask(targetActivity.getTask(), true /* toTop */,
156                            "startRecentsActivity");
157                }
158            } else {
159                // No recents activity
160                ActivityOptions options = ActivityOptions.makeBasic();
161                options.setLaunchActivityType(mTargetActivityType);
162                options.setAvoidMoveToFront();
163                intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
164
165                mActivityStartController
166                        .obtainStarter(intent, "startRecentsActivity_noTargetActivity")
167                        .setCallingUid(recentsUid)
168                        .setCallingPackage(recentsComponent.getPackageName())
169                        .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
170                        .setMayWait(mUserController.getCurrentUserId())
171                        .execute();
172                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
173
174                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
175                        mTargetActivityType).getTopActivity();
176                display = targetActivity.getDisplay();
177
178                // TODO: Maybe wait for app to draw in this particular case?
179
180                if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
181            }
182
183            // Mark the target activity as launch-behind to bump its visibility for the
184            // duration of the gesture that is driven by the recents component
185            targetActivity.mLaunchTaskBehind = true;
186
187            // Fetch all the surface controls and pass them to the client to get the animation
188            // started. Cancel any existing recents animation running synchronously (do not hold the
189            // WM lock)
190            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
191                    "startRecentsActivity");
192            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
193                    this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
194
195            // If we updated the launch-behind state, update the visibility of the activities after
196            // we fetch the visible tasks to be controlled by the animation
197            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
198
199            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
200                    targetActivity);
201        } catch (Exception e) {
202            Slog.e(TAG, "Failed to start recents activity", e);
203            throw e;
204        } finally {
205            mWindowManager.continueSurfaceLayout();
206            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
207        }
208    }
209
210    private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
211        synchronized (mService) {
212            if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
213                    + mWindowManager.getRecentsAnimationController()
214                    + " reorderMode=" + reorderMode);
215
216            // Cancel the associated assistant data request
217            if (mAssistDataRequester != null) {
218                mAssistDataRequester.cancel();
219                mAssistDataRequester = null;
220            }
221
222            if (mWindowManager.getRecentsAnimationController() == null) return;
223
224            // Just to be sure end the launch hint in case the target activity was never launched.
225            // However, if we're keeping the activity and making it visible, we can leave it on.
226            if (reorderMode != REORDER_KEEP_IN_PLACE) {
227                mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
228            }
229
230            mService.setRunningRemoteAnimation(mCallingPid, false);
231
232            mWindowManager.inSurfaceTransaction(() -> {
233                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
234                        "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
235                mWindowManager.deferSurfaceLayout();
236                try {
237                    mWindowManager.cleanupRecentsAnimation(reorderMode);
238
239                    final ActivityStack targetStack = mDefaultDisplay.getStack(
240                            WINDOWING_MODE_UNDEFINED, mTargetActivityType);
241                    final ActivityRecord targetActivity = targetStack != null
242                            ? targetStack.getTopActivity()
243                            : null;
244                    if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
245                            + " targetActivity=" + targetActivity
246                            + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
247                    if (targetActivity == null) {
248                        return;
249                    }
250
251                    // Restore the launched-behind state
252                    targetActivity.mLaunchTaskBehind = false;
253
254                    if (reorderMode == REORDER_MOVE_TO_TOP) {
255                        // Bring the target stack to the front
256                        mStackSupervisor.mNoAnimActivities.add(targetActivity);
257                        targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
258                        if (DEBUG) {
259                            final ActivityStack topStack = getTopNonAlwaysOnTopStack();
260                            if (topStack != targetStack) {
261                                Slog.w(TAG, "Expected target stack=" + targetStack
262                                        + " to be top most but found stack=" + topStack);
263                            }
264                        }
265                    } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
266                        // Restore the target stack to its previous position
267                        final ActivityDisplay display = targetActivity.getDisplay();
268                        display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
269                        if (DEBUG) {
270                            final ActivityStack aboveTargetStack =
271                                    mDefaultDisplay.getStackAbove(targetStack);
272                            if (mRestoreTargetBehindStack != null
273                                    && aboveTargetStack != mRestoreTargetBehindStack) {
274                                Slog.w(TAG, "Expected target stack=" + targetStack
275                                        + " to restored behind stack=" + mRestoreTargetBehindStack
276                                        + " but it is behind stack=" + aboveTargetStack);
277                            }
278                        }
279                    } else {
280                        // Keep target stack in place, nothing changes, so ignore the transition
281                        // logic below
282                        return;
283                    }
284
285                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
286                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
287                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
288
289                    // No reason to wait for the pausing activity in this case, as the hiding of
290                    // surfaces needs to be done immediately.
291                    mWindowManager.executeAppTransition();
292
293                    // After reordering the stacks, reset the minimized state. At this point, either
294                    // the target activity is now top-most and we will stay minimized (if in
295                    // split-screen), or we will have returned to the app, and the minimized state
296                    // should be reset
297                    mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
298                } catch (Exception e) {
299                    Slog.e(TAG, "Failed to clean up recents activity", e);
300                    throw e;
301                } finally {
302                    mWindowManager.continueSurfaceLayout();
303                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
304                }
305            });
306        }
307    }
308
309    @Override
310    public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
311            boolean runSychronously) {
312        if (runSychronously) {
313            finishAnimation(reorderMode);
314        } else {
315            mService.mHandler.post(() -> finishAnimation(reorderMode));
316        }
317    }
318
319    /**
320     * Called only when the animation should be canceled prior to starting.
321     */
322    private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
323        try {
324            recentsAnimationRunner.onAnimationCanceled();
325        } catch (RemoteException e) {
326            Slog.e(TAG, "Failed to cancel recents animation before start", e);
327        }
328    }
329
330    /**
331     * @return The top stack that is not always-on-top.
332     */
333    private ActivityStack getTopNonAlwaysOnTopStack() {
334        for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
335            final ActivityStack s = mDefaultDisplay.getChildAt(i);
336            if (s.getWindowConfiguration().isAlwaysOnTop()) {
337                continue;
338            }
339            return s;
340        }
341        return null;
342    }
343
344    /**
345     * @return the top activity in the {@param targetStack} matching the {@param component}, or just
346     * the top activity of the top task if no task matches the component.
347     */
348    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
349        if (targetStack == null) {
350            return null;
351        }
352
353        for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
354            final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
355            if (task.getBaseIntent().getComponent().equals(component)) {
356                return task.getTopActivity();
357            }
358        }
359        return targetStack.getTopActivity();
360    }
361}
362