1
2package com.android.server.wm;
3
4import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
5import static android.app.ActivityManagerInternal.APP_TRANSITION_SAVED_SURFACE;
6import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
7import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
8import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
9import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
10import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
11import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
12import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
13import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
14import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
15import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
16import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
17import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
18import static com.android.server.wm.AppTransition.TRANSIT_NONE;
19import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
20import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
21import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
22import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
23import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
24import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE;
25import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
26import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
27import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN;
28import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
29import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
30import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
31import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
32import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
33import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
34import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
35import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
36import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
37import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
38import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
39import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
40
41import android.content.res.Configuration;
42import android.graphics.GraphicBuffer;
43import android.graphics.PixelFormat;
44import android.graphics.Rect;
45import android.os.Binder;
46import android.os.Debug;
47import android.os.Trace;
48import android.util.ArraySet;
49import android.util.Slog;
50import android.util.SparseIntArray;
51import android.view.Display;
52import android.view.DisplayInfo;
53import android.view.Surface;
54import android.view.SurfaceControl;
55import android.view.WindowManager.LayoutParams;
56import android.view.animation.Animation;
57
58import com.android.server.wm.WindowManagerService.H;
59
60import java.io.PrintWriter;
61import java.util.ArrayList;
62
63/**
64 * Positions windows and their surfaces.
65 *
66 * It sets positions of windows by calculating their frames and then applies this by positioning
67 * surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
68 */
69class WindowSurfacePlacer {
70    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
71    private final WindowManagerService mService;
72    private final WallpaperController mWallpaperControllerLocked;
73
74    private boolean mInLayout = false;
75
76    /** Only do a maximum of 6 repeated layouts. After that quit */
77    private int mLayoutRepeatCount;
78
79    static final int SET_UPDATE_ROTATION                = 1 << 0;
80    static final int SET_WALLPAPER_MAY_CHANGE           = 1 << 1;
81    static final int SET_FORCE_HIDING_CHANGED           = 1 << 2;
82    static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 3;
83    static final int SET_TURN_ON_SCREEN                 = 1 << 4;
84    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 5;
85
86    private final Rect mTmpStartRect = new Rect();
87    private final Rect mTmpContentRect = new Rect();
88
89    private boolean mTraversalScheduled;
90    private int mDeferDepth = 0;
91
92    private static final class LayerAndToken {
93        public int layer;
94        public AppWindowToken token;
95    }
96    private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
97
98    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
99    private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
100
101    private final Runnable mPerformSurfacePlacement;
102
103    public WindowSurfacePlacer(WindowManagerService service) {
104        mService = service;
105        mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
106        mPerformSurfacePlacement = () -> {
107            synchronized (mService.mWindowMap) {
108                performSurfacePlacement();
109            }
110        };
111    }
112
113    /**
114     * See {@link WindowManagerService#deferSurfaceLayout()}
115     */
116    void deferLayout() {
117        mDeferDepth++;
118    }
119
120    /**
121     * See {@link WindowManagerService#continueSurfaceLayout()}
122     */
123    void continueLayout() {
124        mDeferDepth--;
125        if (mDeferDepth <= 0) {
126            performSurfacePlacement();
127        }
128    }
129
130    final void performSurfacePlacement() {
131        performSurfacePlacement(false /* force */);
132    }
133
134    final void performSurfacePlacement(boolean force) {
135        if (mDeferDepth > 0 && !force) {
136            return;
137        }
138        int loopCount = 6;
139        do {
140            mTraversalScheduled = false;
141            performSurfacePlacementLoop();
142            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
143            loopCount--;
144        } while (mTraversalScheduled && loopCount > 0);
145        mService.mRoot.mWallpaperActionPending = false;
146    }
147
148    private void performSurfacePlacementLoop() {
149        if (mInLayout) {
150            if (DEBUG) {
151                throw new RuntimeException("Recursive call!");
152            }
153            Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
154                    + Debug.getCallers(3));
155            return;
156        }
157
158        if (mService.mWaitingForConfig) {
159            // Our configuration has changed (most likely rotation), but we
160            // don't yet have the complete configuration to report to
161            // applications.  Don't do any window layout until we have it.
162            return;
163        }
164
165        if (!mService.mDisplayReady) {
166            // Not yet initialized, nothing to do.
167            return;
168        }
169
170        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
171        mInLayout = true;
172
173        boolean recoveringMemory = false;
174        if (!mService.mForceRemoves.isEmpty()) {
175            recoveringMemory = true;
176            // Wait a little bit for things to settle down, and off we go.
177            while (!mService.mForceRemoves.isEmpty()) {
178                final WindowState ws = mService.mForceRemoves.remove(0);
179                Slog.i(TAG, "Force removing: " + ws);
180                ws.removeImmediately();
181            }
182            Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
183            Object tmp = new Object();
184            synchronized (tmp) {
185                try {
186                    tmp.wait(250);
187                } catch (InterruptedException e) {
188                }
189            }
190        }
191
192        try {
193            mService.mRoot.performSurfacePlacement(recoveringMemory);
194
195            mInLayout = false;
196
197            if (mService.mRoot.isLayoutNeeded()) {
198                if (++mLayoutRepeatCount < 6) {
199                    requestTraversal();
200                } else {
201                    Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
202                    mLayoutRepeatCount = 0;
203                }
204            } else {
205                mLayoutRepeatCount = 0;
206            }
207
208            if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
209                mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
210                mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
211            }
212        } catch (RuntimeException e) {
213            mInLayout = false;
214            Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
215        }
216
217        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
218    }
219
220    void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
221        if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
222            Slog.v(TAG, "Layouts looping: " + msg +
223                    ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges));
224        }
225    }
226
227    boolean isInLayout() {
228        return mInLayout;
229    }
230
231    /**
232     * @return bitmap indicating if another pass through layout must be made.
233     */
234    int handleAppTransitionReadyLocked() {
235        int appsCount = mService.mOpeningApps.size();
236        if (!transitionGoodToGo(appsCount)) {
237            return 0;
238        }
239        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
240
241        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
242        int transit = mService.mAppTransition.getAppTransition();
243        if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
244            transit = AppTransition.TRANSIT_UNSET;
245        }
246        mService.mSkipAppTransitionAnimation = false;
247        mService.mNoAnimationNotifyOnTransitionFinished.clear();
248
249        mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
250
251        final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
252        // TODO: Don't believe this is really needed...
253        //mService.mWindowsChanged = true;
254
255        mService.mRoot.mWallpaperMayChange = false;
256
257        // The top-most window will supply the layout params, and we will determine it below.
258        LayoutParams animLp = null;
259        int bestAnimLayer = -1;
260        boolean fullscreenAnim = false;
261        boolean voiceInteraction = false;
262
263        int i;
264        for (i = 0; i < appsCount; i++) {
265            final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
266            // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
267            // window is removed, or window relayout to invisible. This also affects window
268            // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
269            // transition selection depends on wallpaper target visibility.
270            wtoken.clearAnimatingFlags();
271
272        }
273
274        // Adjust wallpaper before we pull the lower/upper target, since pending changes
275        // (like the clearAnimatingFlags() above) might affect wallpaper target result.
276        // Or, the opening app window should be a wallpaper target.
277        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
278                mService.mOpeningApps);
279
280        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
281        boolean openingAppHasWallpaper = false;
282        boolean closingAppHasWallpaper = false;
283
284        // Do a first pass through the tokens for two things:
285        // (1) Determine if both the closing and opening app token sets are wallpaper targets, in
286        // which case special animations are needed (since the wallpaper needs to stay static behind
287        // them).
288        // (2) Find the layout params of the top-most application window in the tokens, which is
289        // what will control the animation theme.
290        final int closingAppsCount = mService.mClosingApps.size();
291        appsCount = closingAppsCount + mService.mOpeningApps.size();
292        for (i = 0; i < appsCount; i++) {
293            final AppWindowToken wtoken;
294            if (i < closingAppsCount) {
295                wtoken = mService.mClosingApps.valueAt(i);
296                if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
297                    closingAppHasWallpaper = true;
298                }
299            } else {
300                wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
301                if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
302                    openingAppHasWallpaper = true;
303                }
304            }
305
306            voiceInteraction |= wtoken.mVoiceInteraction;
307
308            if (wtoken.fillsParent()) {
309                final WindowState ws = wtoken.findMainWindow();
310                if (ws != null) {
311                    animLp = ws.mAttrs;
312                    bestAnimLayer = ws.mLayer;
313                    fullscreenAnim = true;
314                }
315            } else if (!fullscreenAnim) {
316                final WindowState ws = wtoken.findMainWindow();
317                if (ws != null) {
318                    if (ws.mLayer > bestAnimLayer) {
319                        animLp = ws.mAttrs;
320                        bestAnimLayer = ws.mLayer;
321                    }
322                }
323            }
324        }
325
326        transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
327                closingAppHasWallpaper);
328
329        // If all closing windows are obscured, then there is no need to do an animation. This is
330        // the case, for example, when this transition is being done behind the lock screen.
331        if (!mService.mPolicy.allowAppAnimationsLw()) {
332            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
333                    "Animations disallowed by keyguard or dream.");
334            animLp = null;
335        }
336
337        processApplicationsAnimatingInPlace(transit);
338
339        mTmpLayerAndToken.token = null;
340        handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
341        final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
342        final int topClosingLayer = mTmpLayerAndToken.layer;
343
344        final AppWindowToken topOpeningApp = handleOpeningApps(transit,
345                animLp, voiceInteraction, topClosingLayer);
346
347        mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
348
349        final AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ?  null :
350                topOpeningApp.mAppAnimator;
351        final AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
352                topClosingApp.mAppAnimator;
353
354        final int flags = mService.mAppTransition.getTransitFlags();
355        int layoutRedo = mService.mAppTransition.goodToGo(transit, openingAppAnimator,
356                closingAppAnimator, mService.mOpeningApps, mService.mClosingApps);
357        handleNonAppWindowsInTransition(transit, flags);
358        mService.mAppTransition.postAnimationCallback();
359        mService.mAppTransition.clear();
360
361        mService.mTaskSnapshotController.onTransitionStarting();
362
363        mService.mOpeningApps.clear();
364        mService.mClosingApps.clear();
365        mService.mUnknownAppVisibilityController.clear();
366
367        // This has changed the visibility of windows, so perform
368        // a new layout to get them all up-to-date.
369        displayContent.setLayoutNeeded();
370
371        // TODO(multidisplay): IMEs are only supported on the default display.
372        final DisplayContent dc = mService.getDefaultDisplayContentLocked();
373        dc.computeImeTarget(true /* updateImeTarget */);
374        mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
375                true /*updateInputWindows*/);
376        mService.mFocusMayChange = false;
377
378        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
379
380        return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
381    }
382
383    private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
384            boolean voiceInteraction, int topClosingLayer) {
385        AppWindowToken topOpeningApp = null;
386        final int appsCount = mService.mOpeningApps.size();
387        for (int i = 0; i < appsCount; i++) {
388            AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
389            final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
390            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
391
392            if (!appAnimator.usingTransferredAnimation) {
393                appAnimator.clearThumbnail();
394                appAnimator.setNullAnimation();
395            }
396
397            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)){
398                // This token isn't going to be animating. Add it to the list of tokens to
399                // be notified of app transition complete since the notification will not be
400                // sent be the app window animator.
401                mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
402            }
403            wtoken.updateReportedVisibilityLocked();
404            wtoken.waitingToShow = false;
405            wtoken.setAllAppWinAnimators();
406
407            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
408                    ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
409            mService.openSurfaceTransaction();
410            try {
411                mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
412            } finally {
413                mService.closeSurfaceTransaction();
414                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
415                        "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()");
416            }
417            mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
418
419            int topOpeningLayer = 0;
420            if (animLp != null) {
421                final int layer = wtoken.getHighestAnimLayer();
422                if (topOpeningApp == null || layer > topOpeningLayer) {
423                    topOpeningApp = wtoken;
424                    topOpeningLayer = layer;
425                }
426            }
427            if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
428                createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
429            }
430        }
431        return topOpeningApp;
432    }
433
434    private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction,
435            LayerAndToken layerAndToken) {
436        final int appsCount;
437        appsCount = mService.mClosingApps.size();
438        for (int i = 0; i < appsCount; i++) {
439            AppWindowToken wtoken = mService.mClosingApps.valueAt(i);
440
441            // If we still have some windows animating with saved surfaces that's
442            // either invisible or already removed, mark them exiting so that they
443            // are disposed of after the exit animation. These are not supposed to
444            // be shown, or are delayed removal until app is actually drawn (in which
445            // case the window will be removed after the animation).
446            wtoken.markSavedSurfaceExiting();
447
448            final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
449            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
450            appAnimator.clearThumbnail();
451            appAnimator.setNullAnimation();
452            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
453            //       animating?
454            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
455            wtoken.updateReportedVisibilityLocked();
456            // Force the allDrawn flag, because we want to start
457            // this guy's animations regardless of whether it's
458            // gotten drawn.
459            wtoken.allDrawn = true;
460            wtoken.deferClearAllDrawn = false;
461            // Ensure that apps that are mid-starting are also scheduled to have their
462            // starting windows removed after the animation is complete
463            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
464                    && wtoken.getController() != null) {
465                wtoken.getController().removeStartingWindow();
466            }
467            mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
468
469            if (animLp != null) {
470                int layer = wtoken.getHighestAnimLayer();
471                if (layerAndToken.token == null || layer > layerAndToken.layer) {
472                    layerAndToken.token = wtoken;
473                    layerAndToken.layer = layer;
474                }
475            }
476            if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
477                createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
478            }
479        }
480    }
481
482    private void handleNonAppWindowsInTransition(int transit, int flags) {
483        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
484            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
485                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
486                Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
487                        (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
488                if (anim != null) {
489                    mService.getDefaultDisplayContentLocked().mWallpaperController
490                            .startWallpaperAnimation(anim);
491                }
492            }
493        }
494        if (transit == TRANSIT_KEYGUARD_GOING_AWAY
495                || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
496            mService.getDefaultDisplayContentLocked().startKeyguardExitOnNonAppWindows(
497                    transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
498                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
499        }
500    }
501
502    private boolean transitionGoodToGo(int appsCount) {
503        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
504                "Checking " + appsCount + " opening apps (frozen="
505                        + mService.mDisplayFrozen + " timeout="
506                        + mService.mAppTransition.isTimeout() + ")...");
507        final ScreenRotationAnimation screenRotationAnimation =
508            mService.mAnimator.getScreenRotationAnimationLocked(
509                    Display.DEFAULT_DISPLAY);
510
511        final SparseIntArray reasons = mTempTransitionReasons;
512        if (!mService.mAppTransition.isTimeout()) {
513            // Imagine the case where we are changing orientation due to an app transition, but a previous
514            // orientation change is still in progress. We won't process the orientation change
515            // for our transition because we need to wait for the rotation animation to finish.
516            // If we start the app transition at this point, we will interrupt it halfway with a new rotation
517            // animation after the old one finally finishes. It's better to defer the
518            // app transition.
519            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
520                    mService.rotationNeedsUpdateLocked()) {
521                if (DEBUG_APP_TRANSITIONS) {
522                    Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
523                }
524                return false;
525            }
526            for (int i = 0; i < appsCount; i++) {
527                AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
528                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
529                        "Check opening app=" + wtoken + ": allDrawn="
530                        + wtoken.allDrawn + " startingDisplayed="
531                        + wtoken.startingDisplayed + " startingMoved="
532                        + wtoken.startingMoved + " isRelaunching()="
533                        + wtoken.isRelaunching());
534
535                final boolean drawnBeforeRestoring = wtoken.allDrawn;
536                wtoken.restoreSavedSurfaceForInterestingWindows();
537
538                final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
539                if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
540                    return false;
541                }
542                final TaskStack stack = wtoken.getStack();
543                final int stackId = stack != null ? stack.mStackId : INVALID_STACK_ID;
544                if (allDrawn) {
545                    reasons.put(stackId, drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
546                            : APP_TRANSITION_SAVED_SURFACE);
547                } else {
548                    reasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
549                            ? APP_TRANSITION_SPLASH_SCREEN
550                            : APP_TRANSITION_SNAPSHOT);
551                }
552            }
553
554            // We also need to wait for the specs to be fetched, if needed.
555            if (mService.mAppTransition.isFetchingAppTransitionsSpecs()) {
556                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
557                return false;
558            }
559
560            if (!mService.mUnknownAppVisibilityController.allResolved()) {
561                if (DEBUG_APP_TRANSITIONS) {
562                    Slog.v(TAG, "unknownApps is not empty: "
563                            + mService.mUnknownAppVisibilityController.getDebugMessage());
564                }
565                return false;
566            }
567
568            // If the wallpaper is visible, we need to check it's ready too.
569            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
570                    mWallpaperControllerLocked.wallpaperTransitionReady();
571            if (wallpaperReady) {
572                mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone())
573                        .sendToTarget();
574                return true;
575            }
576            return false;
577        }
578        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone()).sendToTarget();
579        return true;
580    }
581
582    private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
583            boolean closingAppHasWallpaper) {
584        // Given no app transition pass it through instead of a wallpaper transition
585        if (transit == TRANSIT_NONE) {
586            return TRANSIT_NONE;
587        }
588
589        // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
590        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
591        final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating()
592                ? null : wallpaperTarget;
593        final ArraySet<AppWindowToken> openingApps = mService.mOpeningApps;
594        final ArraySet<AppWindowToken> closingApps = mService.mClosingApps;
595        boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
596        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
597                "New wallpaper target=" + wallpaperTarget
598                        + ", oldWallpaper=" + oldWallpaper
599                        + ", openingApps=" + openingApps
600                        + ", closingApps=" + closingApps);
601        mService.mAnimateWallpaperWithTarget = false;
602        if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
603            transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
604            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
605                    "New transit: " + AppTransition.appTransitionToString(transit));
606        }
607        // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
608        // relies on the fact that we always execute a Keyguard transition after preparing one.
609        else if (!isKeyguardGoingAwayTransit(transit)) {
610            if (closingAppHasWallpaper && openingAppHasWallpaper) {
611                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
612                switch (transit) {
613                    case TRANSIT_ACTIVITY_OPEN:
614                    case TRANSIT_TASK_OPEN:
615                    case TRANSIT_TASK_TO_FRONT:
616                        transit = TRANSIT_WALLPAPER_INTRA_OPEN;
617                        break;
618                    case TRANSIT_ACTIVITY_CLOSE:
619                    case TRANSIT_TASK_CLOSE:
620                    case TRANSIT_TASK_TO_BACK:
621                        transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
622                        break;
623                }
624                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
625                        "New transit: " + AppTransition.appTransitionToString(transit));
626            } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
627                    && !openingApps.contains(oldWallpaper.mAppToken)
628                    && closingApps.contains(oldWallpaper.mAppToken)) {
629                // We are transitioning from an activity with a wallpaper to one without.
630                transit = TRANSIT_WALLPAPER_CLOSE;
631                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
632                        + AppTransition.appTransitionToString(transit));
633            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
634                    openingApps.contains(wallpaperTarget.mAppToken)) {
635                // We are transitioning from an activity without
636                // a wallpaper to now showing the wallpaper
637                transit = TRANSIT_WALLPAPER_OPEN;
638                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
639                        + AppTransition.appTransitionToString(transit));
640            } else {
641                mService.mAnimateWallpaperWithTarget = true;
642            }
643        }
644        return transit;
645    }
646
647    private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
648        for (int i = apps.size() - 1; i >= 0; i--) {
649            if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
650                return true;
651            }
652        }
653        return false;
654    }
655
656    private void processApplicationsAnimatingInPlace(int transit) {
657        if (transit == TRANSIT_TASK_IN_PLACE) {
658            // Find the focused window
659            final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
660            if (win != null) {
661                final AppWindowToken wtoken = win.mAppToken;
662                final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
663                if (DEBUG_APP_TRANSITIONS)
664                    Slog.v(TAG, "Now animating app in place " + wtoken);
665                appAnimator.clearThumbnail();
666                appAnimator.setNullAnimation();
667                mService.updateTokenInPlaceLocked(wtoken, transit);
668                wtoken.updateReportedVisibilityLocked();
669                wtoken.setAllAppWinAnimators();
670                mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
671                mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
672            }
673        }
674    }
675
676    private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
677            int openingLayer, int closingLayer) {
678        AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
679        if (openingAppAnimator == null || openingAppAnimator.animation == null) {
680            return;
681        }
682        final int taskId = appToken.getTask().mTaskId;
683        final GraphicBuffer thumbnailHeader =
684                mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
685        if (thumbnailHeader == null) {
686            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
687            return;
688        }
689        // This thumbnail animation is very special, we need to have
690        // an extra surface with the thumbnail included with the animation.
691        Rect dirty = new Rect(0, 0, thumbnailHeader.getWidth(), thumbnailHeader.getHeight());
692        try {
693            // TODO(multi-display): support other displays
694            final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
695            final Display display = displayContent.getDisplay();
696            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
697
698            // Create a new surface for the thumbnail
699            WindowState window = appToken.findMainWindow();
700            SurfaceControl surfaceControl = new SurfaceControl(mService.mFxSession,
701                    "thumbnail anim", dirty.width(), dirty.height(),
702                    PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN,
703                    appToken.windowType,
704                    window != null ? window.mOwnerUid : Binder.getCallingUid());
705            surfaceControl.setLayerStack(display.getLayerStack());
706            if (SHOW_TRANSACTIONS) {
707                Slog.i(TAG, "  THUMBNAIL " + surfaceControl + ": CREATE");
708            }
709
710            // Transfer the thumbnail to the surface
711            Surface drawSurface = new Surface();
712            drawSurface.copyFrom(surfaceControl);
713            drawSurface.attachAndQueueBuffer(thumbnailHeader);
714            drawSurface.release();
715
716            // Get the thumbnail animation
717            Animation anim;
718            if (mService.mAppTransition.isNextThumbnailTransitionAspectScaled()) {
719                // If this is a multi-window scenario, we use the windows frame as
720                // destination of the thumbnail header animation. If this is a full screen
721                // window scenario, we use the whole display as the target.
722                WindowState win = appToken.findMainWindow();
723                Rect appRect = win != null ? win.getContentFrameLw() :
724                        new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
725                Rect insets = win != null ? win.mContentInsets : null;
726                final Configuration displayConfig = displayContent.getConfiguration();
727                // For the new aspect-scaled transition, we want it to always show
728                // above the animating opening/closing window, and we want to
729                // synchronize its thumbnail surface with the surface for the
730                // open/close animation (only on the way down)
731                anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
732                        insets, thumbnailHeader, taskId, displayConfig.uiMode,
733                        displayConfig.orientation);
734                openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
735                openingAppAnimator.deferThumbnailDestruction =
736                        !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
737            } else {
738                anim = mService.mAppTransition.createThumbnailScaleAnimationLocked(
739                        displayInfo.appWidth, displayInfo.appHeight, transit, thumbnailHeader);
740            }
741            anim.restrictDuration(MAX_ANIMATION_DURATION);
742            anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
743
744            openingAppAnimator.thumbnail = surfaceControl;
745            openingAppAnimator.thumbnailLayer = openingLayer;
746            openingAppAnimator.thumbnailAnimation = anim;
747            mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
748        } catch (Surface.OutOfResourcesException e) {
749            Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
750                    + dirty.width() + " h=" + dirty.height(), e);
751            openingAppAnimator.clearThumbnail();
752        }
753    }
754
755    void requestTraversal() {
756        if (!mTraversalScheduled) {
757            mTraversalScheduled = true;
758            mService.mAnimationHandler.post(mPerformSurfacePlacement);
759        }
760    }
761
762    /**
763     * Puts the {@param surface} into a pending list to be destroyed after the current transaction
764     * has been committed.
765     */
766    void destroyAfterTransaction(SurfaceControl surface) {
767        mPendingDestroyingSurfaces.add(surface);
768    }
769
770    /**
771     * Destroys any surfaces that have been put into the pending list with
772     * {@link #destroyAfterTransaction}.
773     */
774    void destroyPendingSurfaces() {
775        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
776            mPendingDestroyingSurfaces.get(i).destroy();
777        }
778        mPendingDestroyingSurfaces.clear();
779    }
780
781    public void dump(PrintWriter pw, String prefix) {
782        pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled);
783        pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow);
784        pw.println(prefix + "mObscuringWindow=" + mService.mRoot.mObscuringWindow);
785    }
786}
787