1 /* 2 * Copyright (C) 2019 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.app.StatusBarManager.WINDOW_STATE_HIDDEN; 20 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 25 import static android.view.InsetsController.ANIMATION_TYPE_HIDE; 26 import static android.view.InsetsController.ANIMATION_TYPE_SHOW; 27 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; 28 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 29 import static android.view.InsetsState.ITYPE_CAPTION_BAR; 30 import static android.view.InsetsState.ITYPE_CLIMATE_BAR; 31 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 32 import static android.view.InsetsState.ITYPE_IME; 33 import static android.view.InsetsState.ITYPE_INVALID; 34 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 35 import static android.view.InsetsState.ITYPE_STATUS_BAR; 36 import static android.view.SyncRtSurfaceTransactionApplier.applyParams; 37 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 38 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; 39 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; 40 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 41 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 42 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 43 44 import android.annotation.NonNull; 45 import android.annotation.Nullable; 46 import android.app.ActivityTaskManager; 47 import android.app.StatusBarManager; 48 import android.app.WindowConfiguration; 49 import android.content.ComponentName; 50 import android.content.res.Resources; 51 import android.graphics.Rect; 52 import android.util.ArrayMap; 53 import android.util.IntArray; 54 import android.util.SparseArray; 55 import android.view.InsetsAnimationControlCallbacks; 56 import android.view.InsetsAnimationControlImpl; 57 import android.view.InsetsAnimationControlRunner; 58 import android.view.InsetsController; 59 import android.view.InsetsFrameProvider; 60 import android.view.InsetsSource; 61 import android.view.InsetsSourceControl; 62 import android.view.InsetsState; 63 import android.view.InsetsState.InternalInsetsType; 64 import android.view.InternalInsetsAnimationController; 65 import android.view.SurfaceControl; 66 import android.view.SyncRtSurfaceTransactionApplier; 67 import android.view.WindowInsets.Type; 68 import android.view.WindowInsetsAnimation; 69 import android.view.WindowInsetsAnimation.Bounds; 70 import android.view.WindowInsetsAnimationControlListener; 71 import android.view.WindowManager; 72 73 import com.android.internal.R; 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.server.DisplayThread; 76 import com.android.server.statusbar.StatusBarManagerInternal; 77 78 /** 79 * Policy that implements who gets control over the windows generating insets. 80 */ 81 class InsetsPolicy { 82 83 private final InsetsStateController mStateController; 84 private final DisplayContent mDisplayContent; 85 private final DisplayPolicy mPolicy; 86 private final IntArray mShowingTransientTypes = new IntArray(); 87 88 /** For resetting visibilities of insets sources. */ 89 private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { 90 91 @Override 92 public void notifyInsetsControlChanged() { 93 boolean hasLeash = false; 94 final InsetsSourceControl[] controls = 95 mStateController.getControlsForDispatch(this); 96 if (controls == null) { 97 return; 98 } 99 for (InsetsSourceControl control : controls) { 100 final @InternalInsetsType int type = control.getType(); 101 if (mShowingTransientTypes.indexOf(type) != -1) { 102 // The visibilities of transient bars will be handled with animations. 103 continue; 104 } 105 final SurfaceControl leash = control.getLeash(); 106 if (leash != null) { 107 hasLeash = true; 108 109 // We use alpha to control the visibility here which aligns the logic at 110 // SurfaceAnimator.createAnimationLeash 111 mDisplayContent.getPendingTransaction().setAlpha( 112 leash, InsetsState.getDefaultVisibility(type) ? 1f : 0f); 113 } 114 } 115 if (hasLeash) { 116 mDisplayContent.scheduleAnimation(); 117 } 118 } 119 }; 120 121 private WindowState mFocusedWin; 122 private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); 123 private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); 124 private boolean mAnimatingShown; 125 /** 126 * Let remote insets controller control system bars regardless of other settings. 127 */ 128 private boolean mRemoteInsetsControllerControlsSystemBars; 129 private final boolean mHideNavBarForKeyboard; 130 private final float[] mTmpFloat9 = new float[9]; 131 InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent)132 InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { 133 mStateController = stateController; 134 mDisplayContent = displayContent; 135 mPolicy = displayContent.getDisplayPolicy(); 136 final Resources r = mPolicy.getContext().getResources(); 137 mRemoteInsetsControllerControlsSystemBars = r.getBoolean( 138 R.bool.config_remoteInsetsControllerControlsSystemBars); 139 mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); 140 } 141 getRemoteInsetsControllerControlsSystemBars()142 boolean getRemoteInsetsControllerControlsSystemBars() { 143 return mRemoteInsetsControllerControlsSystemBars; 144 } 145 146 /** 147 * Used only for testing. 148 */ 149 @VisibleForTesting setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars)150 void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) { 151 mRemoteInsetsControllerControlsSystemBars = controlsSystemBars; 152 } 153 154 /** Updates the target which can control system bars. */ updateBarControlTarget(@ullable WindowState focusedWin)155 void updateBarControlTarget(@Nullable WindowState focusedWin) { 156 if (mFocusedWin != focusedWin) { 157 abortTransient(); 158 } 159 mFocusedWin = focusedWin; 160 final InsetsControlTarget statusControlTarget = 161 getStatusControlTarget(focusedWin, false /* fake */); 162 final InsetsControlTarget navControlTarget = 163 getNavControlTarget(focusedWin, false /* fake */); 164 final WindowState notificationShade = mPolicy.getNotificationShade(); 165 final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow(); 166 mStateController.onBarControlTargetChanged( 167 statusControlTarget, 168 statusControlTarget == mDummyControlTarget 169 ? getStatusControlTarget(focusedWin, true /* fake */) 170 : statusControlTarget == notificationShade 171 ? getStatusControlTarget(topApp, true /* fake */) 172 : null, 173 navControlTarget, 174 navControlTarget == mDummyControlTarget 175 ? getNavControlTarget(focusedWin, true /* fake */) 176 : navControlTarget == notificationShade 177 ? getNavControlTarget(topApp, true /* fake */) 178 : null); 179 mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR); 180 mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR); 181 } 182 isHidden(@nternalInsetsType int type)183 boolean isHidden(@InternalInsetsType int type) { 184 final WindowContainerInsetsSourceProvider provider = mStateController 185 .peekSourceProvider(type); 186 return provider != null && provider.hasWindowContainer() 187 && !provider.getSource().isVisible(); 188 } 189 showTransient(@nternalInsetsType int[] types, boolean isGestureOnSystemBar)190 void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) { 191 boolean changed = false; 192 for (int i = types.length - 1; i >= 0; i--) { 193 final @InternalInsetsType int type = types[i]; 194 if (!isHidden(type)) { 195 continue; 196 } 197 if (mShowingTransientTypes.indexOf(type) != -1) { 198 continue; 199 } 200 mShowingTransientTypes.add(type); 201 changed = true; 202 } 203 if (changed) { 204 StatusBarManagerInternal statusBarManagerInternal = 205 mPolicy.getStatusBarManagerInternal(); 206 if (statusBarManagerInternal != null) { 207 statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(), 208 mShowingTransientTypes.toArray(), isGestureOnSystemBar); 209 } 210 updateBarControlTarget(mFocusedWin); 211 dispatchTransientSystemBarsVisibilityChanged( 212 mFocusedWin, 213 isTransient(ITYPE_STATUS_BAR) || isTransient(ITYPE_NAVIGATION_BAR), 214 isGestureOnSystemBar); 215 216 // The leashes can be created while updating bar control target. The surface transaction 217 // of the new leashes might not be applied yet. The callback posted here ensures we can 218 // get the valid leashes because the surface transaction will be applied in the next 219 // animation frame which will be triggered if a new leash is created. 220 mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { 221 synchronized (mDisplayContent.mWmService.mGlobalLock) { 222 startAnimation(true /* show */, null /* callback */); 223 } 224 }); 225 } 226 } 227 hideTransient()228 void hideTransient() { 229 if (mShowingTransientTypes.size() == 0) { 230 return; 231 } 232 233 dispatchTransientSystemBarsVisibilityChanged( 234 mFocusedWin, 235 /* areVisible= */ false, 236 /* wereRevealedFromSwipeOnSystemBar= */ false); 237 238 startAnimation(false /* show */, () -> { 239 synchronized (mDisplayContent.mWmService.mGlobalLock) { 240 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 241 // We are about to clear mShowingTransientTypes, we don't want the transient bar 242 // can cause insets on the client. Restore the client visibility. 243 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 244 mStateController.getSourceProvider(type).setClientVisible(false); 245 } 246 mShowingTransientTypes.clear(); 247 updateBarControlTarget(mFocusedWin); 248 } 249 }); 250 } 251 isTransient(@nternalInsetsType int type)252 boolean isTransient(@InternalInsetsType int type) { 253 return mShowingTransientTypes.indexOf(type) != -1; 254 } 255 256 /** 257 * Adjusts the sources in {@code originalState} to account for things like transient bars, IME 258 * & rounded corners. 259 */ adjustInsetsForWindow(WindowState target, InsetsState originalState, boolean includesTransient)260 InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState, 261 boolean includesTransient) { 262 InsetsState state; 263 if (!includesTransient) { 264 state = adjustVisibilityForTransientTypes(originalState); 265 } else { 266 state = originalState; 267 } 268 state = adjustVisibilityForIme(target, state, state == originalState); 269 return adjustInsetsForRoundedCorners(target.mToken, state, state == originalState); 270 } 271 adjustInsetsForWindow(WindowState target, InsetsState originalState)272 InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) { 273 return adjustInsetsForWindow(target, originalState, false); 274 } 275 276 /** 277 * @see WindowState#getInsetsState() 278 */ getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)279 InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { 280 final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs); 281 final WindowToken token = mDisplayContent.getWindowToken(attrs.token); 282 if (token != null) { 283 final InsetsState rotatedState = token.getFixedRotationTransformInsetsState(); 284 if (rotatedState != null) { 285 return rotatedState; 286 } 287 } 288 final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); 289 // Always use windowing mode fullscreen when get insets for window metrics to make sure it 290 // contains all insets types. 291 final InsetsState originalState = mDisplayContent.getInsetsPolicy() 292 .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop, 293 attrs.type, mStateController.getRawInsetsState()); 294 InsetsState state = adjustVisibilityForTransientTypes(originalState); 295 return adjustInsetsForRoundedCorners(token, state, state == originalState); 296 } 297 298 /** 299 * @param type the internal type of the insets. 300 * @return {@code true} if the given type is controllable, {@code false} otherwise. 301 */ isInsetsTypeControllable(@nternalInsetsType int type)302 static boolean isInsetsTypeControllable(@InternalInsetsType int type) { 303 switch (type) { 304 case ITYPE_STATUS_BAR: 305 case ITYPE_NAVIGATION_BAR: 306 case ITYPE_IME: 307 case ITYPE_CLIMATE_BAR: 308 case ITYPE_EXTRA_NAVIGATION_BAR: 309 return true; 310 default: 311 return false; 312 } 313 } 314 getInsetsTypeForLayoutParams( WindowManager.LayoutParams attrs)315 private static @InternalInsetsType int getInsetsTypeForLayoutParams( 316 WindowManager.LayoutParams attrs) { 317 @WindowManager.LayoutParams.WindowType int type = attrs.type; 318 switch (type) { 319 case TYPE_STATUS_BAR: 320 return ITYPE_STATUS_BAR; 321 case TYPE_NAVIGATION_BAR: 322 return ITYPE_NAVIGATION_BAR; 323 case TYPE_INPUT_METHOD: 324 return ITYPE_IME; 325 } 326 327 // If not one of the types above, check whether an internal inset mapping is specified. 328 if (attrs.providedInsets != null) { 329 for (InsetsFrameProvider provider : attrs.providedInsets) { 330 switch (provider.type) { 331 case ITYPE_STATUS_BAR: 332 case ITYPE_NAVIGATION_BAR: 333 case ITYPE_CLIMATE_BAR: 334 case ITYPE_EXTRA_NAVIGATION_BAR: 335 return provider.type; 336 } 337 } 338 } 339 340 return ITYPE_INVALID; 341 } 342 343 344 /** 345 * Modifies the given {@code state} according to the {@code type} (Inset type) provided by 346 * the target. 347 * When performing layout of the target or dispatching insets to the target, we need to exclude 348 * sources which should not be visible to the target. e.g., the source which represents the 349 * target window itself, and the IME source when the target is above IME. We also need to 350 * exclude certain types of insets source for client within specific windowing modes. 351 * 352 * @param type the inset type provided by the target 353 * @param windowingMode the windowing mode of the target 354 * @param isAlwaysOnTop is the target always on top 355 * @param windowType the type of the target 356 * @param state the input inset state containing all the sources 357 * @return The state stripped of the necessary information. 358 */ enforceInsetsPolicyForTarget(@nternalInsetsType int type, @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, int windowType, InsetsState state)359 InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type, 360 @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, 361 int windowType, InsetsState state) { 362 boolean stateCopied = false; 363 364 if (type != ITYPE_INVALID) { 365 state = new InsetsState(state); 366 stateCopied = true; 367 state.removeSource(type); 368 369 // Navigation bar doesn't get influenced by anything else 370 if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { 371 state.removeSource(ITYPE_STATUS_BAR); 372 state.removeSource(ITYPE_CLIMATE_BAR); 373 state.removeSource(ITYPE_CAPTION_BAR); 374 state.removeSource(ITYPE_NAVIGATION_BAR); 375 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); 376 } 377 378 // Status bar doesn't get influenced by caption bar 379 if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) { 380 state.removeSource(ITYPE_CAPTION_BAR); 381 } 382 } 383 ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController 384 .getSourceProviders(); 385 for (int i = providers.size() - 1; i >= 0; i--) { 386 WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i); 387 if (otherProvider.overridesFrame(windowType)) { 388 if (!stateCopied) { 389 state = new InsetsState(state); 390 stateCopied = true; 391 } 392 InsetsSource override = 393 new InsetsSource(state.getSource(otherProvider.getSource().getType())); 394 override.setFrame(otherProvider.getOverriddenFrame(windowType)); 395 state.addSource(override); 396 } 397 } 398 399 if (WindowConfiguration.isFloating(windowingMode) 400 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { 401 if (!stateCopied) { 402 state = new InsetsState(state); 403 stateCopied = true; 404 } 405 state.removeSource(ITYPE_STATUS_BAR); 406 state.removeSource(ITYPE_NAVIGATION_BAR); 407 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); 408 if (windowingMode == WINDOWING_MODE_PINNED) { 409 state.removeSource(ITYPE_IME); 410 } 411 } 412 413 return state; 414 } 415 adjustVisibilityForTransientTypes(InsetsState originalState)416 private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) { 417 InsetsState state = originalState; 418 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 419 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 420 final InsetsSource originalSource = state.peekSource(type); 421 if (originalSource != null && originalSource.isVisible()) { 422 if (state == originalState) { 423 // The source will be modified, create a non-deep copy to store the new one. 424 state = new InsetsState(originalState); 425 } 426 // Replace the source with a copy in invisible state. 427 final InsetsSource source = new InsetsSource(originalSource); 428 source.setVisible(false); 429 state.addSource(source); 430 } 431 } 432 return state; 433 } 434 adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState)435 private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, 436 boolean copyState) { 437 if (w.mIsImWindow) { 438 // If navigation bar is not hidden by IME, IME should always receive visible 439 // navigation bar insets. 440 final boolean navVisible = !mHideNavBarForKeyboard; 441 final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); 442 if (originalNavSource != null && originalNavSource.isVisible() != navVisible) { 443 final InsetsState state = copyState ? new InsetsState(originalState) 444 : originalState; 445 final InsetsSource navSource = new InsetsSource(originalNavSource); 446 navSource.setVisible(navVisible); 447 state.addSource(navSource); 448 return state; 449 } 450 } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { 451 // During switching tasks with gestural navigation, before the next IME input target 452 // starts the input, we should adjust and freeze the last IME visibility of the window 453 // in case delivering obsoleted IME insets state during transitioning. 454 final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); 455 456 if (originalImeSource != null) { 457 final boolean imeVisibility = w.getRequestedVisibility(ITYPE_IME); 458 final InsetsState state = copyState ? new InsetsState(originalState) 459 : originalState; 460 final InsetsSource imeSource = new InsetsSource(originalImeSource); 461 imeSource.setVisible(imeVisibility); 462 state.addSource(imeSource); 463 return state; 464 } 465 } 466 return originalState; 467 } 468 adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState, boolean copyState)469 private InsetsState adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState, 470 boolean copyState) { 471 if (token != null) { 472 final ActivityRecord activityRecord = token.asActivityRecord(); 473 final Task task = activityRecord != null ? activityRecord.getTask() : null; 474 if (task != null && !task.getWindowConfiguration().tasksAreFloating()) { 475 // Use task bounds to calculating rounded corners if the task is not floating. 476 final Rect roundedCornerFrame = new Rect(task.getBounds()); 477 final InsetsState state = copyState ? new InsetsState(originalState) 478 : originalState; 479 state.setRoundedCornerFrame(roundedCornerFrame); 480 return state; 481 } 482 } 483 return originalState; 484 } 485 onInsetsModified(InsetsControlTarget caller)486 void onInsetsModified(InsetsControlTarget caller) { 487 mStateController.onInsetsModified(caller); 488 checkAbortTransient(caller); 489 updateBarControlTarget(mFocusedWin); 490 } 491 492 /** 493 * Called when a control target modified the insets state. If the target set a insets source to 494 * visible while it is shown transiently, we need to abort the transient state. While IME is 495 * requested visible, we also need to abort the transient state of navigation bar if it is shown 496 * transiently. 497 * 498 * @param caller who changed the insets state. 499 */ checkAbortTransient(InsetsControlTarget caller)500 private void checkAbortTransient(InsetsControlTarget caller) { 501 if (mShowingTransientTypes.size() != 0) { 502 final IntArray abortTypes = new IntArray(); 503 final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME); 504 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 505 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 506 if ((mStateController.isFakeTarget(type, caller) 507 && caller.getRequestedVisibility(type)) 508 || (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) { 509 mShowingTransientTypes.remove(i); 510 abortTypes.add(type); 511 } 512 } 513 StatusBarManagerInternal statusBarManagerInternal = 514 mPolicy.getStatusBarManagerInternal(); 515 if (abortTypes.size() > 0 && statusBarManagerInternal != null) { 516 statusBarManagerInternal.abortTransient( 517 mDisplayContent.getDisplayId(), abortTypes.toArray()); 518 } 519 } 520 } 521 522 /** 523 * If the caller is not {@link #updateBarControlTarget}, it should call 524 * updateBarControlTarget(mFocusedWin) after this invocation. 525 */ abortTransient()526 private void abortTransient() { 527 StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal(); 528 if (statusBarManagerInternal != null) { 529 statusBarManagerInternal.abortTransient( 530 mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray()); 531 } 532 mShowingTransientTypes.clear(); 533 534 dispatchTransientSystemBarsVisibilityChanged( 535 mFocusedWin, 536 /* areVisible= */ false, 537 /* wereRevealedFromSwipeOnSystemBar= */ false); 538 } 539 getStatusControlTarget(@ullable WindowState focusedWin, boolean fake)540 private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, 541 boolean fake) { 542 if (!fake && isShowingTransientTypes(Type.statusBars())) { 543 return mDummyControlTarget; 544 } 545 final WindowState notificationShade = mPolicy.getNotificationShade(); 546 if (focusedWin == notificationShade) { 547 // Notification shade has control anyways, no reason to force anything. 548 return focusedWin; 549 } 550 if (remoteInsetsControllerControlsSystemBars(focusedWin)) { 551 ComponentName component = focusedWin.mActivityRecord != null 552 ? focusedWin.mActivityRecord.mActivityComponent : null; 553 mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( 554 component, focusedWin.getRequestedVisibilities()); 555 return mDisplayContent.mRemoteInsetsControlTarget; 556 } 557 if (mPolicy.areSystemBarsForcedShownLw()) { 558 // Status bar is forcibly shown. We don't want the client to control the status bar, and 559 // we will dispatch the real visibility of status bar to the client. 560 return null; 561 } 562 if (forceShowsStatusBarTransiently() && !fake) { 563 // Status bar is forcibly shown transiently, and its new visibility won't be 564 // dispatched to the client so that we can keep the layout stable. We will dispatch the 565 // fake control to the client, so that it can re-show the bar during this scenario. 566 return mDummyControlTarget; 567 } 568 if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar() 569 && (notificationShade == null || !notificationShade.canReceiveKeys())) { 570 // Non-fullscreen focused window should not break the state that the top-fullscreen-app 571 // window hides status bar, unless the notification shade can receive keys. 572 return mPolicy.getTopFullscreenOpaqueWindow(); 573 } 574 return focusedWin; 575 } 576 canBeTopFullscreenOpaqueWindow(@ullable WindowState win)577 private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) { 578 // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may 579 // haven't drawn or committed the visibility. 580 final boolean nonAttachedAppWindow = win != null 581 && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 582 && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 583 return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent() 584 && !win.inMultiWindowMode(); 585 } 586 getNavControlTarget(@ullable WindowState focusedWin, boolean fake)587 private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, 588 boolean fake) { 589 final WindowState imeWin = mDisplayContent.mInputMethodWindow; 590 if (imeWin != null && imeWin.isVisible() && !mHideNavBarForKeyboard) { 591 // Force showing navigation bar while IME is visible and if navigation bar is not 592 // configured to be hidden by the IME. 593 return null; 594 } 595 if (!fake && isShowingTransientTypes(Type.navigationBars())) { 596 return mDummyControlTarget; 597 } 598 if (focusedWin == mPolicy.getNotificationShade()) { 599 // Notification shade has control anyways, no reason to force anything. 600 return focusedWin; 601 } 602 if (mPolicy.isForceShowNavigationBarEnabled() && focusedWin != null 603 && focusedWin.getActivityType() == ACTIVITY_TYPE_STANDARD) { 604 // When "force show navigation bar" is enabled, it means both force visible is true, and 605 // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown 606 // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could 607 // still control the navigation bar in this mode. 608 return null; 609 } 610 if (remoteInsetsControllerControlsSystemBars(focusedWin)) { 611 ComponentName component = focusedWin.mActivityRecord != null 612 ? focusedWin.mActivityRecord.mActivityComponent : null; 613 mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( 614 component, focusedWin.getRequestedVisibilities()); 615 return mDisplayContent.mRemoteInsetsControlTarget; 616 } 617 if (mPolicy.areSystemBarsForcedShownLw()) { 618 // Navigation bar is forcibly shown. We don't want the client to control the navigation 619 // bar, and we will dispatch the real visibility of navigation bar to the client. 620 return null; 621 } 622 if (forceShowsNavigationBarTransiently() && !fake) { 623 // Navigation bar is forcibly shown transiently, and its new visibility won't be 624 // dispatched to the client so that we can keep the layout stable. We will dispatch the 625 // fake control to the client, so that it can re-show the bar during this scenario. 626 return mDummyControlTarget; 627 } 628 return focusedWin; 629 } 630 isShowingTransientTypes(@ype.InsetsType int types)631 private boolean isShowingTransientTypes(@Type.InsetsType int types) { 632 final IntArray showingTransientTypes = mShowingTransientTypes; 633 for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { 634 if ((InsetsState.toPublicType(showingTransientTypes.get(i)) & types) != 0) { 635 return true; 636 } 637 } 638 return false; 639 } 640 641 /** 642 * Determines whether the remote insets controller should take control of system bars for all 643 * windows. 644 */ remoteInsetsControllerControlsSystemBars(@ullable WindowState focusedWin)645 boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) { 646 if (focusedWin == null) { 647 return false; 648 } 649 if (!mRemoteInsetsControllerControlsSystemBars) { 650 return false; 651 } 652 if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) { 653 // No remote insets control target to take control of insets. 654 return false; 655 } 656 // If necessary, auto can control application windows when 657 // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases 658 // where we want to dictate system bar inset state for applications. 659 return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 660 && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 661 } 662 forceShowsStatusBarTransiently()663 private boolean forceShowsStatusBarTransiently() { 664 final WindowState win = mPolicy.getStatusBar(); 665 return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; 666 } 667 forceShowsNavigationBarTransiently()668 private boolean forceShowsNavigationBarTransiently() { 669 final WindowState win = mPolicy.getNotificationShade(); 670 return win != null 671 && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; 672 } 673 674 @VisibleForTesting startAnimation(boolean show, Runnable callback)675 void startAnimation(boolean show, Runnable callback) { 676 int typesReady = 0; 677 final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); 678 final IntArray showingTransientTypes = mShowingTransientTypes; 679 for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { 680 final @InternalInsetsType int type = showingTransientTypes.get(i); 681 WindowContainerInsetsSourceProvider provider = mStateController.getSourceProvider(type); 682 InsetsSourceControl control = provider.getControl(mDummyControlTarget); 683 if (control == null || control.getLeash() == null) { 684 continue; 685 } 686 typesReady |= InsetsState.toPublicType(type); 687 controls.put(control.getType(), new InsetsSourceControl(control)); 688 } 689 controlAnimationUnchecked(typesReady, controls, show, callback); 690 } 691 controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback)692 private void controlAnimationUnchecked(int typesReady, 693 SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) { 694 InsetsPolicyAnimationControlListener listener = 695 new InsetsPolicyAnimationControlListener(show, callback, typesReady); 696 listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); 697 } 698 dispatchTransientSystemBarsVisibilityChanged( @ullable WindowState focusedWindow, boolean areVisible, boolean wereRevealedFromSwipeOnSystemBar)699 private void dispatchTransientSystemBarsVisibilityChanged( 700 @Nullable WindowState focusedWindow, 701 boolean areVisible, 702 boolean wereRevealedFromSwipeOnSystemBar) { 703 if (focusedWindow == null) { 704 return; 705 } 706 707 Task task = focusedWindow.getTask(); 708 if (task == null) { 709 return; 710 } 711 712 int taskId = task.mTaskId; 713 boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID; 714 if (!isValidTaskId) { 715 return; 716 } 717 718 mDisplayContent.mWmService.mTaskSystemBarsListenerController 719 .dispatchTransientSystemBarVisibilityChanged( 720 taskId, 721 areVisible, 722 wereRevealedFromSwipeOnSystemBar); 723 } 724 725 private class BarWindow { 726 727 private final int mId; 728 private @StatusBarManager.WindowVisibleState int mState = 729 StatusBarManager.WINDOW_STATE_SHOWING; 730 BarWindow(int id)731 BarWindow(int id) { 732 mId = id; 733 } 734 updateVisibility(@ullable InsetsControlTarget controlTarget, @InternalInsetsType int type)735 private void updateVisibility(@Nullable InsetsControlTarget controlTarget, 736 @InternalInsetsType int type) { 737 setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type)); 738 } 739 setVisible(boolean visible)740 private void setVisible(boolean visible) { 741 final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN; 742 if (mState != state) { 743 mState = state; 744 StatusBarManagerInternal statusBarManagerInternal = 745 mPolicy.getStatusBarManagerInternal(); 746 if (statusBarManagerInternal != null) { 747 statusBarManagerInternal.setWindowState( 748 mDisplayContent.getDisplayId(), mId, state); 749 } 750 } 751 } 752 } 753 754 private class InsetsPolicyAnimationControlListener extends 755 InsetsController.InternalAnimationControlListener { 756 Runnable mFinishCallback; 757 InsetsPolicyAnimationControlCallbacks mControlCallbacks; 758 InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types)759 InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { 760 super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, 761 false /* disable */, 0 /* floatingImeBottomInsets */, null); 762 mFinishCallback = finishCallback; 763 mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); 764 } 765 766 @Override onAnimationFinish()767 protected void onAnimationFinish() { 768 super.onAnimationFinish(); 769 if (mFinishCallback != null) { 770 DisplayThread.getHandler().post(mFinishCallback); 771 } 772 } 773 774 private class InsetsPolicyAnimationControlCallbacks implements 775 InsetsAnimationControlCallbacks { 776 private InsetsAnimationControlImpl mAnimationControl = null; 777 private InsetsPolicyAnimationControlListener mListener; 778 InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener)779 InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { 780 mListener = listener; 781 } 782 controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show)783 private void controlAnimationUnchecked(int typesReady, 784 SparseArray<InsetsSourceControl> controls, boolean show) { 785 if (typesReady == 0) { 786 // nothing to animate. 787 return; 788 } 789 mAnimatingShown = show; 790 791 final InsetsState state = mFocusedWin.getInsetsState(); 792 793 // We are about to playing the default animation. Passing a null frame indicates 794 // the controlled types should be animated regardless of the frame. 795 mAnimationControl = new InsetsAnimationControlImpl(controls, 796 null /* frame */, state, mListener, typesReady, this, 797 mListener.getDurationMs(), getInsetsInterpolator(), 798 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show 799 ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN 800 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, 801 null /* translator */); 802 SurfaceAnimationThread.getHandler().post( 803 () -> mListener.onReady(mAnimationControl, typesReady)); 804 } 805 806 /** Called on SurfaceAnimationThread without global WM lock held. */ 807 @Override scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)808 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { 809 if (mAnimationControl.applyChangeInsets(null /* outState */)) { 810 mAnimationControl.finish(mAnimatingShown); 811 } 812 } 813 814 @Override notifyFinished(InsetsAnimationControlRunner runner, boolean shown)815 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { 816 // Nothing's needed here. Finish steps is handled in the listener 817 // onAnimationFinished callback. 818 } 819 820 /** Called on SurfaceAnimationThread without global WM lock held. */ 821 @Override applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params)822 public void applySurfaceParams( 823 final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { 824 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 825 for (int i = params.length - 1; i >= 0; i--) { 826 SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; 827 applyParams(t, surfaceParams, mTmpFloat9); 828 } 829 t.apply(); 830 t.close(); 831 } 832 833 // Since we don't push applySurfaceParams to a Handler-queue we don't need 834 // to push release in this case. 835 @Override releaseSurfaceControlFromRt(SurfaceControl sc)836 public void releaseSurfaceControlFromRt(SurfaceControl sc) { 837 sc.release(); 838 } 839 840 @Override 841 public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController> startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)842 void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, 843 WindowInsetsAnimation animation, 844 Bounds bounds) { 845 } 846 847 @Override reportPerceptible(int types, boolean perceptible)848 public void reportPerceptible(int types, boolean perceptible) { 849 // No-op for now - only client windows report perceptibility for now, with policy 850 // controllers assumed to always be perceptible. 851 } 852 } 853 } 854 } 855