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