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