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