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 android.view.InsetsState.ITYPE_CLIMATE_BAR; 20 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 21 import static android.view.InsetsState.ITYPE_IME; 22 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 23 import static android.view.InsetsState.ITYPE_STATUS_BAR; 24 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; 25 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; 26 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; 27 import static android.view.ViewRootImpl.sNewInsetsMode; 28 29 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; 30 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL; 31 import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED; 32 33 import android.annotation.NonNull; 34 import android.annotation.Nullable; 35 import android.graphics.Point; 36 import android.graphics.Rect; 37 import android.util.proto.ProtoOutputStream; 38 import android.view.InsetsSource; 39 import android.view.InsetsSourceControl; 40 import android.view.InsetsState; 41 import android.view.SurfaceControl; 42 import android.view.SurfaceControl.Transaction; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.function.TriConsumer; 46 import com.android.server.protolog.common.ProtoLog; 47 import com.android.server.wm.SurfaceAnimator.AnimationType; 48 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 49 50 import java.io.PrintWriter; 51 52 /** 53 * Controller for a specific inset source on the server. It's called provider as it provides the 54 * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. 55 */ 56 class InsetsSourceProvider { 57 58 protected final DisplayContent mDisplayContent; 59 protected final @NonNull InsetsSource mSource; 60 protected WindowState mWin; 61 62 private final Rect mTmpRect = new Rect(); 63 private final InsetsStateController mStateController; 64 private final InsetsSourceControl mFakeControl; 65 private @Nullable InsetsSourceControl mControl; 66 private @Nullable InsetsControlTarget mControlTarget; 67 private @Nullable InsetsControlTarget mPendingControlTarget; 68 private @Nullable InsetsControlTarget mFakeControlTarget; 69 70 private @Nullable ControlAdapter mAdapter; 71 private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; 72 private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider; 73 private final Rect mImeOverrideFrame = new Rect(); 74 private boolean mIsLeashReadyForDispatching; 75 76 /** The visibility override from the current controlling window. */ 77 private boolean mClientVisible; 78 79 /** 80 * Whether the window is available and considered visible as in {@link WindowState#isVisible}. 81 */ 82 private boolean mServerVisible; 83 84 private boolean mSeamlessRotating; 85 private long mFinishSeamlessRotateFrameNumber = -1; 86 87 private final boolean mControllable; 88 InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent)89 InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, 90 DisplayContent displayContent) { 91 mClientVisible = InsetsState.getDefaultVisibility(source.getType()); 92 mSource = source; 93 mDisplayContent = displayContent; 94 mStateController = stateController; 95 mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */, 96 new Point()); 97 98 final int type = source.getType(); 99 if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR || type == ITYPE_CLIMATE_BAR 100 || type == ITYPE_EXTRA_NAVIGATION_BAR) { 101 mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL; 102 } else if (type == ITYPE_IME) { 103 mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME; 104 } else { 105 mControllable = false; 106 } 107 } 108 getSource()109 InsetsSource getSource() { 110 return mSource; 111 } 112 113 /** 114 * @return Whether the current flag configuration allows to control this source. 115 */ isControllable()116 boolean isControllable() { 117 return mControllable; 118 } 119 120 /** 121 * Updates the window that currently backs this source. 122 * 123 * @param win The window that links to this source. 124 * @param frameProvider Based on display frame state and the window, calculates the resulting 125 * frame that should be reported to clients. 126 * @param imeFrameProvider Based on display frame state and the window, calculates the resulting 127 * frame that should be reported to IME. 128 */ setWindow(@ullable WindowState win, @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider, @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider)129 void setWindow(@Nullable WindowState win, 130 @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider, 131 @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) { 132 if (mWin != null) { 133 if (mControllable) { 134 mWin.setControllableInsetProvider(null); 135 } 136 // The window may be animating such that we can hand out the leash to the control 137 // target. Revoke the leash by cancelling the animation to correct the state. 138 // TODO: Ideally, we should wait for the animation to finish so previous window can 139 // animate-out as new one animates-in. 140 mWin.cancelAnimation(); 141 } 142 ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); 143 mWin = win; 144 mFrameProvider = frameProvider; 145 mImeFrameProvider = imeFrameProvider; 146 if (win == null) { 147 setServerVisible(false); 148 mSource.setFrame(new Rect()); 149 mSource.setVisibleFrame(null); 150 } else if (mControllable) { 151 mWin.setControllableInsetProvider(this); 152 if (mPendingControlTarget != null) { 153 updateControlForTarget(mPendingControlTarget, true /* force */); 154 mPendingControlTarget = null; 155 } 156 } 157 } 158 159 /** 160 * @return Whether there is a window which backs this source. 161 */ hasWindow()162 boolean hasWindow() { 163 return mWin != null; 164 } 165 166 /** 167 * The source frame can affect the layout of other windows, so this should be called once the 168 * window gets laid out. 169 */ updateSourceFrame()170 void updateSourceFrame() { 171 if (mWin == null) { 172 return; 173 } 174 175 // Make sure we set the valid source frame only when server visible is true, because the 176 // frame may not yet determined that server side doesn't think the window is ready to 177 // visible. (i.e. No surface, pending insets that were given during layout, etc..) 178 if (mServerVisible) { 179 mTmpRect.set(mWin.getFrameLw()); 180 if (mFrameProvider != null) { 181 mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect); 182 } else { 183 mTmpRect.inset(mWin.mGivenContentInsets); 184 } 185 } else { 186 mTmpRect.setEmpty(); 187 } 188 mSource.setFrame(mTmpRect); 189 190 if (mImeFrameProvider != null) { 191 mImeOverrideFrame.set(mWin.getFrameLw()); 192 mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, 193 mImeOverrideFrame); 194 } 195 196 if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0 197 || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) { 198 mTmpRect.set(mWin.getFrameLw()); 199 mTmpRect.inset(mWin.mGivenVisibleInsets); 200 mSource.setVisibleFrame(mTmpRect); 201 } else { 202 mSource.setVisibleFrame(null); 203 } 204 } 205 206 /** @return A new source computed by the specified window frame in the given display frames. */ createSimulatedSource(DisplayFrames displayFrames, WindowFrames windowFrames)207 InsetsSource createSimulatedSource(DisplayFrames displayFrames, WindowFrames windowFrames) { 208 // Don't copy visible frame because it might not be calculated in the provided display 209 // frames and it is not significant for this usage. 210 final InsetsSource source = new InsetsSource(mSource.getType()); 211 source.setVisible(mSource.isVisible()); 212 mTmpRect.set(windowFrames.mFrame); 213 if (mFrameProvider != null) { 214 mFrameProvider.accept(displayFrames, mWin, mTmpRect); 215 } 216 source.setFrame(mTmpRect); 217 return source; 218 } 219 220 /** 221 * Called when a layout pass has occurred. 222 */ onPostLayout()223 void onPostLayout() { 224 if (mWin == null) { 225 return; 226 } 227 228 setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy() 229 && !mWin.mGivenInsetsPending); 230 updateSourceFrame(); 231 if (mControl != null) { 232 final Rect frame = mWin.getWindowFrames().mFrame; 233 if (mControl.setSurfacePosition(frame.left, frame.top) && mControlTarget != null) { 234 // The leash has been stale, we need to create a new one for the client. 235 updateControlForTarget(mControlTarget, true /* force */); 236 mStateController.notifyControlChanged(mControlTarget); 237 } 238 } 239 } 240 241 /** 242 * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) 243 */ updateControlForFakeTarget(@ullable InsetsControlTarget fakeTarget)244 void updateControlForFakeTarget(@Nullable InsetsControlTarget fakeTarget) { 245 if (fakeTarget == mFakeControlTarget) { 246 return; 247 } 248 mFakeControlTarget = fakeTarget; 249 } 250 updateControlForTarget(@ullable InsetsControlTarget target, boolean force)251 void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { 252 if (mSeamlessRotating) { 253 // We are un-rotating the window against the display rotation. We don't want the target 254 // to control the window for now. 255 return; 256 } 257 if (target != null && target.getWindow() != null) { 258 // ime control target could be a different window. 259 // Refer WindowState#getImeControlTarget(). 260 target = target.getWindow().getImeControlTarget(); 261 } 262 263 if (mWin != null && mWin.getSurfaceControl() == null) { 264 // if window doesn't have a surface, set it null and return. 265 setWindow(null, null, null); 266 } 267 if (mWin == null) { 268 mPendingControlTarget = target; 269 return; 270 } 271 if (target == mControlTarget && !force) { 272 return; 273 } 274 if (target == null) { 275 // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields. 276 mWin.cancelAnimation(); 277 setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); 278 return; 279 } 280 mAdapter = new ControlAdapter(); 281 if (getSource().getType() == ITYPE_IME) { 282 setClientVisible(target.getImeRequestedVisibility(mSource.getType())); 283 } 284 final Transaction t = mDisplayContent.getPendingTransaction(); 285 mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */, 286 ANIMATION_TYPE_INSETS_CONTROL); 287 288 // The leash was just created. We cannot dispatch it until its surface transaction is 289 // applied. Otherwise, the client's operation to the leash might be overwritten by us. 290 mIsLeashReadyForDispatching = false; 291 292 final SurfaceControl leash = mAdapter.mCapturedLeash; 293 final long frameNumber = mFinishSeamlessRotateFrameNumber; 294 mFinishSeamlessRotateFrameNumber = -1; 295 if (frameNumber >= 0 && mWin.mHasSurface && leash != null) { 296 // We just finished the seamless rotation. We don't want to change the position or the 297 // window crop of the surface controls (including the leash) until the client finishes 298 // drawing the new frame of the new orientation. Although we cannot defer the reparent 299 // operation, it is fine, because reparent won't cause any visual effect. 300 final SurfaceControl barrier = mWin.getClientViewRootSurface(); 301 t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); 302 t.deferTransactionUntil(leash, barrier, frameNumber); 303 } 304 mControlTarget = target; 305 updateVisibility(); 306 mControl = new InsetsSourceControl(mSource.getType(), leash, 307 new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); 308 ProtoLog.d(WM_DEBUG_IME, 309 "InsetsSource Control %s for target %s", mControl, mControlTarget); 310 } 311 startSeamlessRotation()312 void startSeamlessRotation() { 313 if (!mSeamlessRotating) { 314 mSeamlessRotating = true; 315 316 // This will revoke the leash and clear the control target. 317 mWin.cancelAnimation(); 318 } 319 } 320 finishSeamlessRotation(boolean timeout)321 void finishSeamlessRotation(boolean timeout) { 322 if (mSeamlessRotating) { 323 mSeamlessRotating = false; 324 mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber(); 325 } 326 } 327 onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource)328 boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) { 329 if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) { 330 return false; 331 } 332 setClientVisible(modifiedSource.isVisible()); 333 return true; 334 } 335 onSurfaceTransactionApplied()336 void onSurfaceTransactionApplied() { 337 mIsLeashReadyForDispatching = true; 338 } 339 setClientVisible(boolean clientVisible)340 private void setClientVisible(boolean clientVisible) { 341 if (mClientVisible == clientVisible) { 342 return; 343 } 344 mClientVisible = clientVisible; 345 mDisplayContent.mWmService.mH.obtainMessage( 346 LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget(); 347 updateVisibility(); 348 } 349 350 @VisibleForTesting setServerVisible(boolean serverVisible)351 void setServerVisible(boolean serverVisible) { 352 mServerVisible = serverVisible; 353 updateVisibility(); 354 } 355 updateVisibility()356 private void updateVisibility() { 357 mSource.setVisible(mServerVisible && (isMirroredSource() || mClientVisible)); 358 ProtoLog.d(WM_DEBUG_IME, 359 "InsetsSource updateVisibility serverVisible: %s clientVisible: %s", 360 mServerVisible, mClientVisible); 361 } 362 isMirroredSource()363 private boolean isMirroredSource() { 364 if (mWin == null) { 365 return false; 366 } 367 final int[] provides = mWin.mAttrs.providesInsetsTypes; 368 if (provides == null) { 369 return false; 370 } 371 for (int i = 0; i < provides.length; i++) { 372 if (provides[i] == ITYPE_IME) { 373 return true; 374 } 375 } 376 return false; 377 } 378 getControl(InsetsControlTarget target)379 InsetsSourceControl getControl(InsetsControlTarget target) { 380 if (target == mControlTarget) { 381 if (!mIsLeashReadyForDispatching && mControl != null) { 382 // The surface transaction of preparing leash is not applied yet. We don't send it 383 // to the client in case that the client applies its transaction sooner than ours 384 // that we could unexpectedly overwrite the surface state. 385 return new InsetsSourceControl(mControl.getType(), null /* leash */, 386 mControl.getSurfacePosition()); 387 } 388 return mControl; 389 } 390 if (target == mFakeControlTarget) { 391 return mFakeControl; 392 } 393 return null; 394 } 395 getControlTarget()396 InsetsControlTarget getControlTarget() { 397 return mControlTarget; 398 } 399 isClientVisible()400 boolean isClientVisible() { 401 return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible; 402 } 403 404 /** 405 * @return Whether this provider uses a different frame to dispatch to the IME. 406 */ overridesImeFrame()407 boolean overridesImeFrame() { 408 return mImeFrameProvider != null; 409 } 410 411 /** 412 * @return Rect to dispatch to the IME as frame. Only valid if {@link #overridesImeFrame()} 413 * returns {@code true}. 414 */ getImeOverrideFrame()415 Rect getImeOverrideFrame() { 416 return mImeOverrideFrame; 417 } 418 dump(PrintWriter pw, String prefix)419 public void dump(PrintWriter pw, String prefix) { 420 pw.println(prefix + "InsetsSourceProvider"); 421 pw.print(prefix + " mSource="); mSource.dump(prefix + " ", pw); 422 if (mControl != null) { 423 pw.print(prefix + " mControl="); 424 mControl.dump(prefix + " ", pw); 425 } 426 pw.print(prefix + " mFakeControl="); mFakeControl.dump(prefix + " ", pw); 427 pw.print(" mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching); 428 pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toString()); 429 if (mWin != null) { 430 pw.print(prefix + " mWin="); 431 mWin.dump(pw, prefix + " ", false /* dumpAll */); 432 } 433 if (mAdapter != null) { 434 pw.print(prefix + " mAdapter="); 435 mAdapter.dump(pw, prefix + " "); 436 } 437 if (mControlTarget != null) { 438 pw.print(prefix + " mControlTarget="); 439 if (mControlTarget.getWindow() != null) { 440 mControlTarget.getWindow().dump(pw, prefix + " ", false /* dumpAll */); 441 } 442 } 443 if (mPendingControlTarget != null) { 444 pw.print(prefix + " mPendingControlTarget="); 445 if (mPendingControlTarget.getWindow() != null) { 446 mPendingControlTarget.getWindow().dump(pw, prefix + " ", false /* dumpAll */); 447 } 448 } 449 if (mFakeControlTarget != null) { 450 pw.print(prefix + " mFakeControlTarget="); 451 if (mFakeControlTarget.getWindow() != null) { 452 mFakeControlTarget.getWindow().dump(pw, prefix + " ", false /* dumpAll */); 453 } 454 } 455 } 456 457 private class ControlAdapter implements AnimationAdapter { 458 459 private SurfaceControl mCapturedLeash; 460 461 @Override getShowWallpaper()462 public boolean getShowWallpaper() { 463 return false; 464 } 465 466 @Override startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type, OnAnimationFinishedCallback finishCallback)467 public void startAnimation(SurfaceControl animationLeash, Transaction t, 468 @AnimationType int type, OnAnimationFinishedCallback finishCallback) { 469 // TODO(b/118118435): We can remove the type check when implementing the transient bar 470 // animation. 471 if (mSource.getType() == ITYPE_IME) { 472 // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed. 473 t.setAlpha(animationLeash, 1 /* alpha */); 474 t.hide(animationLeash); 475 } 476 ProtoLog.i(WM_DEBUG_IME, 477 "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource, 478 mControlTarget); 479 480 mCapturedLeash = animationLeash; 481 final Rect frame = mWin.getWindowFrames().mFrame; 482 t.setPosition(mCapturedLeash, frame.left, frame.top); 483 } 484 485 @Override onAnimationCancelled(SurfaceControl animationLeash)486 public void onAnimationCancelled(SurfaceControl animationLeash) { 487 if (mAdapter == this) { 488 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this); 489 mControl = null; 490 mControlTarget = null; 491 mAdapter = null; 492 setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); 493 ProtoLog.i(WM_DEBUG_IME, 494 "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s", 495 mSource, mControlTarget); 496 } 497 } 498 499 @Override getDurationHint()500 public long getDurationHint() { 501 return 0; 502 } 503 504 @Override getStatusBarTransitionsStartTime()505 public long getStatusBarTransitionsStartTime() { 506 return 0; 507 } 508 509 @Override dump(PrintWriter pw, String prefix)510 public void dump(PrintWriter pw, String prefix) { 511 pw.println(prefix + "ControlAdapter"); 512 pw.print(prefix + " mCapturedLeash="); pw.print(mCapturedLeash); 513 } 514 515 @Override dumpDebug(ProtoOutputStream proto)516 public void dumpDebug(ProtoOutputStream proto) { 517 } 518 } 519 } 520