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