1 /* 2 * Copyright (C) 2011 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.os.Process.INVALID_UID; 20 import static android.view.Display.INVALID_DISPLAY; 21 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 23 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 24 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 25 26 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; 27 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS; 28 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; 29 import static com.android.server.wm.ProtoLogGroup.WM_ERROR; 30 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; 31 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; 32 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; 33 import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN; 34 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 35 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 36 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; 37 import static com.android.server.wm.WindowTokenProto.HASH_CODE; 38 import static com.android.server.wm.WindowTokenProto.PAUSED; 39 import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW; 40 import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; 41 42 import android.annotation.CallSuper; 43 import android.app.IWindowToken; 44 import android.app.servertransaction.FixedRotationAdjustmentsItem; 45 import android.content.res.Configuration; 46 import android.graphics.Rect; 47 import android.os.Debug; 48 import android.os.IBinder; 49 import android.os.RemoteException; 50 import android.util.Slog; 51 import android.util.SparseArray; 52 import android.util.proto.ProtoOutputStream; 53 import android.view.DisplayAdjustments.FixedRotationAdjustments; 54 import android.view.DisplayInfo; 55 import android.view.InsetsState; 56 import android.view.SurfaceControl; 57 import android.view.WindowManager; 58 59 import com.android.internal.annotations.VisibleForTesting; 60 import com.android.server.policy.WindowManagerPolicy; 61 import com.android.server.protolog.common.ProtoLog; 62 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.Comparator; 66 67 /** 68 * Container of a set of related windows in the window manager. Often this is an AppWindowToken, 69 * which is the handle for an Activity that it uses to display windows. For nested windows, there is 70 * a WindowToken created for the parent window to manage its children. 71 */ 72 class WindowToken extends WindowContainer<WindowState> { 73 private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM; 74 75 // The actual token. 76 final IBinder token; 77 78 // The type of window this token is for, as per WindowManager.LayoutParams. 79 final int windowType; 80 81 /** {@code true} if this holds the rounded corner overlay */ 82 final boolean mRoundedCornerOverlay; 83 84 // Set if this token was explicitly added by a client, so should 85 // persist (not be removed) when all windows are removed. 86 boolean mPersistOnEmpty; 87 88 // For printing. 89 String stringName; 90 91 // Is key dispatching paused for this token? 92 boolean paused = false; 93 94 // Temporary for finding which tokens no longer have visible windows. 95 boolean hasVisible; 96 97 // Set to true when this token is in a pending transaction where it 98 // will be shown. 99 boolean waitingToShow; 100 101 /** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */ 102 final boolean mOwnerCanManageAppTokens; 103 104 private FixedRotationTransformState mFixedRotationTransformState; 105 106 private Configuration mLastReportedConfig; 107 private int mLastReportedDisplay = INVALID_DISPLAY; 108 109 /** 110 * When set to {@code true}, this window token is created from {@link android.app.WindowContext} 111 */ 112 @VisibleForTesting 113 final boolean mFromClientToken; 114 115 private DeathRecipient mDeathRecipient; 116 private boolean mBinderDied = false; 117 118 private final int mOwnerUid; 119 120 /** 121 * Used to fix the transform of the token to be rotated to a rotation different than it's 122 * display. The window frames and surfaces corresponding to this token will be layouted and 123 * rotated by the given rotated display info, frames and insets. 124 */ 125 private static class FixedRotationTransformState { 126 final DisplayInfo mDisplayInfo; 127 final DisplayFrames mDisplayFrames; 128 final InsetsState mInsetsState = new InsetsState(); 129 final Configuration mRotatedOverrideConfiguration; 130 final SeamlessRotator mRotator; 131 /** 132 * The tokens that share the same transform. Their end time of transform are the same. The 133 * list should at least contain the token who creates this state. 134 */ 135 final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3); 136 final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3); 137 final SparseArray<Rect> mBarContentFrames = new SparseArray<>(); 138 boolean mIsTransforming = true; 139 FixedRotationTransformState(DisplayInfo rotatedDisplayInfo, DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig, int currentRotation)140 FixedRotationTransformState(DisplayInfo rotatedDisplayInfo, 141 DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig, 142 int currentRotation) { 143 mDisplayInfo = rotatedDisplayInfo; 144 mDisplayFrames = rotatedDisplayFrames; 145 mRotatedOverrideConfiguration = rotatedConfig; 146 // This will use unrotate as rotate, so the new and old rotation are inverted. 147 mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation, 148 rotatedDisplayInfo, true /* applyFixedTransformationHint */); 149 } 150 151 /** 152 * Transforms the window container from the next rotation to the current rotation for 153 * showing the window in a display with different rotation. 154 */ transform(WindowContainer<?> container)155 void transform(WindowContainer<?> container) { 156 mRotator.unrotate(container.getPendingTransaction(), container); 157 if (!mRotatedContainers.contains(container)) { 158 mRotatedContainers.add(container); 159 } 160 } 161 162 /** 163 * Resets the transformation of the window containers which have been rotated. This should 164 * be called when the window has the same rotation as display. 165 */ resetTransform()166 void resetTransform() { 167 for (int i = mRotatedContainers.size() - 1; i >= 0; i--) { 168 final WindowContainer<?> c = mRotatedContainers.get(i); 169 // If the window is detached (no parent), its surface may have been released. 170 if (c.getParent() != null) { 171 mRotator.finish(c.getPendingTransaction(), c); 172 } 173 } 174 } 175 } 176 177 private class DeathRecipient implements IBinder.DeathRecipient { 178 private boolean mHasUnlinkToDeath = false; 179 180 @Override binderDied()181 public void binderDied() { 182 synchronized (mWmService.mGlobalLock) { 183 mBinderDied = true; 184 removeImmediately(); 185 } 186 } 187 linkToDeath()188 void linkToDeath() throws RemoteException { 189 token.linkToDeath(DeathRecipient.this, 0); 190 } 191 unlinkToDeath()192 void unlinkToDeath() { 193 if (mHasUnlinkToDeath) { 194 return; 195 } 196 token.unlinkToDeath(DeathRecipient.this, 0); 197 mHasUnlinkToDeath = true; 198 } 199 } 200 201 /** 202 * Compares two child window of this token and returns -1 if the first is lesser than the 203 * second in terms of z-order and 1 otherwise. 204 */ 205 private final Comparator<WindowState> mWindowComparator = 206 (WindowState newWindow, WindowState existingWindow) -> { 207 final WindowToken token = WindowToken.this; 208 if (newWindow.mToken != token) { 209 throw new IllegalArgumentException("newWindow=" + newWindow 210 + " is not a child of token=" + token); 211 } 212 213 if (existingWindow.mToken != token) { 214 throw new IllegalArgumentException("existingWindow=" + existingWindow 215 + " is not a child of token=" + token); 216 } 217 218 return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1; 219 }; 220 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens)221 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 222 DisplayContent dc, boolean ownerCanManageAppTokens) { 223 this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, 224 false /* roundedCornerOverlay */); 225 } 226 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay)227 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 228 DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { 229 this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID, 230 roundedCornerOverlay, false /* fromClientToken */); 231 } 232 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, boolean roundedCornerOverlay, boolean fromClientToken)233 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 234 DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, 235 boolean roundedCornerOverlay, boolean fromClientToken) { 236 super(service); 237 token = _token; 238 windowType = type; 239 mPersistOnEmpty = persistOnEmpty; 240 mOwnerCanManageAppTokens = ownerCanManageAppTokens; 241 mOwnerUid = ownerUid; 242 mRoundedCornerOverlay = roundedCornerOverlay; 243 mFromClientToken = fromClientToken; 244 if (dc != null) { 245 dc.addWindowToken(token, this); 246 } 247 if (shouldReportToClient()) { 248 try { 249 mDeathRecipient = new DeathRecipient(); 250 mDeathRecipient.linkToDeath(); 251 } catch (RemoteException e) { 252 Slog.e(TAG, "Unable to add window token with type " + windowType + " on " 253 + "display " + dc.getDisplayId(), e); 254 mDeathRecipient = null; 255 return; 256 } 257 } 258 } 259 removeAllWindowsIfPossible()260 void removeAllWindowsIfPossible() { 261 for (int i = mChildren.size() - 1; i >= 0; --i) { 262 final WindowState win = mChildren.get(i); 263 ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT, 264 "removeAllWindowsIfPossible: removing win=%s", win); 265 win.removeIfPossible(); 266 } 267 } 268 setExiting()269 void setExiting() { 270 if (isEmpty()) { 271 super.removeImmediately(); 272 return; 273 } 274 275 // This token is exiting, so allow it to be removed when it no longer contains any windows. 276 mPersistOnEmpty = false; 277 278 if (!isVisible()) { 279 return; 280 } 281 282 final int count = mChildren.size(); 283 boolean changed = false; 284 final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN); 285 286 for (int i = 0; i < count; i++) { 287 final WindowState win = mChildren.get(i); 288 changed |= win.onSetAppExiting(); 289 } 290 291 final ActivityRecord app = asActivityRecord(); 292 if (app != null) { 293 app.setVisible(false); 294 } 295 296 if (changed) { 297 mWmService.mWindowPlacerLocked.performSurfacePlacement(); 298 mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/); 299 } 300 301 if (delayed) { 302 mDisplayContent.mExitingTokens.add(this); 303 } 304 } 305 306 /** 307 * @return The scale for applications running in compatibility mode. Multiply the size in the 308 * application by this scale will be the size in the screen. 309 */ getSizeCompatScale()310 float getSizeCompatScale() { 311 return mDisplayContent.mCompatibleScreenScale; 312 } 313 314 /** 315 * Returns true if the new window is considered greater than the existing window in terms of 316 * z-order. 317 */ isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow)318 protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, 319 WindowState existingWindow) { 320 // New window is considered greater if it has a higher or equal base layer. 321 return newWindow.mBaseLayer >= existingWindow.mBaseLayer; 322 } 323 addWindow(final WindowState win)324 void addWindow(final WindowState win) { 325 ProtoLog.d(WM_DEBUG_FOCUS, 326 "addWindow: win=%s Callers=%s", win, Debug.getCallers(5)); 327 328 if (win.isChildWindow()) { 329 // Child windows are added to their parent windows. 330 return; 331 } 332 // This token is created from WindowContext and the client requests to addView now, create a 333 // surface for this token. 334 if (mSurfaceControl == null) { 335 createSurfaceControl(true /* force */); 336 } 337 if (!mChildren.contains(win)) { 338 ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this); 339 addChild(win, mWindowComparator); 340 mWmService.mWindowsChanged = true; 341 // TODO: Should we also be setting layout needed here and other places? 342 } 343 } 344 345 @Override createSurfaceControl(boolean force)346 void createSurfaceControl(boolean force) { 347 if (!mFromClientToken || force) { 348 super.createSurfaceControl(force); 349 } 350 } 351 352 /** Returns true if the token windows list is empty. */ isEmpty()353 boolean isEmpty() { 354 return mChildren.isEmpty(); 355 } 356 getReplacingWindow()357 WindowState getReplacingWindow() { 358 for (int i = mChildren.size() - 1; i >= 0; i--) { 359 final WindowState win = mChildren.get(i); 360 final WindowState replacing = win.getReplacingWindow(); 361 if (replacing != null) { 362 return replacing; 363 } 364 } 365 return null; 366 } 367 368 /** Return true if this token has a window that wants the wallpaper displayed behind it. */ windowsCanBeWallpaperTarget()369 boolean windowsCanBeWallpaperTarget() { 370 for (int j = mChildren.size() - 1; j >= 0; j--) { 371 final WindowState w = mChildren.get(j); 372 if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { 373 return true; 374 } 375 } 376 377 return false; 378 } 379 380 @Override removeImmediately()381 void removeImmediately() { 382 if (mDisplayContent != null) { 383 mDisplayContent.removeWindowToken(token); 384 } 385 // Needs to occur after the token is removed from the display above to avoid attempt at 386 // duplicate removal of this window container from it's parent. 387 super.removeImmediately(); 388 389 reportWindowTokenRemovedToClient(); 390 } 391 reportWindowTokenRemovedToClient()392 private void reportWindowTokenRemovedToClient() { 393 if (!shouldReportToClient()) { 394 return; 395 } 396 mDeathRecipient.unlinkToDeath(); 397 IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); 398 try { 399 windowTokenClient.onWindowTokenRemoved(); 400 } catch (RemoteException e) { 401 ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); 402 } 403 } 404 405 @Override onDisplayChanged(DisplayContent dc)406 void onDisplayChanged(DisplayContent dc) { 407 dc.reParentWindowToken(this); 408 409 // TODO(b/36740756): One day this should perhaps be hooked 410 // up with goodToGo, so we don't move a window 411 // to another display before the window behind 412 // it is ready. 413 super.onDisplayChanged(dc); 414 reportConfigToWindowTokenClient(); 415 } 416 417 @Override onConfigurationChanged(Configuration newParentConfig)418 public void onConfigurationChanged(Configuration newParentConfig) { 419 super.onConfigurationChanged(newParentConfig); 420 reportConfigToWindowTokenClient(); 421 } 422 reportConfigToWindowTokenClient()423 void reportConfigToWindowTokenClient() { 424 if (!shouldReportToClient()) { 425 return; 426 } 427 if (mLastReportedConfig == null) { 428 mLastReportedConfig = new Configuration(); 429 } 430 final Configuration config = getConfiguration(); 431 final int displayId = getDisplayContent().getDisplayId(); 432 if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) { 433 // No changes since last reported time. 434 return; 435 } 436 437 mLastReportedConfig.setTo(config); 438 mLastReportedDisplay = displayId; 439 440 IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); 441 try { 442 windowTokenClient.onConfigurationChanged(config, displayId); 443 } catch (RemoteException e) { 444 ProtoLog.w(WM_ERROR, 445 "Could not report config changes to the window token client."); 446 } 447 } 448 449 /** 450 * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and 451 * registered from client side. 452 */ shouldReportToClient()453 private boolean shouldReportToClient() { 454 // Only report to client for WindowToken because Activities are updated through ATM 455 // callbacks. 456 return asActivityRecord() == null 457 // Report to {@link android.view.WindowTokenClient} if this token was registered from it. 458 && mFromClientToken && !mBinderDied; 459 } 460 461 @Override assignLayer(SurfaceControl.Transaction t, int layer)462 void assignLayer(SurfaceControl.Transaction t, int layer) { 463 if (windowType == TYPE_DOCK_DIVIDER) { 464 // See {@link DisplayContent#mSplitScreenDividerAnchor} 465 super.assignRelativeLayer(t, 466 mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1); 467 } else if (mRoundedCornerOverlay) { 468 super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); 469 } else { 470 super.assignLayer(t, layer); 471 } 472 } 473 474 @Override makeSurface()475 SurfaceControl.Builder makeSurface() { 476 final SurfaceControl.Builder builder = super.makeSurface(); 477 if (mRoundedCornerOverlay) { 478 builder.setParent(null); 479 } 480 return builder; 481 } 482 hasFixedRotationTransform()483 boolean hasFixedRotationTransform() { 484 return mFixedRotationTransformState != null; 485 } 486 487 /** Returns {@code true} if the given token shares the same transform. */ hasFixedRotationTransform(WindowToken token)488 boolean hasFixedRotationTransform(WindowToken token) { 489 if (mFixedRotationTransformState == null || token == null) { 490 return false; 491 } 492 return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState; 493 } 494 isFinishingFixedRotationTransform()495 boolean isFinishingFixedRotationTransform() { 496 return mFixedRotationTransformState != null 497 && !mFixedRotationTransformState.mIsTransforming; 498 } 499 isFixedRotationTransforming()500 boolean isFixedRotationTransforming() { 501 return mFixedRotationTransformState != null 502 && mFixedRotationTransformState.mIsTransforming; 503 } 504 getFixedRotationTransformDisplayInfo()505 DisplayInfo getFixedRotationTransformDisplayInfo() { 506 return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayInfo : null; 507 } 508 getFixedRotationTransformDisplayFrames()509 DisplayFrames getFixedRotationTransformDisplayFrames() { 510 return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayFrames : null; 511 } 512 getFixedRotationTransformDisplayBounds()513 Rect getFixedRotationTransformDisplayBounds() { 514 return isFixedRotationTransforming() 515 ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration 516 .getBounds() 517 : null; 518 } 519 getFixedRotationBarContentFrame(int windowType)520 Rect getFixedRotationBarContentFrame(int windowType) { 521 return isFixedRotationTransforming() 522 ? mFixedRotationTransformState.mBarContentFrames.get(windowType) 523 : null; 524 } 525 getFixedRotationTransformInsetsState()526 InsetsState getFixedRotationTransformInsetsState() { 527 return isFixedRotationTransforming() ? mFixedRotationTransformState.mInsetsState : null; 528 } 529 530 /** Applies the rotated layout environment to this token in the simulated rotated display. */ applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config)531 void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, 532 Configuration config) { 533 if (mFixedRotationTransformState != null) { 534 return; 535 } 536 mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, 537 new Configuration(config), mDisplayContent.getRotation()); 538 mFixedRotationTransformState.mAssociatedTokens.add(this); 539 mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, 540 mFixedRotationTransformState.mInsetsState, 541 mFixedRotationTransformState.mBarContentFrames); 542 onConfigurationChanged(getParent().getConfiguration()); 543 notifyFixedRotationTransform(true /* enabled */); 544 } 545 546 /** 547 * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this 548 * one. This takes the same effect as {@link #applyFixedRotationTransform}. 549 */ linkFixedRotationTransform(WindowToken other)550 void linkFixedRotationTransform(WindowToken other) { 551 if (mFixedRotationTransformState != null) { 552 return; 553 } 554 final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; 555 if (fixedRotationState == null) { 556 return; 557 } 558 mFixedRotationTransformState = fixedRotationState; 559 fixedRotationState.mAssociatedTokens.add(this); 560 onConfigurationChanged(getParent().getConfiguration()); 561 notifyFixedRotationTransform(true /* enabled */); 562 } 563 564 /** 565 * Return {@code true} if one of the associated activity is still animating. Otherwise, 566 * return {@code false}. 567 */ hasAnimatingFixedRotationTransition()568 boolean hasAnimatingFixedRotationTransition() { 569 if (mFixedRotationTransformState == null) { 570 return false; 571 } 572 573 for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) { 574 final ActivityRecord r = 575 mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord(); 576 if (r != null && r.isAnimating(TRANSITION | PARENTS)) { 577 return true; 578 } 579 } 580 return false; 581 } 582 finishFixedRotationTransform()583 void finishFixedRotationTransform() { 584 finishFixedRotationTransform(null /* applyDisplayRotation */); 585 } 586 587 /** 588 * Finishes the transform and apply display rotation if the action is given. If the display will 589 * not rotate, the transformed containers are restored to their original states. 590 */ finishFixedRotationTransform(Runnable applyDisplayRotation)591 void finishFixedRotationTransform(Runnable applyDisplayRotation) { 592 final FixedRotationTransformState state = mFixedRotationTransformState; 593 if (state == null) { 594 return; 595 } 596 597 state.resetTransform(); 598 // Clear the flag so if the display will be updated to the same orientation, the transform 599 // won't take effect. 600 state.mIsTransforming = false; 601 if (applyDisplayRotation != null) { 602 applyDisplayRotation.run(); 603 } else { 604 // The display will not rotate to the rotation of this container, let's cancel them. 605 for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { 606 state.mAssociatedTokens.get(i).cancelFixedRotationTransform(); 607 } 608 } 609 // The state is cleared at the end, because it is used to indicate that other windows can 610 // use seamless rotation when applying rotation to display. 611 for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { 612 state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); 613 } 614 } 615 cleanUpFixedRotationTransformState()616 private void cleanUpFixedRotationTransformState() { 617 mFixedRotationTransformState = null; 618 notifyFixedRotationTransform(false /* enabled */); 619 } 620 621 /** Notifies application side to enable or disable the rotation adjustment of display info. */ notifyFixedRotationTransform(boolean enabled)622 private void notifyFixedRotationTransform(boolean enabled) { 623 FixedRotationAdjustments adjustments = null; 624 // A token may contain windows of the same processes or different processes. The list is 625 // used to avoid sending the same adjustments to a process multiple times. 626 ArrayList<WindowProcessController> notifiedProcesses = null; 627 for (int i = mChildren.size() - 1; i >= 0; i--) { 628 final WindowState w = mChildren.get(i); 629 final WindowProcessController app; 630 if (w.mAttrs.type == TYPE_APPLICATION_STARTING) { 631 // Use the host activity because starting window is controlled by window manager. 632 final ActivityRecord r = asActivityRecord(); 633 if (r == null) { 634 continue; 635 } 636 app = r.app; 637 } else { 638 app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid); 639 } 640 if (app == null || !app.hasThread()) { 641 continue; 642 } 643 if (notifiedProcesses == null) { 644 notifiedProcesses = new ArrayList<>(2); 645 adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null; 646 } else if (notifiedProcesses.contains(app)) { 647 continue; 648 } 649 notifiedProcesses.add(app); 650 try { 651 mWmService.mAtmService.getLifecycleManager().scheduleTransaction( 652 app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments)); 653 } catch (RemoteException e) { 654 Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e); 655 } 656 } 657 } 658 659 /** Restores the changes that applies to this container. */ cancelFixedRotationTransform()660 private void cancelFixedRotationTransform() { 661 final WindowContainer<?> parent = getParent(); 662 if (parent == null) { 663 // The window may be detached or detaching. 664 return; 665 } 666 notifyFixedRotationTransform(false /* enabled */); 667 final int originalRotation = getWindowConfiguration().getRotation(); 668 onConfigurationChanged(parent.getConfiguration()); 669 onCancelFixedRotationTransform(originalRotation); 670 } 671 672 /** 673 * It is called when the window is using fixed rotation transform, and before display applies 674 * the same rotation, the rotation change for display is canceled, e.g. the orientation from 675 * sensor is updated to previous direction. 676 */ onCancelFixedRotationTransform(int originalDisplayRotation)677 void onCancelFixedRotationTransform(int originalDisplayRotation) { 678 } 679 createFixedRotationAdjustmentsIfNeeded()680 FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() { 681 if (!isFixedRotationTransforming()) { 682 return null; 683 } 684 return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation, 685 mFixedRotationTransformState.mDisplayInfo.displayCutout); 686 } 687 688 @Override resolveOverrideConfiguration(Configuration newParentConfig)689 void resolveOverrideConfiguration(Configuration newParentConfig) { 690 super.resolveOverrideConfiguration(newParentConfig); 691 if (isFixedRotationTransforming()) { 692 // Apply the rotated configuration to current resolved configuration, so the merged 693 // override configuration can update to the same state. 694 getResolvedOverrideConfiguration().updateFrom( 695 mFixedRotationTransformState.mRotatedOverrideConfiguration); 696 } 697 } 698 699 @Override updateSurfacePosition(SurfaceControl.Transaction t)700 void updateSurfacePosition(SurfaceControl.Transaction t) { 701 super.updateSurfacePosition(t); 702 if (isFixedRotationTransforming()) { 703 // The window is layouted in a simulated rotated display but the real display hasn't 704 // rotated, so here transforms its surface to fit in the real display. 705 mFixedRotationTransformState.transform(this); 706 } 707 } 708 709 @Override resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t)710 void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) { 711 // Keep the transformed position to animate because the surface will show in different 712 // rotation than the animator of leash. 713 if (!isFixedRotationTransforming()) { 714 super.resetSurfacePositionForAnimationLeash(t); 715 } 716 } 717 718 /** 719 * Gives a chance to this {@link WindowToken} to adjust the {@link 720 * android.view.WindowManager.LayoutParams} of its windows. 721 */ adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs)722 void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) { 723 } 724 725 726 @CallSuper 727 @Override dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)728 public void dumpDebug(ProtoOutputStream proto, long fieldId, 729 @WindowTraceLogLevel int logLevel) { 730 if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) { 731 return; 732 } 733 734 final long token = proto.start(fieldId); 735 super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); 736 proto.write(HASH_CODE, System.identityHashCode(this)); 737 proto.write(WAITING_TO_SHOW, waitingToShow); 738 proto.write(PAUSED, paused); 739 proto.end(token); 740 } 741 742 @Override getProtoFieldId()743 long getProtoFieldId() { 744 return WINDOW_TOKEN; 745 } 746 dump(PrintWriter pw, String prefix, boolean dumpAll)747 void dump(PrintWriter pw, String prefix, boolean dumpAll) { 748 super.dump(pw, prefix, dumpAll); 749 pw.print(prefix); pw.print("windows="); pw.println(mChildren); 750 pw.print(prefix); pw.print("windowType="); pw.print(windowType); 751 pw.print(" hasVisible="); pw.print(hasVisible); 752 if (waitingToShow) { 753 pw.print(" waitingToShow=true"); 754 } 755 pw.println(); 756 if (hasFixedRotationTransform()) { 757 pw.print(prefix); 758 pw.print("fixedRotationConfig="); 759 pw.println(mFixedRotationTransformState.mRotatedOverrideConfiguration); 760 } 761 } 762 763 @Override toString()764 public String toString() { 765 if (stringName == null) { 766 StringBuilder sb = new StringBuilder(); 767 sb.append("WindowToken{"); 768 sb.append(Integer.toHexString(System.identityHashCode(this))); 769 sb.append(" "); sb.append(token); sb.append('}'); 770 stringName = sb.toString(); 771 } 772 return stringName; 773 } 774 775 @Override getName()776 String getName() { 777 return toString(); 778 } 779 780 /** 781 * Return whether windows from this token can layer above the 782 * system bars, or in other words extend outside of the "Decor Frame" 783 */ canLayerAboveSystemBars()784 boolean canLayerAboveSystemBars() { 785 int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, 786 mOwnerCanManageAppTokens); 787 int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR, 788 mOwnerCanManageAppTokens); 789 return mOwnerCanManageAppTokens && (layer > navLayer); 790 } 791 getWindowLayerFromType()792 int getWindowLayerFromType() { 793 return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens); 794 } 795 getOwnerUid()796 int getOwnerUid() { 797 return mOwnerUid; 798 } 799 } 800