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