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 /** The state may not only be used by self. Make sure to leave the influence by others. */ disassociate(WindowToken token)177 void disassociate(WindowToken token) { 178 mAssociatedTokens.remove(token); 179 mRotatedContainers.remove(token); 180 } 181 } 182 183 private class DeathRecipient implements IBinder.DeathRecipient { 184 private boolean mHasUnlinkToDeath = false; 185 186 @Override binderDied()187 public void binderDied() { 188 synchronized (mWmService.mGlobalLock) { 189 mBinderDied = true; 190 removeImmediately(); 191 } 192 } 193 linkToDeath()194 void linkToDeath() throws RemoteException { 195 token.linkToDeath(DeathRecipient.this, 0); 196 } 197 unlinkToDeath()198 void unlinkToDeath() { 199 if (mHasUnlinkToDeath) { 200 return; 201 } 202 token.unlinkToDeath(DeathRecipient.this, 0); 203 mHasUnlinkToDeath = true; 204 } 205 } 206 207 /** 208 * Compares two child window of this token and returns -1 if the first is lesser than the 209 * second in terms of z-order and 1 otherwise. 210 */ 211 private final Comparator<WindowState> mWindowComparator = 212 (WindowState newWindow, WindowState existingWindow) -> { 213 final WindowToken token = WindowToken.this; 214 if (newWindow.mToken != token) { 215 throw new IllegalArgumentException("newWindow=" + newWindow 216 + " is not a child of token=" + token); 217 } 218 219 if (existingWindow.mToken != token) { 220 throw new IllegalArgumentException("existingWindow=" + existingWindow 221 + " is not a child of token=" + token); 222 } 223 224 return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1; 225 }; 226 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens)227 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 228 DisplayContent dc, boolean ownerCanManageAppTokens) { 229 this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, 230 false /* roundedCornerOverlay */); 231 } 232 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay)233 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 234 DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { 235 this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID, 236 roundedCornerOverlay, false /* fromClientToken */); 237 } 238 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, boolean roundedCornerOverlay, boolean fromClientToken)239 WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, 240 DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, 241 boolean roundedCornerOverlay, boolean fromClientToken) { 242 super(service); 243 token = _token; 244 windowType = type; 245 mPersistOnEmpty = persistOnEmpty; 246 mOwnerCanManageAppTokens = ownerCanManageAppTokens; 247 mOwnerUid = ownerUid; 248 mRoundedCornerOverlay = roundedCornerOverlay; 249 mFromClientToken = fromClientToken; 250 if (dc != null) { 251 dc.addWindowToken(token, this); 252 } 253 if (shouldReportToClient()) { 254 try { 255 mDeathRecipient = new DeathRecipient(); 256 mDeathRecipient.linkToDeath(); 257 } catch (RemoteException e) { 258 Slog.e(TAG, "Unable to add window token with type " + windowType + " on " 259 + "display " + dc.getDisplayId(), e); 260 mDeathRecipient = null; 261 return; 262 } 263 } 264 } 265 removeAllWindowsIfPossible()266 void removeAllWindowsIfPossible() { 267 for (int i = mChildren.size() - 1; i >= 0; --i) { 268 final WindowState win = mChildren.get(i); 269 ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT, 270 "removeAllWindowsIfPossible: removing win=%s", win); 271 win.removeIfPossible(); 272 } 273 } 274 setExiting()275 void setExiting() { 276 if (isEmpty()) { 277 super.removeImmediately(); 278 return; 279 } 280 281 // This token is exiting, so allow it to be removed when it no longer contains any windows. 282 mPersistOnEmpty = false; 283 284 if (!isVisible()) { 285 return; 286 } 287 288 final int count = mChildren.size(); 289 boolean changed = false; 290 final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN); 291 292 for (int i = 0; i < count; i++) { 293 final WindowState win = mChildren.get(i); 294 changed |= win.onSetAppExiting(); 295 } 296 297 final ActivityRecord app = asActivityRecord(); 298 if (app != null) { 299 app.setVisible(false); 300 } 301 302 if (changed) { 303 mWmService.mWindowPlacerLocked.performSurfacePlacement(); 304 mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/); 305 } 306 307 if (delayed) { 308 mDisplayContent.mExitingTokens.add(this); 309 } 310 } 311 312 /** 313 * @return The scale for applications running in compatibility mode. Multiply the size in the 314 * application by this scale will be the size in the screen. 315 */ getSizeCompatScale()316 float getSizeCompatScale() { 317 return mDisplayContent.mCompatibleScreenScale; 318 } 319 320 /** 321 * Returns true if the new window is considered greater than the existing window in terms of 322 * z-order. 323 */ isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow)324 protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow, 325 WindowState existingWindow) { 326 // New window is considered greater if it has a higher or equal base layer. 327 return newWindow.mBaseLayer >= existingWindow.mBaseLayer; 328 } 329 addWindow(final WindowState win)330 void addWindow(final WindowState win) { 331 ProtoLog.d(WM_DEBUG_FOCUS, 332 "addWindow: win=%s Callers=%s", win, Debug.getCallers(5)); 333 334 if (win.isChildWindow()) { 335 // Child windows are added to their parent windows. 336 return; 337 } 338 // This token is created from WindowContext and the client requests to addView now, create a 339 // surface for this token. 340 if (mSurfaceControl == null) { 341 createSurfaceControl(true /* force */); 342 } 343 if (!mChildren.contains(win)) { 344 ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this); 345 addChild(win, mWindowComparator); 346 mWmService.mWindowsChanged = true; 347 // TODO: Should we also be setting layout needed here and other places? 348 } 349 } 350 351 @Override createSurfaceControl(boolean force)352 void createSurfaceControl(boolean force) { 353 if (!mFromClientToken || force) { 354 super.createSurfaceControl(force); 355 } 356 } 357 358 /** Returns true if the token windows list is empty. */ isEmpty()359 boolean isEmpty() { 360 return mChildren.isEmpty(); 361 } 362 getReplacingWindow()363 WindowState getReplacingWindow() { 364 for (int i = mChildren.size() - 1; i >= 0; i--) { 365 final WindowState win = mChildren.get(i); 366 final WindowState replacing = win.getReplacingWindow(); 367 if (replacing != null) { 368 return replacing; 369 } 370 } 371 return null; 372 } 373 374 /** Return true if this token has a window that wants the wallpaper displayed behind it. */ windowsCanBeWallpaperTarget()375 boolean windowsCanBeWallpaperTarget() { 376 for (int j = mChildren.size() - 1; j >= 0; j--) { 377 final WindowState w = mChildren.get(j); 378 if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { 379 return true; 380 } 381 } 382 383 return false; 384 } 385 386 @Override removeImmediately()387 void removeImmediately() { 388 if (mDisplayContent != null) { 389 mDisplayContent.removeWindowToken(token); 390 } 391 // Needs to occur after the token is removed from the display above to avoid attempt at 392 // duplicate removal of this window container from it's parent. 393 super.removeImmediately(); 394 395 reportWindowTokenRemovedToClient(); 396 } 397 reportWindowTokenRemovedToClient()398 private void reportWindowTokenRemovedToClient() { 399 if (!shouldReportToClient()) { 400 return; 401 } 402 mDeathRecipient.unlinkToDeath(); 403 IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); 404 try { 405 windowTokenClient.onWindowTokenRemoved(); 406 } catch (RemoteException e) { 407 ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); 408 } 409 } 410 411 @Override onDisplayChanged(DisplayContent dc)412 void onDisplayChanged(DisplayContent dc) { 413 dc.reParentWindowToken(this); 414 415 // TODO(b/36740756): One day this should perhaps be hooked 416 // up with goodToGo, so we don't move a window 417 // to another display before the window behind 418 // it is ready. 419 super.onDisplayChanged(dc); 420 reportConfigToWindowTokenClient(); 421 } 422 423 @Override onConfigurationChanged(Configuration newParentConfig)424 public void onConfigurationChanged(Configuration newParentConfig) { 425 super.onConfigurationChanged(newParentConfig); 426 reportConfigToWindowTokenClient(); 427 } 428 reportConfigToWindowTokenClient()429 void reportConfigToWindowTokenClient() { 430 if (!shouldReportToClient()) { 431 return; 432 } 433 if (mLastReportedConfig == null) { 434 mLastReportedConfig = new Configuration(); 435 } 436 final Configuration config = getConfiguration(); 437 final int displayId = getDisplayContent().getDisplayId(); 438 if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) { 439 // No changes since last reported time. 440 return; 441 } 442 443 mLastReportedConfig.setTo(config); 444 mLastReportedDisplay = displayId; 445 446 IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); 447 try { 448 windowTokenClient.onConfigurationChanged(config, displayId); 449 } catch (RemoteException e) { 450 ProtoLog.w(WM_ERROR, 451 "Could not report config changes to the window token client."); 452 } 453 } 454 455 /** 456 * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and 457 * registered from client side. 458 */ shouldReportToClient()459 private boolean shouldReportToClient() { 460 // Only report to client for WindowToken because Activities are updated through ATM 461 // callbacks. 462 return asActivityRecord() == null 463 // Report to {@link android.view.WindowTokenClient} if this token was registered from it. 464 && mFromClientToken && !mBinderDied; 465 } 466 467 @Override assignLayer(SurfaceControl.Transaction t, int layer)468 void assignLayer(SurfaceControl.Transaction t, int layer) { 469 if (windowType == TYPE_DOCK_DIVIDER) { 470 // See {@link DisplayContent#mSplitScreenDividerAnchor} 471 super.assignRelativeLayer(t, 472 mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1); 473 } else if (mRoundedCornerOverlay) { 474 super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); 475 } else { 476 super.assignLayer(t, layer); 477 } 478 } 479 480 @Override makeSurface()481 SurfaceControl.Builder makeSurface() { 482 final SurfaceControl.Builder builder = super.makeSurface(); 483 if (mRoundedCornerOverlay) { 484 builder.setParent(null); 485 } 486 return builder; 487 } 488 hasFixedRotationTransform()489 boolean hasFixedRotationTransform() { 490 return mFixedRotationTransformState != null; 491 } 492 493 /** Returns {@code true} if the given token shares the same transform. */ hasFixedRotationTransform(WindowToken token)494 boolean hasFixedRotationTransform(WindowToken token) { 495 if (mFixedRotationTransformState == null || token == null) { 496 return false; 497 } 498 return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState; 499 } 500 isFinishingFixedRotationTransform()501 boolean isFinishingFixedRotationTransform() { 502 return mFixedRotationTransformState != null 503 && !mFixedRotationTransformState.mIsTransforming; 504 } 505 isFixedRotationTransforming()506 boolean isFixedRotationTransforming() { 507 return mFixedRotationTransformState != null 508 && mFixedRotationTransformState.mIsTransforming; 509 } 510 getFixedRotationTransformDisplayInfo()511 DisplayInfo getFixedRotationTransformDisplayInfo() { 512 return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayInfo : null; 513 } 514 getFixedRotationTransformDisplayFrames()515 DisplayFrames getFixedRotationTransformDisplayFrames() { 516 return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayFrames : null; 517 } 518 getFixedRotationTransformDisplayBounds()519 Rect getFixedRotationTransformDisplayBounds() { 520 return isFixedRotationTransforming() 521 ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration 522 .getBounds() 523 : null; 524 } 525 getFixedRotationBarContentFrame(int windowType)526 Rect getFixedRotationBarContentFrame(int windowType) { 527 return isFixedRotationTransforming() 528 ? mFixedRotationTransformState.mBarContentFrames.get(windowType) 529 : null; 530 } 531 getFixedRotationTransformInsetsState()532 InsetsState getFixedRotationTransformInsetsState() { 533 return isFixedRotationTransforming() ? mFixedRotationTransformState.mInsetsState : null; 534 } 535 536 /** Applies the rotated layout environment to this token in the simulated rotated display. */ applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config)537 void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, 538 Configuration config) { 539 if (mFixedRotationTransformState != null) { 540 mFixedRotationTransformState.disassociate(this); 541 } 542 mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, 543 new Configuration(config), mDisplayContent.getRotation()); 544 mFixedRotationTransformState.mAssociatedTokens.add(this); 545 mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, 546 mFixedRotationTransformState.mInsetsState, 547 mFixedRotationTransformState.mBarContentFrames); 548 onFixedRotationStatePrepared(); 549 } 550 551 /** 552 * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this 553 * one. This takes the same effect as {@link #applyFixedRotationTransform}. 554 */ linkFixedRotationTransform(WindowToken other)555 void linkFixedRotationTransform(WindowToken other) { 556 final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; 557 if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) { 558 return; 559 } 560 if (mFixedRotationTransformState != null) { 561 mFixedRotationTransformState.disassociate(this); 562 } 563 mFixedRotationTransformState = fixedRotationState; 564 fixedRotationState.mAssociatedTokens.add(this); 565 onFixedRotationStatePrepared(); 566 } 567 568 /** 569 * Makes the rotated states take effect for this window container and its client process. 570 * This should only be called when {@link #mFixedRotationTransformState} is non-null. 571 */ onFixedRotationStatePrepared()572 private void onFixedRotationStatePrepared() { 573 // Send the adjustment info first so when the client receives configuration change, it can 574 // get the rotated display metrics. 575 notifyFixedRotationTransform(true /* enabled */); 576 // Resolve the rotated configuration. 577 onConfigurationChanged(getParent().getConfiguration()); 578 final ActivityRecord r = asActivityRecord(); 579 if (r != null && r.hasProcess()) { 580 // The application needs to be configured as in a rotated environment for compatibility. 581 // This registration will send the rotated configuration to its process. 582 r.app.registerActivityConfigurationListener(r); 583 } 584 } 585 586 /** 587 * Return {@code true} if one of the associated activity is still animating. Otherwise, 588 * return {@code false}. 589 */ hasAnimatingFixedRotationTransition()590 boolean hasAnimatingFixedRotationTransition() { 591 if (mFixedRotationTransformState == null) { 592 return false; 593 } 594 595 for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) { 596 final ActivityRecord r = 597 mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord(); 598 if (r != null && r.isAnimating(TRANSITION | PARENTS)) { 599 return true; 600 } 601 } 602 return false; 603 } 604 finishFixedRotationTransform()605 void finishFixedRotationTransform() { 606 finishFixedRotationTransform(null /* applyDisplayRotation */); 607 } 608 609 /** 610 * Finishes the transform and apply display rotation if the action is given. If the display will 611 * not rotate, the transformed containers are restored to their original states. 612 */ finishFixedRotationTransform(Runnable applyDisplayRotation)613 void finishFixedRotationTransform(Runnable applyDisplayRotation) { 614 final FixedRotationTransformState state = mFixedRotationTransformState; 615 if (state == null) { 616 return; 617 } 618 619 state.resetTransform(); 620 // Clear the flag so if the display will be updated to the same orientation, the transform 621 // won't take effect. 622 state.mIsTransforming = false; 623 if (applyDisplayRotation != null) { 624 applyDisplayRotation.run(); 625 } 626 // The state is cleared at the end, because it is used to indicate that other windows can 627 // use seamless rotation when applying rotation to display. 628 for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { 629 final WindowToken token = state.mAssociatedTokens.get(i); 630 token.mFixedRotationTransformState = null; 631 token.notifyFixedRotationTransform(false /* enabled */); 632 if (applyDisplayRotation == null) { 633 // Notify cancellation because the display does not change rotation. 634 token.cancelFixedRotationTransform(); 635 } 636 } 637 } 638 639 /** Notifies application side to enable or disable the rotation adjustment of display info. */ notifyFixedRotationTransform(boolean enabled)640 void notifyFixedRotationTransform(boolean enabled) { 641 FixedRotationAdjustments adjustments = null; 642 // A token may contain windows of the same processes or different processes. The list is 643 // used to avoid sending the same adjustments to a process multiple times. 644 ArrayList<WindowProcessController> notifiedProcesses = null; 645 for (int i = mChildren.size() - 1; i >= 0; i--) { 646 final WindowState w = mChildren.get(i); 647 final WindowProcessController app; 648 if (w.mAttrs.type == TYPE_APPLICATION_STARTING) { 649 // Use the host activity because starting window is controlled by window manager. 650 final ActivityRecord r = asActivityRecord(); 651 if (r == null) { 652 continue; 653 } 654 app = r.app; 655 } else { 656 app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid); 657 } 658 if (app == null || !app.hasThread()) { 659 continue; 660 } 661 if (notifiedProcesses == null) { 662 notifiedProcesses = new ArrayList<>(2); 663 adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null; 664 } else if (notifiedProcesses.contains(app)) { 665 continue; 666 } 667 notifiedProcesses.add(app); 668 try { 669 mWmService.mAtmService.getLifecycleManager().scheduleTransaction( 670 app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments)); 671 } catch (RemoteException e) { 672 Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e); 673 } 674 } 675 } 676 677 /** Restores the changes that applies to this container. */ cancelFixedRotationTransform()678 private void cancelFixedRotationTransform() { 679 final WindowContainer<?> parent = getParent(); 680 if (parent == null) { 681 // The window may be detached or detaching. 682 return; 683 } 684 final int originalRotation = getWindowConfiguration().getRotation(); 685 onConfigurationChanged(parent.getConfiguration()); 686 onCancelFixedRotationTransform(originalRotation); 687 } 688 689 /** 690 * It is called when the window is using fixed rotation transform, and before display applies 691 * the same rotation, the rotation change for display is canceled, e.g. the orientation from 692 * sensor is updated to previous direction. 693 */ onCancelFixedRotationTransform(int originalDisplayRotation)694 void onCancelFixedRotationTransform(int originalDisplayRotation) { 695 } 696 createFixedRotationAdjustmentsIfNeeded()697 FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() { 698 if (!isFixedRotationTransforming()) { 699 return null; 700 } 701 final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo; 702 return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth, 703 displayInfo.appHeight, displayInfo.displayCutout); 704 } 705 706 @Override resolveOverrideConfiguration(Configuration newParentConfig)707 void resolveOverrideConfiguration(Configuration newParentConfig) { 708 super.resolveOverrideConfiguration(newParentConfig); 709 if (isFixedRotationTransforming()) { 710 // Apply the rotated configuration to current resolved configuration, so the merged 711 // override configuration can update to the same state. 712 getResolvedOverrideConfiguration().updateFrom( 713 mFixedRotationTransformState.mRotatedOverrideConfiguration); 714 } 715 } 716 717 @Override updateSurfacePosition(SurfaceControl.Transaction t)718 void updateSurfacePosition(SurfaceControl.Transaction t) { 719 super.updateSurfacePosition(t); 720 if (isFixedRotationTransforming()) { 721 // The window is layouted in a simulated rotated display but the real display hasn't 722 // rotated, so here transforms its surface to fit in the real display. 723 mFixedRotationTransformState.transform(this); 724 } 725 } 726 727 @Override resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t)728 void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) { 729 // Keep the transformed position to animate because the surface will show in different 730 // rotation than the animator of leash. 731 if (!isFixedRotationTransforming()) { 732 super.resetSurfacePositionForAnimationLeash(t); 733 } 734 } 735 736 /** 737 * Gives a chance to this {@link WindowToken} to adjust the {@link 738 * android.view.WindowManager.LayoutParams} of its windows. 739 */ adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs)740 void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) { 741 } 742 743 744 @CallSuper 745 @Override dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)746 public void dumpDebug(ProtoOutputStream proto, long fieldId, 747 @WindowTraceLogLevel int logLevel) { 748 if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) { 749 return; 750 } 751 752 final long token = proto.start(fieldId); 753 super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); 754 proto.write(HASH_CODE, System.identityHashCode(this)); 755 proto.write(WAITING_TO_SHOW, waitingToShow); 756 proto.write(PAUSED, paused); 757 proto.end(token); 758 } 759 760 @Override getProtoFieldId()761 long getProtoFieldId() { 762 return WINDOW_TOKEN; 763 } 764 dump(PrintWriter pw, String prefix, boolean dumpAll)765 void dump(PrintWriter pw, String prefix, boolean dumpAll) { 766 super.dump(pw, prefix, dumpAll); 767 pw.print(prefix); pw.print("windows="); pw.println(mChildren); 768 pw.print(prefix); pw.print("windowType="); pw.print(windowType); 769 pw.print(" hasVisible="); pw.print(hasVisible); 770 if (waitingToShow) { 771 pw.print(" waitingToShow=true"); 772 } 773 pw.println(); 774 if (hasFixedRotationTransform()) { 775 pw.print(prefix); 776 pw.print("fixedRotationConfig="); 777 pw.println(mFixedRotationTransformState.mRotatedOverrideConfiguration); 778 } 779 } 780 781 @Override toString()782 public String toString() { 783 if (stringName == null) { 784 StringBuilder sb = new StringBuilder(); 785 sb.append("WindowToken{"); 786 sb.append(Integer.toHexString(System.identityHashCode(this))); 787 sb.append(" "); sb.append(token); sb.append('}'); 788 stringName = sb.toString(); 789 } 790 return stringName; 791 } 792 793 @Override getName()794 String getName() { 795 return toString(); 796 } 797 798 /** 799 * Return whether windows from this token can layer above the 800 * system bars, or in other words extend outside of the "Decor Frame" 801 */ canLayerAboveSystemBars()802 boolean canLayerAboveSystemBars() { 803 int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, 804 mOwnerCanManageAppTokens); 805 int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR, 806 mOwnerCanManageAppTokens); 807 return mOwnerCanManageAppTokens && (layer > navLayer); 808 } 809 getWindowLayerFromType()810 int getWindowLayerFromType() { 811 return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens); 812 } 813 getOwnerUid()814 int getOwnerUid() { 815 return mOwnerUid; 816 } 817 } 818