1 /*
2  * Copyright (C) 2016 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
20 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
21 
22 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
23 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
24 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
25 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
26 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
27 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
31 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
32 
33 import android.app.ActivityManager.TaskSnapshot;
34 import android.content.res.CompatibilityInfo;
35 import android.content.res.Configuration;
36 import android.graphics.Bitmap;
37 import android.graphics.Rect;
38 import android.os.Debug;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.Trace;
44 import android.util.Slog;
45 import android.view.DisplayInfo;
46 import android.view.IApplicationToken;
47 import android.view.WindowManagerPolicy.StartingSurface;
48 
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.server.AttributeCache;
51 /**
52  * Controller for the app window token container. This is created by activity manager to link
53  * activity records to the app window token container they use in window manager.
54  *
55  * Test class: {@link AppWindowContainerControllerTests}
56  */
57 public class AppWindowContainerController
58         extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
59 
60     private static final int STARTING_WINDOW_TYPE_NONE = 0;
61     private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
62     private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
63 
64     private final IApplicationToken mToken;
65     private final Handler mHandler;
66 
67     private final class H extends Handler {
68         public static final int NOTIFY_WINDOWS_DRAWN = 1;
69         public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
70 
H(Looper looper)71         public H(Looper looper) {
72             super(looper);
73         }
74 
75         @Override
handleMessage(Message msg)76         public void handleMessage(Message msg) {
77             switch (msg.what) {
78                 case NOTIFY_WINDOWS_DRAWN:
79                     if (mListener == null) {
80                         return;
81                     }
82                     if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
83                             + AppWindowContainerController.this.mToken);
84                     mListener.onWindowsDrawn(msg.getWhen());
85                     break;
86                 case NOTIFY_STARTING_WINDOW_DRAWN:
87                     if (mListener == null) {
88                         return;
89                     }
90                     if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
91                             + AppWindowContainerController.this.mToken);
92                     mListener.onStartingWindowDrawn(msg.getWhen());
93                     break;
94                 default:
95                     break;
96             }
97         }
98     }
99 
100     private final Runnable mOnWindowsVisible = () -> {
101         if (mListener == null) {
102             return;
103         }
104         if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
105                 + AppWindowContainerController.this.mToken);
106         mListener.onWindowsVisible();
107     };
108 
109     private final Runnable mOnWindowsGone = () -> {
110         if (mListener == null) {
111             return;
112         }
113         if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
114                 + AppWindowContainerController.this.mToken);
115         mListener.onWindowsGone();
116     };
117 
118     private final Runnable mRemoveStartingWindow = () -> {
119         StartingSurface surface = null;
120         synchronized (mWindowMap) {
121             if (mContainer == null) {
122                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
123                         + " remove starting window");
124                 return;
125             }
126             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer
127                     + ": startingWindow=" + mContainer.startingWindow
128                     + " startingView=" + mContainer.startingSurface);
129             if (mContainer.startingData != null) {
130                 surface = mContainer.startingSurface;
131                 mContainer.startingData = null;
132                 mContainer.startingSurface = null;
133                 mContainer.startingWindow = null;
134                 mContainer.startingDisplayed = false;
135                 if (surface == null && DEBUG_STARTING_WINDOW) {
136                     Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
137                             + "remove");
138                 }
139             } else if (DEBUG_STARTING_WINDOW) {
140                 Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
141                         + mContainer);
142             }
143         }
144         if (surface != null) {
145             try {
146                 surface.remove();
147             } catch (Exception e) {
148                 Slog.w(TAG_WM, "Exception when removing starting window", e);
149             }
150         }
151     };
152 
153     private final Runnable mAddStartingWindow = () -> {
154         final StartingData startingData;
155         final AppWindowToken container;
156 
157         synchronized (mWindowMap) {
158             if (mContainer == null) {
159                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
160                         + " add starting window");
161                 return;
162             }
163             startingData = mContainer.startingData;
164             container = mContainer;
165         }
166 
167         if (startingData == null) {
168             // Animation has been canceled... do nothing.
169             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "startingData was nulled out before handling"
170                     + " mAddStartingWindow: " + mContainer);
171             return;
172         }
173 
174         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
175                 + this + ": startingData=" + container.startingData);
176 
177         StartingSurface surface = null;
178         try {
179             surface = startingData.createStartingSurface(container);
180         } catch (Exception e) {
181             Slog.w(TAG_WM, "Exception when adding starting window", e);
182         }
183         if (surface != null) {
184             boolean abort = false;
185             synchronized(mWindowMap) {
186                 // If the window was successfully added, then
187                 // we need to remove it.
188                 if (container.removed || container.startingData == null) {
189                     if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
190                             "Aborted starting " + container
191                                     + ": removed=" + container.removed
192                                     + " startingData=" + container.startingData);
193                     container.startingWindow = null;
194                     container.startingData = null;
195                     abort = true;
196                 } else {
197                     container.startingSurface = surface;
198                 }
199                 if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
200                         "Added starting " + mContainer
201                                 + ": startingWindow="
202                                 + container.startingWindow + " startingView="
203                                 + container.startingSurface);
204             }
205             if (abort) {
206                 surface.remove();
207             }
208         } else if (DEBUG_STARTING_WINDOW) {
209             Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
210         }
211     };
212 
AppWindowContainerController(TaskWindowContainerController taskController, IApplicationToken token, AppWindowContainerListener listener, int index, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, Configuration overrideConfig, Rect bounds)213     public AppWindowContainerController(TaskWindowContainerController taskController,
214             IApplicationToken token, AppWindowContainerListener listener, int index,
215             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
216             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
217             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
218             Configuration overrideConfig, Rect bounds) {
219         this(taskController, token, listener, index, requestedOrientation, fullscreen,
220                 showForAllUsers,
221                 configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable,
222                 targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos,
223                 WindowManagerService.getInstance(), overrideConfig, bounds);
224     }
225 
AppWindowContainerController(TaskWindowContainerController taskController, IApplicationToken token, AppWindowContainerListener listener, int index, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, WindowManagerService service, Configuration overrideConfig, Rect bounds)226     public AppWindowContainerController(TaskWindowContainerController taskController,
227             IApplicationToken token, AppWindowContainerListener listener, int index,
228             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges,
229             boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
230             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
231             WindowManagerService service, Configuration overrideConfig, Rect bounds) {
232         super(listener, service);
233         mHandler = new H(service.mH.getLooper());
234         mToken = token;
235         synchronized(mWindowMap) {
236             AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
237             if (atoken != null) {
238                 // TODO: Should this throw an exception instead?
239                 Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
240                 return;
241             }
242 
243             final Task task = taskController.mContainer;
244             if (task == null) {
245                 throw new IllegalArgumentException("AppWindowContainerController: invalid "
246                         + " controller=" + taskController);
247             }
248 
249             atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(),
250                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion,
251                     requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind,
252                     alwaysFocusable, this, overrideConfig, bounds);
253             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
254                     + " controller=" + taskController + " at " + index);
255             task.addChild(atoken, index);
256         }
257     }
258 
259     @VisibleForTesting
createAppWindow(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, AppWindowContainerController controller, Configuration overrideConfig, Rect bounds)260     AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
261             boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
262             boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
263             int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
264             boolean alwaysFocusable, AppWindowContainerController controller,
265             Configuration overrideConfig, Rect bounds) {
266         return new AppWindowToken(service, token, voiceInteraction, dc,
267                 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
268                 rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
269                 controller, overrideConfig, bounds);
270     }
271 
removeContainer(int displayId)272     public void removeContainer(int displayId) {
273         synchronized(mWindowMap) {
274             final DisplayContent dc = mRoot.getDisplayContent(displayId);
275             if (dc == null) {
276                 Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
277                         + mToken + " from non-existing displayId=" + displayId);
278                 return;
279             }
280             dc.removeAppToken(mToken.asBinder());
281             super.removeContainer();
282         }
283     }
284 
285     @Override
removeContainer()286     public void removeContainer() {
287         throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
288     }
289 
reparent(TaskWindowContainerController taskController, int position)290     public void reparent(TaskWindowContainerController taskController, int position) {
291         synchronized (mWindowMap) {
292             if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
293                     + " to task=" + taskController + " at " + position);
294             if (mContainer == null) {
295                 if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
296                         "reparent: could not find app token=" + mToken);
297                 return;
298             }
299             final Task task = taskController.mContainer;
300             if (task == null) {
301                 throw new IllegalArgumentException("reparent: could not find task="
302                         + taskController);
303             }
304             mContainer.reparent(task, position);
305             mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
306         }
307     }
308 
setOrientation(int requestedOrientation, int displayId, Configuration displayConfig, boolean freezeScreenIfNeeded)309     public Configuration setOrientation(int requestedOrientation, int displayId,
310             Configuration displayConfig, boolean freezeScreenIfNeeded) {
311         synchronized(mWindowMap) {
312             if (mContainer == null) {
313                 Slog.w(TAG_WM,
314                         "Attempted to set orientation of non-existing app token: " + mToken);
315                 return null;
316             }
317 
318             mContainer.setOrientation(requestedOrientation);
319 
320             final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
321             return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
322 
323         }
324     }
325 
getOrientation()326     public int getOrientation() {
327         synchronized(mWindowMap) {
328             if (mContainer == null) {
329                 return SCREEN_ORIENTATION_UNSPECIFIED;
330             }
331 
332             return mContainer.getOrientationIgnoreVisibility();
333         }
334     }
335 
336     // TODO(b/36505427): Maybe move to WindowContainerController so other sub-classes can use it as
337     // a generic way to set override config. Need to untangle current ways the override config is
338     // currently set for tasks and displays before we are doing that though.
onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds)339     public void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) {
340         synchronized(mWindowMap) {
341             if (mContainer != null) {
342                 mContainer.onOverrideConfigurationChanged(overrideConfiguration, bounds);
343             }
344         }
345     }
346 
setDisablePreviewScreenshots(boolean disable)347     public void setDisablePreviewScreenshots(boolean disable) {
348         synchronized (mWindowMap) {
349             if (mContainer == null) {
350                 Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
351                         + " token: " + mToken);
352                 return;
353             }
354             mContainer.setDisablePreviewScreenshots(disable);
355         }
356     }
357 
setVisibility(boolean visible, boolean deferHidingClient)358     public void setVisibility(boolean visible, boolean deferHidingClient) {
359         synchronized(mWindowMap) {
360             if (mContainer == null) {
361                 Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
362                         + mToken);
363                 return;
364             }
365 
366             final AppWindowToken wtoken = mContainer;
367 
368             // Don't set visibility to false if we were already not visible. This prevents WM from
369             // adding the app to the closing app list which doesn't make sense for something that is
370             // already not visible. However, set visibility to true even if we are already visible.
371             // This makes sure the app is added to the opening apps list so that the right
372             // transition can be selected.
373             // TODO: Probably a good idea to separate the concept of opening/closing apps from the
374             // concept of setting visibility...
375             if (!visible && wtoken.hiddenRequested) {
376 
377                 if (!deferHidingClient && wtoken.mDeferHidingClient) {
378                     // We previously deferred telling the client to hide itself when visibility was
379                     // initially set to false. Now we would like it to hide, so go ahead and set it.
380                     wtoken.mDeferHidingClient = deferHidingClient;
381                     wtoken.setClientHidden(true);
382                 }
383                 return;
384             }
385 
386             if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
387                     + mToken + ", visible=" + visible + "): " + mService.mAppTransition
388                     + " hidden=" + wtoken.hidden + " hiddenRequested="
389                     + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
390 
391             mService.mOpeningApps.remove(wtoken);
392             mService.mClosingApps.remove(wtoken);
393             wtoken.waitingToShow = false;
394             wtoken.hiddenRequested = !visible;
395             wtoken.mDeferHidingClient = deferHidingClient;
396 
397             if (!visible) {
398                 // If the app is dead while it was visible, we kept its dead window on screen.
399                 // Now that the app is going invisible, we can remove it. It will be restarted
400                 // if made visible again.
401                 wtoken.removeDeadWindows();
402                 wtoken.setVisibleBeforeClientHidden();
403                 mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
404             } else {
405                 if (!mService.mAppTransition.isTransitionSet()
406                         && mService.mAppTransition.isReady()) {
407                     // Add the app mOpeningApps if transition is unset but ready. This means
408                     // we're doing a screen freeze, and the unfreeze will wait for all opening
409                     // apps to be ready.
410                     mService.mOpeningApps.add(wtoken);
411                 }
412                 wtoken.startingMoved = false;
413                 // If the token is currently hidden (should be the common case), or has been
414                 // stopped, then we need to set up to wait for its windows to be ready.
415                 if (wtoken.hidden || wtoken.mAppStopped) {
416                     wtoken.clearAllDrawn();
417 
418                     // If the app was already visible, don't reset the waitingToShow state.
419                     if (wtoken.hidden) {
420                         wtoken.waitingToShow = true;
421                     }
422 
423                     if (wtoken.isClientHidden()) {
424                         // In the case where we are making an app visible but holding off for a
425                         // transition, we still need to tell the client to make its windows visible
426                         // so they get drawn. Otherwise, we will wait on performing the transition
427                         // until all windows have been drawn, they never will be, and we are sad.
428                         wtoken.setClientHidden(false);
429                     }
430                 }
431                 wtoken.requestUpdateWallpaperIfNeeded();
432 
433                 if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
434                 wtoken.mAppStopped = false;
435             }
436 
437             // If we are preparing an app transition, then delay changing
438             // the visibility of this token until we execute that transition.
439             if (mService.okToDisplay() && mService.mAppTransition.isTransitionSet()) {
440                 // A dummy animation is a placeholder animation which informs others that an
441                 // animation is going on (in this case an application transition). If the animation
442                 // was transferred from another application/animator, no dummy animator should be
443                 // created since an animation is already in progress.
444                 if (wtoken.mAppAnimator.usingTransferredAnimation
445                         && wtoken.mAppAnimator.animation == null) {
446                     Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
447                             + ", using null transferred animation!");
448                 }
449                 if (!wtoken.mAppAnimator.usingTransferredAnimation &&
450                         (!wtoken.startingDisplayed || mService.mSkipAppTransitionAnimation)) {
451                     if (DEBUG_APP_TRANSITIONS) Slog.v(
452                             TAG_WM, "Setting dummy animation on: " + wtoken);
453                     wtoken.mAppAnimator.setDummyAnimation();
454                 }
455                 wtoken.inPendingTransaction = true;
456                 if (visible) {
457                     mService.mOpeningApps.add(wtoken);
458                     wtoken.mEnteringAnimation = true;
459                 } else {
460                     mService.mClosingApps.add(wtoken);
461                     wtoken.mEnteringAnimation = false;
462                 }
463                 if (mService.mAppTransition.getAppTransition()
464                         == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
465                     // We're launchingBehind, add the launching activity to mOpeningApps.
466                     final WindowState win =
467                             mService.getDefaultDisplayContentLocked().findFocusedWindow();
468                     if (win != null) {
469                         final AppWindowToken focusedToken = win.mAppToken;
470                         if (focusedToken != null) {
471                             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
472                                     + " adding " + focusedToken + " to mOpeningApps");
473                             // Force animation to be loaded.
474                             focusedToken.hidden = true;
475                             mService.mOpeningApps.add(focusedToken);
476                         }
477                     }
478                 }
479                 return;
480             }
481 
482             wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
483             wtoken.updateReportedVisibilityLocked();
484         }
485     }
486 
487     /**
488      * Notifies that we launched an app that might be visible or not visible depending on what kind
489      * of Keyguard flags it's going to set on its windows.
490      */
notifyUnknownVisibilityLaunched()491     public void notifyUnknownVisibilityLaunched() {
492         synchronized(mWindowMap) {
493             if (mContainer != null) {
494                 mService.mUnknownAppVisibilityController.notifyLaunched(mContainer);
495             }
496         }
497     }
498 
addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents)499     public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
500             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
501             IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
502             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
503         synchronized(mWindowMap) {
504             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
505                     + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
506                     + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
507                     + " allowTaskSnapshot=" + allowTaskSnapshot);
508 
509             if (mContainer == null) {
510                 Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
511                 return false;
512             }
513 
514             // If the display is frozen, we won't do anything until the actual window is
515             // displayed so there is no reason to put in the starting window.
516             if (!mService.okToDisplay()) {
517                 return false;
518             }
519 
520             if (mContainer.startingData != null) {
521                 return false;
522             }
523 
524             final WindowState mainWin = mContainer.findMainWindow();
525             if (mainWin != null && mainWin.isVisible() && mainWin.isDrawnLw()) {
526                 // App already has a visible window that is drawn...why would you want a starting
527                 // window?
528                 return false;
529             }
530 
531             final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
532                     mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
533                     false /* restoreFromDisk */, false /* reducedResolution */);
534             final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
535                     allowTaskSnapshot, activityCreated, fromRecents, snapshot);
536 
537             if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
538                 return createSnapshot(snapshot);
539             }
540 
541             // If this is a translucent window, then don't show a starting window -- the current
542             // effect (a full-screen opaque starting window that fades away to the real contents
543             // when it is ready) does not work for this.
544             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
545                     + Integer.toHexString(theme));
546             if (theme != 0) {
547                 AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
548                         com.android.internal.R.styleable.Window, mService.mCurrentUserId);
549                 if (ent == null) {
550                     // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
551                     // see that.
552                     return false;
553                 }
554                 final boolean windowIsTranslucent = ent.array.getBoolean(
555                         com.android.internal.R.styleable.Window_windowIsTranslucent, false);
556                 final boolean windowIsFloating = ent.array.getBoolean(
557                         com.android.internal.R.styleable.Window_windowIsFloating, false);
558                 final boolean windowShowWallpaper = ent.array.getBoolean(
559                         com.android.internal.R.styleable.Window_windowShowWallpaper, false);
560                 final boolean windowDisableStarting = ent.array.getBoolean(
561                         com.android.internal.R.styleable.Window_windowDisablePreview, false);
562                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
563                         + " Floating=" + windowIsFloating
564                         + " ShowWallpaper=" + windowShowWallpaper);
565                 if (windowIsTranslucent) {
566                     return false;
567                 }
568                 if (windowIsFloating || windowDisableStarting) {
569                     return false;
570                 }
571                 if (windowShowWallpaper) {
572                     if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
573                             == null) {
574                         // If this theme is requesting a wallpaper, and the wallpaper
575                         // is not currently visible, then this effectively serves as
576                         // an opaque window and our starting window transition animation
577                         // can still work.  We just need to make sure the starting window
578                         // is also showing the wallpaper.
579                         windowFlags |= FLAG_SHOW_WALLPAPER;
580                     } else {
581                         return false;
582                     }
583                 }
584             }
585 
586             if (mContainer.transferStartingWindow(transferFrom)) {
587                 return true;
588             }
589 
590             // There is no existing starting window, and we don't want to create a splash screen, so
591             // that's it!
592             if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
593                 return false;
594             }
595 
596             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
597             mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
598                     compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
599                     mContainer.getMergedOverrideConfiguration());
600             scheduleAddStartingWindow();
601         }
602         return true;
603     }
604 
getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, TaskSnapshot snapshot)605     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
606             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
607             TaskSnapshot snapshot) {
608         if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) {
609             // TODO(b/34099271): Remove this statement to add back the starting window and figure
610             // out why it causes flickering, the starting window appears over the thumbnail while
611             // the docked from recents transition occurs
612             return STARTING_WINDOW_TYPE_NONE;
613         } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
614             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
615         } else if (taskSwitch && allowTaskSnapshot) {
616             return snapshot == null ? STARTING_WINDOW_TYPE_NONE
617                     : snapshotOrientationSameAsTask(snapshot) || fromRecents
618                             ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
619         } else {
620             return STARTING_WINDOW_TYPE_NONE;
621         }
622     }
623 
scheduleAddStartingWindow()624     void scheduleAddStartingWindow() {
625         // Note: we really want to do sendMessageAtFrontOfQueue() because we
626         // want to process the message ASAP, before any other queued
627         // messages.
628         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
629         mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
630     }
631 
createSnapshot(TaskSnapshot snapshot)632     private boolean createSnapshot(TaskSnapshot snapshot) {
633         if (snapshot == null) {
634             return false;
635         }
636 
637         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
638         mContainer.startingData = new SnapshotStartingData(mService, snapshot);
639         scheduleAddStartingWindow();
640         return true;
641     }
642 
snapshotOrientationSameAsTask(TaskSnapshot snapshot)643     private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
644         if (snapshot == null) {
645             return false;
646         }
647         return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
648     }
649 
removeStartingWindow()650     public void removeStartingWindow() {
651         synchronized (mWindowMap) {
652             if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
653                 // Already scheduled.
654                 if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Trying to remove starting window but "
655                         + "already scheduled");
656                 return;
657             }
658 
659             if (mContainer.startingWindow == null) {
660                 if (mContainer.startingData != null) {
661                     // Starting window has not been added yet, but it is scheduled to be added.
662                     // Go ahead and cancel the request.
663                     if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
664                             "Clearing startingData for token=" + mContainer);
665                     mContainer.startingData = null;
666                 }
667                 return;
668             }
669 
670             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
671                     + " startingWindow=" + mContainer.startingWindow);
672             mHandler.post(mRemoveStartingWindow);
673         }
674     }
675 
pauseKeyDispatching()676     public void pauseKeyDispatching() {
677         synchronized (mWindowMap) {
678             if (mContainer != null) {
679                 mService.mInputMonitor.pauseDispatchingLw(mContainer);
680             }
681         }
682     }
683 
resumeKeyDispatching()684     public void resumeKeyDispatching() {
685         synchronized (mWindowMap) {
686             if (mContainer != null) {
687                 mService.mInputMonitor.resumeDispatchingLw(mContainer);
688             }
689         }
690     }
691 
notifyAppResumed(boolean wasStopped)692     public void notifyAppResumed(boolean wasStopped) {
693         synchronized(mWindowMap) {
694             if (mContainer == null) {
695                 Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
696                 return;
697             }
698             mContainer.notifyAppResumed(wasStopped);
699         }
700     }
701 
notifyAppStopped()702     public void notifyAppStopped() {
703         synchronized(mWindowMap) {
704             if (mContainer == null) {
705                 Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
706                         + mToken);
707                 return;
708             }
709             mContainer.notifyAppStopped();
710         }
711     }
712 
startFreezingScreen(int configChanges)713     public void startFreezingScreen(int configChanges) {
714         synchronized(mWindowMap) {
715             if (configChanges == 0 && mService.okToDisplay()) {
716                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
717                 return;
718             }
719 
720             if (mContainer == null) {
721                 Slog.w(TAG_WM,
722                         "Attempted to freeze screen with non-existing app token: " + mContainer);
723                 return;
724             }
725             mContainer.startFreezingScreen();
726         }
727     }
728 
stopFreezingScreen(boolean force)729     public void stopFreezingScreen(boolean force) {
730         synchronized(mWindowMap) {
731             if (mContainer == null) {
732                 return;
733             }
734             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
735                     + mContainer.hidden + " freezing=" + mContainer.mAppAnimator.freezingScreen);
736             mContainer.stopFreezingScreen(true, force);
737         }
738     }
739 
740     /**
741      * Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
742      * In portrait mode, it grabs the full screenshot.
743      *
744      * @param displayId the Display to take a screenshot of.
745      * @param width the width of the target bitmap
746      * @param height the height of the target bitmap
747      * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
748      */
screenshotApplications(int displayId, int width, int height, float frameScale)749     public Bitmap screenshotApplications(int displayId, int width, int height, float frameScale) {
750         try {
751             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
752             final DisplayContent dc;
753             synchronized(mWindowMap) {
754                 dc = mRoot.getDisplayContentOrCreate(displayId);
755                 if (dc == null) {
756                     if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + mToken
757                             + ": returning null. No Display for displayId=" + displayId);
758                     return null;
759                 }
760             }
761             return dc.screenshotApplications(mToken.asBinder(), width, height,
762                     false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
763                     false /* wallpaperOnly */, false /* includeDecor */);
764         } finally {
765             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
766         }
767     }
768 
reportStartingWindowDrawn()769     void reportStartingWindowDrawn() {
770         mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
771     }
772 
reportWindowsDrawn()773     void reportWindowsDrawn() {
774         mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
775     }
776 
reportWindowsVisible()777     void reportWindowsVisible() {
778         mHandler.post(mOnWindowsVisible);
779     }
780 
reportWindowsGone()781     void reportWindowsGone() {
782         mHandler.post(mOnWindowsGone);
783     }
784 
785     /** Calls directly into activity manager so window manager lock shouldn't held. */
keyDispatchingTimedOut(String reason, int windowPid)786     boolean keyDispatchingTimedOut(String reason, int windowPid) {
787         return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
788     }
789 
790     @Override
toString()791     public String toString() {
792         return "AppWindowContainerController{"
793                 + " token=" + mToken
794                 + " mContainer=" + mContainer
795                 + " mListener=" + mListener
796                 + "}";
797     }
798 }
799