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.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 21 import static android.view.InsetsState.ITYPE_CAPTION_BAR; 22 import static android.view.InsetsState.ITYPE_IME; 23 import static android.view.InsetsState.ITYPE_INVALID; 24 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 25 import static android.view.InsetsState.ITYPE_STATUS_BAR; 26 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; 27 import static android.view.ViewRootImpl.sNewInsetsMode; 28 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 29 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 30 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 31 32 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.app.WindowConfiguration; 37 import android.app.WindowConfiguration.WindowingMode; 38 import android.util.ArrayMap; 39 import android.util.ArraySet; 40 import android.util.SparseArray; 41 import android.view.InsetsSource; 42 import android.view.InsetsSourceControl; 43 import android.view.InsetsState; 44 import android.view.InsetsState.InternalInsetsType; 45 import android.view.WindowManager; 46 47 import com.android.server.inputmethod.InputMethodManagerInternal; 48 import com.android.server.protolog.common.ProtoLog; 49 50 import java.io.PrintWriter; 51 import java.util.ArrayList; 52 import java.util.function.Consumer; 53 54 /** 55 * Manages global window inset state in the system represented by {@link InsetsState}. 56 */ 57 class InsetsStateController { 58 59 private final InsetsState mLastState = new InsetsState(); 60 private final InsetsState mState = new InsetsState(); 61 private final DisplayContent mDisplayContent; 62 63 private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>(); 64 private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap = 65 new ArrayMap<>(); 66 private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>(); 67 68 /** @see #onControlFakeTargetChanged */ 69 private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>(); 70 71 private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); 72 73 private final Consumer<WindowState> mDispatchInsetsChanged = w -> { 74 if (w.isVisible()) { 75 w.notifyInsetsChanged(); 76 } 77 }; 78 private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { 79 @Override 80 public void notifyInsetsControlChanged() { 81 InsetsSourceControl[] controls = getControlsForDispatch(this); 82 if (controls == null) { 83 return; 84 } 85 for (InsetsSourceControl control : controls) { 86 if (control.getType() == ITYPE_IME) { 87 mDisplayContent.mWmService.mH.post(() -> 88 InputMethodManagerInternal.get().removeImeSurface()); 89 } 90 } 91 } 92 }; 93 InsetsStateController(DisplayContent displayContent)94 InsetsStateController(DisplayContent displayContent) { 95 mDisplayContent = displayContent; 96 } 97 98 /** 99 * When dispatching window state to the client, we'll need to exclude the source that represents 100 * the window that is being dispatched. We also need to exclude certain types of insets source 101 * for client within specific windowing modes. 102 * 103 * @param target The client we dispatch the state to. 104 * @return The state stripped of the necessary information. 105 */ getInsetsForDispatch(@onNull WindowState target)106 InsetsState getInsetsForDispatch(@NonNull WindowState target) { 107 final InsetsSourceProvider provider = target.getControllableInsetProvider(); 108 final @InternalInsetsType int type = provider != null 109 ? provider.getSource().getType() : ITYPE_INVALID; 110 return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(), 111 isAboveIme(target)); 112 } 113 getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)114 InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { 115 final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type); 116 final WindowToken token = mDisplayContent.getWindowToken(attrs.token); 117 final @WindowingMode int windowingMode = token != null 118 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; 119 final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); 120 return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token)); 121 } 122 isAboveIme(WindowContainer target)123 private boolean isAboveIme(WindowContainer target) { 124 final WindowState imeWindow = mDisplayContent.mInputMethodWindow; 125 if (target == null || imeWindow == null) { 126 return false; 127 } 128 if (target instanceof WindowState) { 129 final WindowState win = (WindowState) target; 130 return win.needsRelativeLayeringToIme() || !win.mBehindIme; 131 } 132 return false; 133 } 134 getInsetsTypeForWindowType(int type)135 private static @InternalInsetsType int getInsetsTypeForWindowType(int type) { 136 switch (type) { 137 case TYPE_STATUS_BAR: 138 return ITYPE_STATUS_BAR; 139 case TYPE_NAVIGATION_BAR: 140 return ITYPE_NAVIGATION_BAR; 141 case TYPE_INPUT_METHOD: 142 return ITYPE_IME; 143 default: 144 return ITYPE_INVALID; 145 } 146 } 147 148 /** @see #getInsetsForDispatch */ getInsetsForDispatchInner(@nternalInsetsType int type, @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme)149 private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type, 150 @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { 151 InsetsState state = mState; 152 153 if (type != ITYPE_INVALID) { 154 state = new InsetsState(state); 155 state.removeSource(type); 156 157 // Navigation bar doesn't get influenced by anything else 158 if (type == ITYPE_NAVIGATION_BAR) { 159 state.removeSource(ITYPE_IME); 160 state.removeSource(ITYPE_STATUS_BAR); 161 state.removeSource(ITYPE_CAPTION_BAR); 162 } 163 164 // Status bar doesn't get influenced by caption bar 165 if (type == ITYPE_STATUS_BAR) { 166 state.removeSource(ITYPE_CAPTION_BAR); 167 } 168 169 // IME needs different frames for certain cases (e.g. navigation bar in gesture nav). 170 if (type == ITYPE_IME) { 171 for (int i = mProviders.size() - 1; i >= 0; i--) { 172 InsetsSourceProvider otherProvider = mProviders.valueAt(i); 173 if (otherProvider.overridesImeFrame()) { 174 InsetsSource override = 175 new InsetsSource( 176 state.getSource(otherProvider.getSource().getType())); 177 override.setFrame(otherProvider.getImeOverrideFrame()); 178 state.addSource(override); 179 } 180 } 181 } 182 } 183 184 if (WindowConfiguration.isFloating(windowingMode) 185 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { 186 state = new InsetsState(state); 187 state.removeSource(ITYPE_STATUS_BAR); 188 state.removeSource(ITYPE_NAVIGATION_BAR); 189 } 190 191 if (aboveIme) { 192 InsetsSource imeSource = state.peekSource(ITYPE_IME); 193 if (imeSource != null && imeSource.isVisible()) { 194 imeSource = new InsetsSource(imeSource); 195 imeSource.setVisible(false); 196 imeSource.setFrame(0, 0, 0, 0); 197 state = new InsetsState(state); 198 state.addSource(imeSource); 199 } 200 } 201 202 return state; 203 } 204 getRawInsetsState()205 InsetsState getRawInsetsState() { 206 return mState; 207 } 208 getControlsForDispatch(InsetsControlTarget target)209 @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { 210 ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); 211 if (controlled == null) { 212 return null; 213 } 214 final int size = controlled.size(); 215 final InsetsSourceControl[] result = new InsetsSourceControl[size]; 216 for (int i = 0; i < size; i++) { 217 result[i] = mProviders.get(controlled.get(i)).getControl(target); 218 } 219 return result; 220 } 221 222 /** 223 * @return The provider of a specific type. 224 */ getSourceProvider(@nternalInsetsType int type)225 InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) { 226 if (type == ITYPE_IME) { 227 return mProviders.computeIfAbsent(type, 228 key -> new ImeInsetsSourceProvider( 229 mState.getSource(key), this, mDisplayContent)); 230 } else { 231 return mProviders.computeIfAbsent(type, 232 key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); 233 } 234 } 235 getImeSourceProvider()236 ImeInsetsSourceProvider getImeSourceProvider() { 237 return (ImeInsetsSourceProvider) getSourceProvider(ITYPE_IME); 238 } 239 240 /** 241 * @return The provider of a specific type or null if we don't have it. 242 */ peekSourceProvider(@nternalInsetsType int type)243 @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) { 244 return mProviders.get(type); 245 } 246 247 /** 248 * Called when a layout pass has occurred. 249 */ onPostLayout()250 void onPostLayout() { 251 mState.setDisplayFrame(mDisplayContent.getBounds()); 252 for (int i = mProviders.size() - 1; i >= 0; i--) { 253 mProviders.valueAt(i).onPostLayout(); 254 } 255 final ArrayList<WindowState> winInsetsChanged = mDisplayContent.mWinInsetsChanged; 256 if (!mLastState.equals(mState)) { 257 mLastState.set(mState, true /* copySources */); 258 notifyInsetsChanged(); 259 } else { 260 // The global insets state has not changed but there might be windows whose conditions 261 // (e.g., z-order) have changed. They can affect the insets states that we dispatch to 262 // the clients. 263 for (int i = winInsetsChanged.size() - 1; i >= 0; i--) { 264 mDispatchInsetsChanged.accept(winInsetsChanged.get(i)); 265 } 266 } 267 winInsetsChanged.clear(); 268 } 269 onInsetsModified(InsetsControlTarget windowState, InsetsState state)270 void onInsetsModified(InsetsControlTarget windowState, InsetsState state) { 271 boolean changed = false; 272 for (int i = 0; i < InsetsState.SIZE; i++) { 273 final InsetsSource source = state.peekSource(i); 274 if (source == null) continue; 275 final InsetsSourceProvider provider = mProviders.get(source.getType()); 276 if (provider == null) { 277 continue; 278 } 279 changed |= provider.onInsetsModified(windowState, source); 280 } 281 if (changed) { 282 notifyInsetsChanged(); 283 mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw(); 284 } 285 } 286 287 /** 288 * Computes insets state of the insets provider window in the display frames. 289 * 290 * @param state The output state. 291 * @param win The owner window of insets provider. 292 * @param displayFrames The display frames to create insets source. 293 * @param windowFrames The specified frames to represent the owner window. 294 */ computeSimulatedState(InsetsState state, WindowState win, DisplayFrames displayFrames, WindowFrames windowFrames)295 void computeSimulatedState(InsetsState state, WindowState win, DisplayFrames displayFrames, 296 WindowFrames windowFrames) { 297 for (int i = mProviders.size() - 1; i >= 0; i--) { 298 final InsetsSourceProvider provider = mProviders.valueAt(i); 299 if (provider.mWin == win) { 300 state.addSource(provider.createSimulatedSource(displayFrames, windowFrames)); 301 } 302 } 303 } 304 isFakeTarget(@nternalInsetsType int type, InsetsControlTarget target)305 boolean isFakeTarget(@InternalInsetsType int type, InsetsControlTarget target) { 306 return mTypeFakeControlTargetMap.get(type) == target; 307 } 308 onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)309 void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) { 310 311 // Make sure that we always have a control target for the IME, even if the IME target is 312 // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible. 313 InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget; 314 onControlChanged(ITYPE_IME, target); 315 ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s", 316 target != null ? target.getWindow() : "null"); 317 notifyPendingInsetsControlChanged(); 318 } 319 320 /** 321 * Called when the focused window that is able to control the system bars changes. 322 * 323 * @param statusControlling The target that is now able to control the status bar appearance 324 * and visibility. 325 * @param navControlling The target that is now able to control the nav bar appearance 326 * and visibility. 327 */ onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)328 void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling, 329 @Nullable InsetsControlTarget fakeStatusControlling, 330 @Nullable InsetsControlTarget navControlling, 331 @Nullable InsetsControlTarget fakeNavControlling) { 332 onControlChanged(ITYPE_STATUS_BAR, statusControlling); 333 onControlChanged(ITYPE_NAVIGATION_BAR, navControlling); 334 onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling); 335 onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling); 336 notifyPendingInsetsControlChanged(); 337 } 338 notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)339 void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, 340 InsetsSourceProvider provider) { 341 removeFromControlMaps(previousControlTarget, provider.getSource().getType(), 342 false /* fake */); 343 } 344 onControlChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget target)345 private void onControlChanged(@InternalInsetsType int type, 346 @Nullable InsetsControlTarget target) { 347 final InsetsControlTarget previous = mTypeControlTargetMap.get(type); 348 if (target == previous) { 349 return; 350 } 351 final InsetsSourceProvider provider = mProviders.get(type); 352 if (provider == null) { 353 return; 354 } 355 if (!provider.isControllable()) { 356 return; 357 } 358 provider.updateControlForTarget(target, false /* force */); 359 target = provider.getControlTarget(); 360 if (previous != null) { 361 removeFromControlMaps(previous, type, false /* fake */); 362 mPendingControlChanged.add(previous); 363 } 364 if (target != null) { 365 addToControlMaps(target, type, false /* fake */); 366 mPendingControlChanged.add(target); 367 } 368 } 369 370 /** 371 * The fake target saved here will be used to pretend to the app that it's still under control 372 * of the bars while it's not really, but we still need to find out the apps intentions around 373 * showing/hiding. For example, when the transient bars are showing, and the fake target 374 * requests to show system bars, the transient state will be aborted. 375 */ onControlFakeTargetChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget fakeTarget)376 void onControlFakeTargetChanged(@InternalInsetsType int type, 377 @Nullable InsetsControlTarget fakeTarget) { 378 if (sNewInsetsMode != NEW_INSETS_MODE_FULL) { 379 return; 380 } 381 final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type); 382 if (fakeTarget == previous) { 383 return; 384 } 385 final InsetsSourceProvider provider = mProviders.get(type); 386 if (provider == null) { 387 return; 388 } 389 provider.updateControlForFakeTarget(fakeTarget); 390 if (previous != null) { 391 removeFromControlMaps(previous, type, true /* fake */); 392 mPendingControlChanged.add(previous); 393 } 394 if (fakeTarget != null) { 395 addToControlMaps(fakeTarget, type, true /* fake */); 396 mPendingControlChanged.add(fakeTarget); 397 } 398 } 399 removeFromControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)400 private void removeFromControlMaps(@NonNull InsetsControlTarget target, 401 @InternalInsetsType int type, boolean fake) { 402 final ArrayList<Integer> array = mControlTargetTypeMap.get(target); 403 if (array == null) { 404 return; 405 } 406 array.remove((Integer) type); 407 if (array.isEmpty()) { 408 mControlTargetTypeMap.remove(target); 409 } 410 if (fake) { 411 mTypeFakeControlTargetMap.remove(type); 412 } else { 413 mTypeControlTargetMap.remove(type); 414 } 415 } 416 addToControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)417 private void addToControlMaps(@NonNull InsetsControlTarget target, 418 @InternalInsetsType int type, boolean fake) { 419 final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target, 420 key -> new ArrayList<>()); 421 array.add(type); 422 if (fake) { 423 mTypeFakeControlTargetMap.put(type, target); 424 } else { 425 mTypeControlTargetMap.put(type, target); 426 } 427 } 428 notifyControlChanged(InsetsControlTarget target)429 void notifyControlChanged(InsetsControlTarget target) { 430 mPendingControlChanged.add(target); 431 notifyPendingInsetsControlChanged(); 432 } 433 notifyPendingInsetsControlChanged()434 private void notifyPendingInsetsControlChanged() { 435 if (mPendingControlChanged.isEmpty()) { 436 return; 437 } 438 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 439 for (int i = mProviders.size() - 1; i >= 0; i--) { 440 final InsetsSourceProvider provider = mProviders.valueAt(i); 441 provider.onSurfaceTransactionApplied(); 442 } 443 for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { 444 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); 445 controlTarget.notifyInsetsControlChanged(); 446 } 447 mPendingControlChanged.clear(); 448 }); 449 } 450 notifyInsetsChanged()451 void notifyInsetsChanged() { 452 mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); 453 if (mDisplayContent.mRemoteInsetsControlTarget != null) { 454 mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged(); 455 } 456 } 457 dump(String prefix, PrintWriter pw)458 void dump(String prefix, PrintWriter pw) { 459 pw.println(prefix + "WindowInsetsStateController"); 460 mState.dump(prefix + " ", pw); 461 pw.println(prefix + " " + "Control map:"); 462 for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) { 463 pw.print(prefix + " "); 464 pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> " 465 + mTypeControlTargetMap.valueAt(i)); 466 } 467 pw.println(prefix + " " + "InsetsSourceProviders map:"); 468 for (int i = mProviders.size() - 1; i >= 0; i--) { 469 pw.print(prefix + " "); 470 pw.println(InsetsState.typeToString(mProviders.keyAt(i)) + " -> "); 471 mProviders.valueAt(i).dump(pw, prefix); 472 } 473 } 474 } 475