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