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