RecentsAnimation.java revision 54cff64ec6ef818e270eb39a74d6a58068553d66
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.WindowConfiguration.ACTIVITY_TYPE_HOME;
21import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
22import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
23import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
24import static android.view.WindowManager.TRANSIT_NONE;
25import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
26
27import android.app.ActivityOptions;
28import android.content.ComponentName;
29import android.content.Intent;
30import android.os.Handler;
31import android.os.RemoteException;
32import android.os.Trace;
33import android.util.Slog;
34import android.view.IRecentsAnimationRunner;
35import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
36import com.android.server.wm.WindowManagerService;
37
38/**
39 * Manages the recents animation, including the reordering of the stacks for the transition and
40 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
41 */
42class RecentsAnimation implements RecentsAnimationCallbacks {
43    private static final String TAG = RecentsAnimation.class.getSimpleName();
44
45    private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
46
47    private final ActivityManagerService mService;
48    private final ActivityStackSupervisor mStackSupervisor;
49    private final ActivityStartController mActivityStartController;
50    private final WindowManagerService mWindowManager;
51    private final UserController mUserController;
52    private final Handler mHandler;
53    private final int mCallingPid;
54
55    private final Runnable mCancelAnimationRunnable;
56
57    // The stack to restore the home stack behind when the animation is finished
58    private ActivityStack mRestoreHomeBehindStack;
59
60    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
61            ActivityStartController activityStartController, WindowManagerService wm,
62            UserController userController, int callingPid) {
63        mService = am;
64        mStackSupervisor = stackSupervisor;
65        mActivityStartController = activityStartController;
66        mHandler = new Handler(mStackSupervisor.mLooper);
67        mWindowManager = wm;
68        mUserController = userController;
69        mCallingPid = callingPid;
70
71        mCancelAnimationRunnable = () -> {
72            // The caller has not finished the animation in a predefined amount of time, so
73            // force-cancel the animation
74            mWindowManager.cancelRecentsAnimation();
75        };
76    }
77
78    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
79            ComponentName recentsComponent, int recentsUid) {
80        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
81
82        if (!mWindowManager.canStartRecentsAnimation()) {
83            notifyAnimationCancelBeforeStart(recentsAnimationRunner);
84            return;
85        }
86
87        // If the existing home activity is already on top, then cancel
88        ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
89        final boolean hasExistingHomeActivity = homeActivity != null;
90        if (hasExistingHomeActivity) {
91            final ActivityDisplay display = homeActivity.getDisplay();
92            mRestoreHomeBehindStack = display.getStackAboveHome();
93            if (mRestoreHomeBehindStack == null) {
94                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
95                return;
96            }
97        }
98
99        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
100
101        mService.setRunningRemoteAnimation(mCallingPid, true);
102
103        mWindowManager.deferSurfaceLayout();
104        try {
105            final ActivityDisplay display;
106            if (hasExistingHomeActivity) {
107                // Move the home activity into place for the animation if it is not already top most
108                display = homeActivity.getDisplay();
109                display.moveHomeStackBehindBottomMostVisibleStack();
110            } else {
111                // No home activity
112                final ActivityOptions opts = ActivityOptions.makeBasic();
113                opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
114                opts.setAvoidMoveToFront();
115                intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
116
117                mActivityStartController
118                        .obtainStarter(intent, "startRecentsActivity_noHomeActivity")
119                        .setCallingUid(recentsUid)
120                        .setCallingPackage(recentsComponent.getPackageName())
121                        .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
122                        .setMayWait(mUserController.getCurrentUserId())
123                        .execute();
124                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
125
126                homeActivity = mStackSupervisor.getHomeActivity();
127                display = homeActivity.getDisplay();
128
129                // TODO: Maybe wait for app to draw in this particular case?
130            }
131
132            // Mark the home activity as launch-behind to bump its visibility for the
133            // duration of the gesture that is driven by the recents component
134            homeActivity.mLaunchTaskBehind = true;
135
136            // Post a timeout for the animation. This needs to happen before initializing the
137            // recents animation on the WM side since we may decide to cancel the animation there
138            mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
139
140            // Fetch all the surface controls and pass them to the client to get the animation
141            // started
142            mWindowManager.cancelRecentsAnimation();
143            mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
144                    display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
145
146            // If we updated the launch-behind state, update the visibility of the activities after
147            // we fetch the visible tasks to be controlled by the animation
148            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
149
150            mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
151                    homeActivity);
152        } finally {
153            mWindowManager.continueSurfaceLayout();
154            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
155        }
156    }
157
158    @Override
159    public void onAnimationFinished(boolean moveHomeToTop) {
160        mHandler.removeCallbacks(mCancelAnimationRunnable);
161        synchronized (mService) {
162            if (mWindowManager.getRecentsAnimationController() == null) return;
163
164            mService.setRunningRemoteAnimation(mCallingPid, false);
165
166            mWindowManager.inSurfaceTransaction(() -> {
167                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
168                        "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
169                mWindowManager.deferSurfaceLayout();
170                try {
171                    mWindowManager.cleanupRecentsAnimation();
172
173                    // Move the home stack to the front
174                    final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
175                    if (homeActivity == null) {
176                        return;
177                    }
178
179                    // Restore the launched-behind state
180                    homeActivity.mLaunchTaskBehind = false;
181
182                    if (moveHomeToTop) {
183                        // Bring the home stack to the front
184                        final ActivityStack homeStack = homeActivity.getStack();
185                        mStackSupervisor.mNoAnimActivities.add(homeActivity);
186                        homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
187                    } else {
188                        // Restore the home stack to its previous position
189                        final ActivityDisplay display = homeActivity.getDisplay();
190                        display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
191                    }
192
193                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
194                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
195                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
196
197                    // No reason to wait for the pausing activity in this case, as the hiding of
198                    // surfaces needs to be done immediately.
199                    mWindowManager.executeAppTransition();
200                } finally {
201                    mWindowManager.continueSurfaceLayout();
202                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
203                }
204            });
205        }
206    }
207
208    /**
209     * Called only when the animation should be canceled prior to starting.
210     */
211    private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
212        try {
213            recentsAnimationRunner.onAnimationCanceled();
214        } catch (RemoteException e) {
215            Slog.e(TAG, "Failed to cancel recents animation before start", e);
216        }
217    }
218}
219