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