1 /* 2 * Copyright (C) 2018 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 com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS; 20 import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH; 21 import static com.android.server.wm.InsetsSourceProviderProto.CLIENT_VISIBLE; 22 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL; 23 import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE; 24 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET; 25 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL; 26 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET; 27 import static com.android.server.wm.InsetsSourceProviderProto.FRAME; 28 import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING; 29 import static com.android.server.wm.InsetsSourceProviderProto.PENDING_CONTROL_TARGET; 30 import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING; 31 import static com.android.server.wm.InsetsSourceProviderProto.SERVER_VISIBLE; 32 import static com.android.server.wm.InsetsSourceProviderProto.SOURCE; 33 import static com.android.server.wm.InsetsSourceProviderProto.SOURCE_WINDOW_STATE; 34 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL; 35 36 import android.annotation.NonNull; 37 import android.annotation.Nullable; 38 import android.graphics.Insets; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.util.SparseArray; 42 import android.util.proto.ProtoOutputStream; 43 import android.view.InsetsSource; 44 import android.view.InsetsSource.Flags; 45 import android.view.InsetsSourceControl; 46 import android.view.SurfaceControl; 47 import android.view.SurfaceControl.Transaction; 48 import android.view.WindowInsets; 49 50 import com.android.internal.annotations.VisibleForTesting; 51 import com.android.internal.protolog.common.ProtoLog; 52 import com.android.internal.util.function.TriFunction; 53 import com.android.server.wm.SurfaceAnimator.AnimationType; 54 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 55 56 import java.io.PrintWriter; 57 import java.util.function.Consumer; 58 59 /** 60 * Controller for a specific inset source on the server. It's called provider as it provides the 61 * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}. 62 */ 63 class InsetsSourceProvider { 64 65 private static final Rect EMPTY_RECT = new Rect(); 66 67 protected final @NonNull InsetsSource mSource; 68 protected final @NonNull DisplayContent mDisplayContent; 69 protected final @NonNull InsetsStateController mStateController; 70 protected @Nullable WindowContainer mWindowContainer; 71 protected @Nullable InsetsSourceControl mControl; 72 protected @Nullable InsetsControlTarget mControlTarget; 73 protected boolean mIsLeashReadyForDispatching; 74 75 private final Rect mTmpRect = new Rect(); 76 private final InsetsSourceControl mFakeControl; 77 private final Consumer<Transaction> mSetLeashPositionConsumer; 78 private @Nullable InsetsControlTarget mPendingControlTarget; 79 private @Nullable InsetsControlTarget mFakeControlTarget; 80 81 private @Nullable ControlAdapter mAdapter; 82 private TriFunction<DisplayFrames, WindowContainer, Rect, Integer> mFrameProvider; 83 private SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>> 84 mOverrideFrameProviders; 85 private final SparseArray<Rect> mOverrideFrames = new SparseArray<Rect>(); 86 private final Rect mSourceFrame = new Rect(); 87 private final Rect mLastSourceFrame = new Rect(); 88 private @NonNull Insets mInsetsHint = Insets.NONE; 89 private boolean mInsetsHintStale = true; 90 private @Flags int mFlagsFromFrameProvider; 91 private @Flags int mFlagsFromServer; 92 private boolean mHasPendingPosition; 93 94 /** The visibility override from the current controlling window. */ 95 private boolean mClientVisible; 96 97 /** 98 * Whether the window container is available and considered visible as in 99 * {@link WindowContainer#isVisible}. 100 */ 101 private boolean mServerVisible; 102 103 private boolean mSeamlessRotating; 104 105 private final boolean mControllable; 106 107 /** 108 * Whether to forced the dimensions of the source window container to the inset frame and crop 109 * out any overflow. 110 * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar 111 * rounded corners overlays. 112 * 113 * TODO: Remove when we enable shell transitions (b/202383002) 114 */ 115 private boolean mCropToProvidingInsets = false; 116 InsetsSourceProvider(@onNull InsetsSource source, @NonNull InsetsStateController stateController, @NonNull DisplayContent displayContent)117 InsetsSourceProvider(@NonNull InsetsSource source, 118 @NonNull InsetsStateController stateController, 119 @NonNull DisplayContent displayContent) { 120 mClientVisible = (WindowInsets.Type.defaultVisible() & source.getType()) != 0; 121 mSource = source; 122 mDisplayContent = displayContent; 123 mStateController = stateController; 124 mFakeControl = new InsetsSourceControl( 125 source.getId(), source.getType(), null /* leash */, false /* initialVisible */, 126 new Point(), Insets.NONE); 127 mControllable = (InsetsPolicy.CONTROLLABLE_TYPES & source.getType()) != 0; 128 mSetLeashPositionConsumer = t -> { 129 if (mControl != null) { 130 final SurfaceControl leash = mControl.getLeash(); 131 if (leash != null) { 132 final Point position = mControl.getSurfacePosition(); 133 t.setPosition(leash, position.x, position.y); 134 } 135 } 136 if (mHasPendingPosition) { 137 mHasPendingPosition = false; 138 if (mPendingControlTarget != mControlTarget) { 139 mStateController.notifyControlTargetChanged(mPendingControlTarget, this); 140 } 141 } 142 }; 143 } 144 getSource()145 InsetsSource getSource() { 146 return mSource; 147 } 148 149 /** 150 * @return Whether the current flag configuration allows to control this source. 151 */ isControllable()152 boolean isControllable() { 153 return mControllable; 154 } 155 156 /** 157 * Updates the window container that currently backs this source. 158 * 159 * @param windowContainer The window container that links to this source. 160 * @param frameProvider Based on display frame state and the window, calculates the resulting 161 * frame that should be reported to clients. 162 * This will only be used when the window container providing the insets is 163 * not a WindowState. 164 * @param overrideFrameProviders Based on display frame state and the window, calculates the 165 * resulting frame that should be reported to given window type. 166 */ setWindowContainer(@ullable WindowContainer windowContainer, @Nullable TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider, @Nullable SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>> overrideFrameProviders)167 void setWindowContainer(@Nullable WindowContainer windowContainer, 168 @Nullable TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider, 169 @Nullable SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>> 170 overrideFrameProviders) { 171 if (mWindowContainer != null) { 172 if (mControllable) { 173 mWindowContainer.setControllableInsetProvider(null); 174 } 175 // The window container may be animating such that we can hand out the leash to the 176 // control target. Revoke the leash by cancelling the animation to correct the state. 177 // TODO: Ideally, we should wait for the animation to finish so previous window can 178 // animate-out as new one animates-in. 179 mWindowContainer.cancelAnimation(); 180 mWindowContainer.getInsetsSourceProviders().remove(mSource.getId()); 181 mSeamlessRotating = false; 182 mHasPendingPosition = false; 183 } 184 ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", 185 windowContainer, WindowInsets.Type.toString(mSource.getType())); 186 mWindowContainer = windowContainer; 187 // TODO: remove the frame provider for non-WindowState container. 188 mFrameProvider = frameProvider; 189 mOverrideFrames.clear(); 190 mOverrideFrameProviders = overrideFrameProviders; 191 if (windowContainer == null) { 192 setServerVisible(false); 193 mSource.setVisibleFrame(null); 194 mSource.setFlags(0, 0xffffffff); 195 mSourceFrame.setEmpty(); 196 } else { 197 mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this); 198 if (mControllable) { 199 mWindowContainer.setControllableInsetProvider(this); 200 if (mPendingControlTarget != mControlTarget) { 201 mStateController.notifyControlTargetChanged(mPendingControlTarget, this); 202 } 203 } 204 } 205 } 206 setFlags(@lags int flags, @Flags int mask)207 boolean setFlags(@Flags int flags, @Flags int mask) { 208 mFlagsFromServer = (mFlagsFromServer & ~mask) | (flags & mask); 209 final @Flags int mergedFlags = mFlagsFromFrameProvider | mFlagsFromServer; 210 if (mSource.getFlags() != mergedFlags) { 211 mSource.setFlags(mergedFlags); 212 return true; 213 } 214 return false; 215 } 216 217 /** 218 * The source frame can affect the layout of other windows, so this should be called once the 219 * window container gets laid out. 220 */ updateSourceFrame(Rect frame)221 void updateSourceFrame(Rect frame) { 222 if (mWindowContainer == null) { 223 return; 224 } 225 WindowState win = mWindowContainer.asWindowState(); 226 227 if (win == null) { 228 // For all the non window WindowContainers. 229 if (mServerVisible) { 230 mTmpRect.set(mWindowContainer.getBounds()); 231 if (mFrameProvider != null) { 232 mFrameProvider.apply(mWindowContainer.getDisplayContent().mDisplayFrames, 233 mWindowContainer, mTmpRect); 234 } 235 } else { 236 mTmpRect.setEmpty(); 237 } 238 mSource.setFrame(mTmpRect); 239 mSource.setVisibleFrame(null); 240 return; 241 } 242 243 mSourceFrame.set(frame); 244 if (mFrameProvider != null) { 245 mFlagsFromFrameProvider = mFrameProvider.apply( 246 mWindowContainer.getDisplayContent().mDisplayFrames, 247 mWindowContainer, 248 mSourceFrame); 249 mSource.setFlags(mFlagsFromFrameProvider | mFlagsFromServer); 250 } 251 updateSourceFrameForServerVisibility(); 252 if (!mLastSourceFrame.equals(mSourceFrame)) { 253 mLastSourceFrame.set(mSourceFrame); 254 mInsetsHintStale = true; 255 } 256 257 if (mOverrideFrameProviders != null) { 258 // Not necessary to clear the mOverrideFrames here. It will be cleared every time the 259 // override frame provider updates. 260 for (int i = mOverrideFrameProviders.size() - 1; i >= 0; i--) { 261 final int windowType = mOverrideFrameProviders.keyAt(i); 262 final Rect overrideFrame; 263 if (mOverrideFrames.contains(windowType)) { 264 overrideFrame = mOverrideFrames.get(windowType); 265 overrideFrame.set(frame); 266 } else { 267 overrideFrame = new Rect(frame); 268 } 269 final TriFunction<DisplayFrames, WindowContainer, Rect, Integer> provider = 270 mOverrideFrameProviders.get(windowType); 271 if (provider != null) { 272 mOverrideFrameProviders.get(windowType).apply( 273 mWindowContainer.getDisplayContent().mDisplayFrames, mWindowContainer, 274 overrideFrame); 275 } 276 mOverrideFrames.put(windowType, overrideFrame); 277 } 278 } 279 280 if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0 281 || win.mGivenVisibleInsets.right != 0 282 || win.mGivenVisibleInsets.bottom != 0) { 283 mTmpRect.set(frame); 284 mTmpRect.inset(win.mGivenVisibleInsets); 285 mSource.setVisibleFrame(mTmpRect); 286 } else { 287 mSource.setVisibleFrame(null); 288 } 289 } 290 updateSourceFrameForServerVisibility()291 private void updateSourceFrameForServerVisibility() { 292 // Make sure we set the valid source frame only when server visible is true, because the 293 // frame may not yet be determined that server side doesn't think the window is ready to 294 // visible. (i.e. No surface, pending insets that were given during layout, etc..) 295 final Rect frame = mServerVisible ? mSourceFrame : EMPTY_RECT; 296 if (mSource.getFrame().equals(frame)) { 297 return; 298 } 299 mSource.setFrame(frame); 300 if (mWindowContainer != null) { 301 mSource.updateSideHint(mWindowContainer.getBounds()); 302 } 303 } 304 onWindowContainerBoundsChanged()305 void onWindowContainerBoundsChanged() { 306 mInsetsHintStale = true; 307 } 308 309 @VisibleForTesting getInsetsHint()310 Insets getInsetsHint() { 311 if (!mServerVisible) { 312 return mInsetsHint; 313 } 314 final WindowState win = mWindowContainer.asWindowState(); 315 if (win != null && win.mGivenInsetsPending) { 316 return mInsetsHint; 317 } 318 if (mInsetsHintStale) { 319 final Rect bounds = mWindowContainer.getBounds(); 320 mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */); 321 mInsetsHintStale = false; 322 } 323 return mInsetsHint; 324 } 325 326 /** @return A new source computed by the specified window frame in the given display frames. */ createSimulatedSource(DisplayFrames displayFrames, Rect frame)327 InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect frame) { 328 final InsetsSource source = new InsetsSource(mSource); 329 mTmpRect.set(frame); 330 if (mFrameProvider != null) { 331 mFrameProvider.apply(displayFrames, mWindowContainer, mTmpRect); 332 } 333 source.setFrame(mTmpRect); 334 335 // Don't copy visible frame because it might not be calculated in the provided display 336 // frames and it is not significant for this usage. 337 source.setVisibleFrame(null); 338 339 return source; 340 } 341 342 /** 343 * Called when a layout pass has occurred. 344 */ onPostLayout()345 void onPostLayout() { 346 if (mWindowContainer == null) { 347 return; 348 } 349 WindowState windowState = mWindowContainer.asWindowState(); 350 boolean isServerVisible = windowState != null 351 ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy() 352 : mWindowContainer.isVisibleRequested(); 353 354 if (android.view.inputmethod.Flags.refactorInsetsController()) { 355 if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible 356 && isServerVisible && windowState != null) { 357 // in case the IME becomes visible, we need to check if it is already drawn and 358 // does not have given insets pending. If it's not yet drawn, we do not set 359 // server visibility 360 isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending; 361 } 362 } 363 final boolean serverVisibleChanged = mServerVisible != isServerVisible; 364 setServerVisible(isServerVisible); 365 updateInsetsControlPosition(windowState, serverVisibleChanged); 366 } 367 updateInsetsControlPosition(WindowState windowState)368 void updateInsetsControlPosition(WindowState windowState) { 369 updateInsetsControlPosition(windowState, false); 370 } 371 updateInsetsControlPosition(WindowState windowState, boolean serverVisibleChanged)372 private void updateInsetsControlPosition(WindowState windowState, 373 boolean serverVisibleChanged) { 374 if (mControl == null) { 375 return; 376 } 377 boolean changed = false; 378 final Point position = getWindowFrameSurfacePosition(); 379 if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) { 380 changed = true; 381 if (windowState != null && windowState.getWindowFrames().didFrameSizeChange() 382 && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) { 383 mHasPendingPosition = true; 384 windowState.applyWithNextDraw(mSetLeashPositionConsumer); 385 } else { 386 Transaction t = mWindowContainer.getSyncTransaction(); 387 if (windowState != null) { 388 // Make the buffer, token transformation, and leash position to be updated 389 // together when the window is drawn for new rotation. Otherwise the window 390 // may be outside the screen by the inconsistent orientations. 391 final AsyncRotationController rotationController = 392 mDisplayContent.getAsyncRotationController(); 393 if (rotationController != null) { 394 final Transaction drawT = 395 rotationController.getDrawTransaction(windowState.mToken); 396 if (drawT != null) { 397 t = drawT; 398 } 399 } 400 } 401 mSetLeashPositionConsumer.accept(t); 402 } 403 } 404 final Insets insetsHint = getInsetsHint(); 405 if (!mControl.getInsetsHint().equals(insetsHint)) { 406 mControl.setInsetsHint(insetsHint); 407 changed = true; 408 } 409 if (android.view.inputmethod.Flags.refactorInsetsController() && serverVisibleChanged) { 410 changed = true; 411 } 412 if (changed) { 413 mStateController.notifyControlChanged(mControlTarget); 414 } 415 } 416 getWindowFrameSurfacePosition()417 private Point getWindowFrameSurfacePosition() { 418 final WindowState win = mWindowContainer.asWindowState(); 419 if (win != null && mControl != null) { 420 final AsyncRotationController controller = mDisplayContent.getAsyncRotationController(); 421 if (controller != null && controller.shouldFreezeInsetsPosition(win)) { 422 // Use previous position because the window still shows with old rotation. 423 return mControl.getSurfacePosition(); 424 } 425 } 426 final Rect frame = win != null ? win.getFrame() : mWindowContainer.getBounds(); 427 final Point position = new Point(); 428 mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position); 429 return position; 430 } 431 432 /** 433 * @see InsetsStateController#onControlTargetChanged 434 */ updateFakeControlTarget(@ullable InsetsControlTarget fakeTarget)435 void updateFakeControlTarget(@Nullable InsetsControlTarget fakeTarget) { 436 if (fakeTarget == mFakeControlTarget) { 437 return; 438 } 439 mFakeControlTarget = fakeTarget; 440 } 441 442 /** 443 * Ensures that the inset source window container is cropped so that anything that doesn't fit 444 * within the inset frame is cropped out until removeCropToProvidingInsetsBounds is called. 445 * 446 * The inset source surface will get cropped to the be of the size of the insets it's providing. 447 * 448 * For example, for the taskbar window which serves as the ITYPE_EXTRA_NAVIGATION_BAR inset 449 * source, the window is larger than the insets because of the rounded corners overlay, but 450 * during task animations we want to make sure that the overlay is cropped out of the window so 451 * that they don't hide the window animations. 452 * 453 * @param t The transaction to use to apply immediate overflow cropping operations. 454 * 455 * NOTE: The relies on the inset source window to have a leash (usually this would be a leash 456 * for the ANIMATION_TYPE_INSETS_CONTROL animation if the inset is controlled by the client) 457 * 458 * TODO: Remove when we migrate over to shell transitions (b/202383002) 459 */ setCropToProvidingInsetsBounds(Transaction t)460 void setCropToProvidingInsetsBounds(Transaction t) { 461 mCropToProvidingInsets = true; 462 463 if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) { 464 // apply to existing leash 465 t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash, 466 getProvidingInsetsBoundsCropRect()); 467 } 468 } 469 470 /** 471 * Removes any overflow cropping and future cropping to the inset source window's leash that may 472 * have been set with a call to setCropToProvidingInsetsBounds(). 473 * @param t The transaction to use to apply immediate removal of overflow cropping. 474 * 475 * TODO: Remove when we migrate over to shell transitions (b/202383002) 476 */ removeCropToProvidingInsetsBounds(Transaction t)477 void removeCropToProvidingInsetsBounds(Transaction t) { 478 mCropToProvidingInsets = false; 479 480 // apply to existing leash 481 if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) { 482 t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash, null); 483 } 484 } 485 getProvidingInsetsBoundsCropRect()486 private Rect getProvidingInsetsBoundsCropRect() { 487 Rect sourceWindowFrame = mWindowContainer.asWindowState() != null 488 ? mWindowContainer.asWindowState().getFrame() 489 : mWindowContainer.getBounds(); 490 Rect insetFrame = getSource().getFrame(); 491 492 // The rectangle in buffer space we want to crop to 493 return new Rect( 494 insetFrame.left - sourceWindowFrame.left, 495 insetFrame.top - sourceWindowFrame.top, 496 insetFrame.right - sourceWindowFrame.left, 497 insetFrame.bottom - sourceWindowFrame.top 498 ); 499 } 500 updateControlForTarget(@ullable InsetsControlTarget target, boolean force)501 void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { 502 if (mSeamlessRotating) { 503 // We are un-rotating the window against the display rotation. We don't want the target 504 // to control the window for now. 505 return; 506 } 507 mPendingControlTarget = target; 508 509 if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) { 510 // if window doesn't have a surface, set it null and return. 511 setWindowContainer(null, null, null); 512 } 513 if (mWindowContainer == null) { 514 return; 515 } 516 if (target == mControlTarget && !force) { 517 return; 518 } 519 if (mHasPendingPosition) { 520 // Don't create a new leash while having a pending position. Otherwise, the position 521 // will be changed earlier than expected, which can cause flicker. 522 return; 523 } 524 if (target == null) { 525 // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields. 526 mWindowContainer.cancelAnimation(); 527 setClientVisible((WindowInsets.Type.defaultVisible() & mSource.getType()) != 0); 528 return; 529 } 530 final Point surfacePosition = getWindowFrameSurfacePosition(); 531 mAdapter = new ControlAdapter(surfacePosition); 532 if (mSource.getType() == WindowInsets.Type.ime()) { 533 setClientVisible(target.isRequestedVisible(WindowInsets.Type.ime())); 534 } 535 final Transaction t = mWindowContainer.getSyncTransaction(); 536 mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */, 537 ANIMATION_TYPE_INSETS_CONTROL); 538 539 // The leash was just created. We cannot dispatch it until its surface transaction is 540 // applied. Otherwise, the client's operation to the leash might be overwritten by us. 541 mIsLeashReadyForDispatching = false; 542 543 final SurfaceControl leash = mAdapter.mCapturedLeash; 544 mControlTarget = target; 545 updateVisibility(); 546 boolean initiallyVisible = mClientVisible; 547 if (mSource.getType() == WindowInsets.Type.ime()) { 548 // The IME cannot be initially visible, see ControlAdapter#startAnimation below. 549 // Also, the ImeInsetsSourceConsumer clears the client visibility upon losing control, 550 // but this won't have reached here yet by the time the new control is created. 551 // Note: The DisplayImeController needs the correct previous client's visibility, so we 552 // only override the initiallyVisible here. 553 initiallyVisible = false; 554 } 555 mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash, 556 initiallyVisible, surfacePosition, getInsetsHint()); 557 558 ProtoLog.d(WM_DEBUG_WINDOW_INSETS, 559 "InsetsSource Control %s for target %s", mControl, mControlTarget); 560 } 561 startSeamlessRotation()562 void startSeamlessRotation() { 563 if (!mSeamlessRotating) { 564 mSeamlessRotating = true; 565 mWindowContainer.cancelAnimation(); 566 } 567 } 568 finishSeamlessRotation()569 void finishSeamlessRotation() { 570 mSeamlessRotating = false; 571 } 572 updateClientVisibility(InsetsControlTarget caller)573 boolean updateClientVisibility(InsetsControlTarget caller) { 574 final boolean requestedVisible = caller.isRequestedVisible(mSource.getType()); 575 if (caller != mControlTarget || requestedVisible == mClientVisible) { 576 return false; 577 } 578 setClientVisible(requestedVisible); 579 return true; 580 } 581 onSurfaceTransactionApplied()582 void onSurfaceTransactionApplied() { 583 mIsLeashReadyForDispatching = true; 584 } 585 setClientVisible(boolean clientVisible)586 void setClientVisible(boolean clientVisible) { 587 if (mClientVisible == clientVisible) { 588 return; 589 } 590 mClientVisible = clientVisible; 591 updateVisibility(); 592 // The visibility change needs a traversal to apply. 593 mDisplayContent.setLayoutNeeded(); 594 mDisplayContent.mWmService.mWindowPlacerLocked.requestTraversal(); 595 } 596 597 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) setServerVisible(boolean serverVisible)598 void setServerVisible(boolean serverVisible) { 599 mServerVisible = serverVisible; 600 updateSourceFrameForServerVisibility(); 601 updateVisibility(); 602 } 603 updateVisibility()604 protected void updateVisibility() { 605 mSource.setVisible(mServerVisible && mClientVisible); 606 ProtoLog.d(WM_DEBUG_WINDOW_INSETS, 607 "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s", 608 WindowInsets.Type.toString(mSource.getType()), 609 mServerVisible, mClientVisible); 610 } 611 isLeashReadyForDispatching()612 protected boolean isLeashReadyForDispatching() { 613 return mIsLeashReadyForDispatching; 614 } 615 616 /** 617 * Gets the source control for the given control target. If this is the provider's control 618 * target, but the leash is not ready for dispatching, a new source control object with the 619 * leash set to {@code null} is returned. 620 * 621 * @param target the control target to get the source control for. 622 */ 623 @Nullable getControl(InsetsControlTarget target)624 InsetsSourceControl getControl(InsetsControlTarget target) { 625 if (target == mControlTarget) { 626 if (!isLeashReadyForDispatching() && mControl != null) { 627 // The surface transaction of preparing leash is not applied yet. We don't send it 628 // to the client in case that the client applies its transaction sooner than ours 629 // that we could unexpectedly overwrite the surface state. 630 return new InsetsSourceControl(mControl.getId(), mControl.getType(), 631 null /* leash */, mControl.isInitiallyVisible(), 632 mControl.getSurfacePosition(), mControl.getInsetsHint()); 633 } 634 return mControl; 635 } 636 if (target == mFakeControlTarget) { 637 return mFakeControl; 638 } 639 return null; 640 } 641 642 /** 643 * Gets the leash of the source control for the given control target. If this is not the 644 * provider's control target, or the leash is not ready for dispatching, this will 645 * return {@code null}. 646 * 647 * @param target the control target to get the source control leash for. 648 */ 649 @Nullable getLeash(@onNull InsetsControlTarget target)650 protected SurfaceControl getLeash(@NonNull InsetsControlTarget target) { 651 return target == mControlTarget && mIsLeashReadyForDispatching && mControl != null 652 ? mControl.getLeash() : null; 653 } 654 655 @Nullable getControlTarget()656 InsetsControlTarget getControlTarget() { 657 return mControlTarget; 658 } 659 660 @Nullable getFakeControlTarget()661 InsetsControlTarget getFakeControlTarget() { 662 return mFakeControlTarget; 663 } 664 isClientVisible()665 boolean isClientVisible() { 666 return mClientVisible; 667 } 668 overridesFrame(int windowType)669 boolean overridesFrame(int windowType) { 670 return mOverrideFrames.contains(windowType); 671 } 672 getOverriddenFrame(int windowType)673 Rect getOverriddenFrame(int windowType) { 674 return mOverrideFrames.get(windowType); 675 } 676 dump(PrintWriter pw, String prefix)677 public void dump(PrintWriter pw, String prefix) { 678 pw.println(prefix + getClass().getSimpleName()); 679 prefix = prefix + " "; 680 pw.print(prefix + "mSource="); mSource.dump("", pw); 681 pw.print(prefix + "mSourceFrame="); 682 pw.println(mSourceFrame); 683 if (mOverrideFrames.size() > 0) { 684 pw.print(prefix + "mOverrideFrames="); 685 pw.println(mOverrideFrames); 686 } 687 if (mControl != null) { 688 pw.print(prefix + "mControl="); 689 mControl.dump("", pw); 690 } 691 if (mControllable) { 692 pw.print(prefix + "mInsetsHint="); 693 pw.print(mInsetsHint); 694 if (mInsetsHintStale) { 695 pw.print(" stale"); 696 } 697 pw.println(); 698 } 699 pw.print(prefix); 700 pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching); 701 pw.print(" mHasPendingPosition="); pw.print(mHasPendingPosition); 702 pw.println(); 703 if (mWindowContainer != null) { 704 pw.print(prefix + "mWindowContainer="); 705 pw.println(mWindowContainer); 706 } 707 if (mAdapter != null) { 708 pw.print(prefix + "mAdapter="); 709 mAdapter.dump(pw, ""); 710 } 711 if (mControlTarget != null) { 712 pw.print(prefix + "mControlTarget="); 713 pw.println(mControlTarget); 714 } 715 if (mPendingControlTarget != mControlTarget) { 716 pw.print(prefix + "mPendingControlTarget="); 717 pw.println(mPendingControlTarget); 718 } 719 if (mFakeControlTarget != null) { 720 pw.print(prefix + "mFakeControlTarget="); 721 pw.println(mFakeControlTarget); 722 } 723 } 724 dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)725 void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { 726 final long token = proto.start(fieldId); 727 mSource.dumpDebug(proto, SOURCE); 728 mTmpRect.dumpDebug(proto, FRAME); 729 mFakeControl.dumpDebug(proto, FAKE_CONTROL); 730 if (mControl != null) { 731 mControl.dumpDebug(proto, CONTROL); 732 } 733 if (mControlTarget != null && mControlTarget.getWindow() != null) { 734 mControlTarget.getWindow().dumpDebug(proto, CONTROL_TARGET, logLevel); 735 } 736 if (mPendingControlTarget != null && mPendingControlTarget != mControlTarget 737 && mPendingControlTarget.getWindow() != null) { 738 mPendingControlTarget.getWindow().dumpDebug(proto, PENDING_CONTROL_TARGET, logLevel); 739 } 740 if (mFakeControlTarget != null && mFakeControlTarget.getWindow() != null) { 741 mFakeControlTarget.getWindow().dumpDebug(proto, FAKE_CONTROL_TARGET, logLevel); 742 } 743 if (mAdapter != null && mAdapter.mCapturedLeash != null) { 744 mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH); 745 } 746 proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching); 747 proto.write(CLIENT_VISIBLE, mClientVisible); 748 proto.write(SERVER_VISIBLE, mServerVisible); 749 proto.write(SEAMLESS_ROTATING, mSeamlessRotating); 750 proto.write(CONTROLLABLE, mControllable); 751 if (mWindowContainer != null && mWindowContainer.asWindowState() != null) { 752 mWindowContainer.asWindowState().dumpDebug(proto, SOURCE_WINDOW_STATE, logLevel); 753 } 754 proto.end(token); 755 } 756 757 private class ControlAdapter implements AnimationAdapter { 758 759 private final Point mSurfacePosition; 760 private SurfaceControl mCapturedLeash; 761 ControlAdapter(Point surfacePosition)762 ControlAdapter(Point surfacePosition) { 763 mSurfacePosition = surfacePosition; 764 } 765 766 @Override getShowWallpaper()767 public boolean getShowWallpaper() { 768 return false; 769 } 770 771 @Override startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback)772 public void startAnimation(SurfaceControl animationLeash, Transaction t, 773 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) { 774 // TODO(b/166736352): Check if we still need to control the IME visibility here. 775 if (mSource.getType() == WindowInsets.Type.ime()) { 776 if (!android.view.inputmethod.Flags.refactorInsetsController() || !mClientVisible) { 777 // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed. 778 t.setAlpha(animationLeash, 1 /* alpha */); 779 t.hide(animationLeash); 780 } 781 } 782 ProtoLog.i(WM_DEBUG_WINDOW_INSETS, 783 "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource, 784 mControlTarget); 785 786 mCapturedLeash = animationLeash; 787 t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y); 788 789 if (mCropToProvidingInsets) { 790 // Apply crop to hide overflow 791 t.setWindowCrop(mCapturedLeash, getProvidingInsetsBoundsCropRect()); 792 } 793 } 794 795 @Override onAnimationCancelled(SurfaceControl animationLeash)796 public void onAnimationCancelled(SurfaceControl animationLeash) { 797 if (mAdapter == this) { 798 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this); 799 mControl = null; 800 mControlTarget = null; 801 mAdapter = null; 802 setClientVisible((WindowInsets.Type.defaultVisible() & mSource.getType()) != 0); 803 ProtoLog.i(WM_DEBUG_WINDOW_INSETS, 804 "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s", 805 mSource, mControlTarget); 806 } 807 } 808 809 @Override getDurationHint()810 public long getDurationHint() { 811 return 0; 812 } 813 814 @Override getStatusBarTransitionsStartTime()815 public long getStatusBarTransitionsStartTime() { 816 return 0; 817 } 818 819 @Override dump(PrintWriter pw, String prefix)820 public void dump(PrintWriter pw, String prefix) { 821 pw.print(prefix + "ControlAdapter mCapturedLeash="); 822 pw.print(mCapturedLeash); 823 pw.println(); 824 } 825 826 @Override dumpDebug(ProtoOutputStream proto)827 public void dumpDebug(ProtoOutputStream proto) { 828 } 829 } 830 } 831