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