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