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