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.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; 21 import static android.view.InsetsSource.ID_IME; 22 import static android.view.WindowInsets.Type.displayCutout; 23 import static android.view.WindowInsets.Type.ime; 24 import static android.view.WindowInsets.Type.mandatorySystemGestures; 25 import static android.view.WindowInsets.Type.systemGestures; 26 27 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_IME; 28 import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER; 29 import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS; 30 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.os.Trace; 34 import android.util.ArrayMap; 35 import android.util.ArraySet; 36 import android.util.SparseArray; 37 import android.util.SparseLongArray; 38 import android.util.proto.ProtoOutputStream; 39 import android.view.InsetsSource; 40 import android.view.InsetsSourceControl; 41 import android.view.InsetsState; 42 import android.view.WindowInsets; 43 import android.view.WindowInsets.Type.InsetsType; 44 import android.view.inputmethod.ImeTracker; 45 46 import com.android.internal.protolog.ProtoLog; 47 import com.android.server.inputmethod.InputMethodManagerInternal; 48 49 import java.io.PrintWriter; 50 import java.util.ArrayList; 51 import java.util.function.Consumer; 52 53 /** 54 * Manages global window inset state in the system represented by {@link InsetsState}. 55 */ 56 class InsetsStateController { 57 58 private final InsetsState mLastState = new InsetsState(); 59 private final InsetsState mState = new InsetsState(); 60 private final DisplayContent mDisplayContent; 61 62 private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>(); 63 private final SparseLongArray mSurfaceTransactionIds = new SparseLongArray(); 64 private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>> 65 mControlTargetProvidersMap = new ArrayMap<>(); 66 private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>> 67 mPendingTargetProvidersMap = new ArrayMap<>(); 68 private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>(); 69 private final SparseArray<InsetsControlTarget> mIdFakeControlTargetMap = new SparseArray<>(); 70 71 private final Consumer<WindowState> mDispatchInsetsChanged = w -> { 72 if (w.isReadyToDispatchInsetsState()) { 73 w.notifyInsetsChanged(); 74 } 75 }; 76 private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { 77 @Override 78 public void notifyInsetsControlChanged(int displayId) { 79 InsetsSourceControl[] controls = getControlsForDispatch(this); 80 if (controls == null) { 81 return; 82 } 83 for (InsetsSourceControl control : controls) { 84 if (control.getType() == WindowInsets.Type.ime()) { 85 mDisplayContent.mWmService.mH.post(() -> 86 InputMethodManagerInternal.get().removeImeSurface(displayId)); 87 } 88 } 89 } 90 }; 91 92 private @InsetsType int mForcedConsumingTypes; 93 InsetsStateController(DisplayContent displayContent)94 InsetsStateController(DisplayContent displayContent) { 95 mDisplayContent = displayContent; 96 } 97 getRawInsetsState()98 InsetsState getRawInsetsState() { 99 return mState; 100 } 101 getControlsForDispatch(InsetsControlTarget target)102 @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { 103 final ArrayList<InsetsSourceProvider> controlled = mControlTargetProvidersMap.get(target); 104 if (controlled == null) { 105 return null; 106 } 107 final int size = controlled.size(); 108 final InsetsSourceControl[] result = new InsetsSourceControl[size]; 109 for (int i = 0; i < size; i++) { 110 result[i] = controlled.get(i).getControl(target); 111 } 112 return result; 113 } 114 getSourceProviders()115 SparseArray<InsetsSourceProvider> getSourceProviders() { 116 return mProviders; 117 } 118 119 /** 120 * @return The provider of a specific source ID. 121 */ getOrCreateSourceProvider(int id, @InsetsType int type)122 InsetsSourceProvider getOrCreateSourceProvider(int id, @InsetsType int type) { 123 InsetsSourceProvider provider = mProviders.get(id); 124 if (provider != null) { 125 return provider; 126 } 127 final InsetsSource source = mState.getOrCreateSource(id, type); 128 provider = id == ID_IME 129 ? new ImeInsetsSourceProvider(source, this, mDisplayContent) 130 : new InsetsSourceProvider(source, this, mDisplayContent); 131 provider.setFlags( 132 (mForcedConsumingTypes & type) != 0 133 ? FLAG_FORCE_CONSUMING 134 : 0, 135 FLAG_FORCE_CONSUMING); 136 mProviders.put(id, provider); 137 return provider; 138 } 139 getImeSourceProvider()140 ImeInsetsSourceProvider getImeSourceProvider() { 141 return (ImeInsetsSourceProvider) getOrCreateSourceProvider(ID_IME, ime()); 142 } 143 removeSourceProvider(int id)144 void removeSourceProvider(int id) { 145 if (id != ID_IME) { 146 mState.removeSource(id); 147 mProviders.remove(id); 148 } 149 } 150 setForcedConsumingTypes(@nsetsType int types)151 void setForcedConsumingTypes(@InsetsType int types) { 152 if (mForcedConsumingTypes != types) { 153 mForcedConsumingTypes = types; 154 boolean changed = false; 155 for (int i = mProviders.size() - 1; i >= 0; i--) { 156 final InsetsSourceProvider provider = mProviders.valueAt(i); 157 changed |= provider.setFlags( 158 (types & provider.getSource().getType()) != 0 159 ? FLAG_FORCE_CONSUMING 160 : 0, 161 FLAG_FORCE_CONSUMING); 162 } 163 if (changed) { 164 notifyInsetsChanged(); 165 } 166 } 167 } 168 169 /** 170 * Called when a layout pass has occurred. 171 */ onPostLayout()172 void onPostLayout() { 173 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ISC.onPostLayout"); 174 for (int i = mProviders.size() - 1; i >= 0; i--) { 175 mProviders.valueAt(i).onPostLayout(); 176 } 177 if (!mLastState.equals(mState)) { 178 mLastState.set(mState, true /* copySources */); 179 notifyInsetsChanged(); 180 } 181 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 182 } 183 184 /** 185 * Updates {@link WindowState#mAboveInsetsState} for all windows in the display. 186 * 187 * @param notifyInsetsChange {@code true} if the clients should be notified about the change. 188 */ updateAboveInsetsState(boolean notifyInsetsChange)189 void updateAboveInsetsState(boolean notifyInsetsChange) { 190 final InsetsState aboveInsetsState = new InsetsState(); 191 aboveInsetsState.set(mState, 192 displayCutout() | systemGestures() | mandatorySystemGestures()); 193 final SparseArray<InsetsSource> localInsetsSourcesFromParent = new SparseArray<>(); 194 final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>(); 195 196 // This method will iterate on the entire hierarchy in top to bottom z-order manner. The 197 // aboveInsetsState will be modified as per the insets provided by the WindowState being 198 // visited. 199 mDisplayContent.updateAboveInsetsState(aboveInsetsState, localInsetsSourcesFromParent, 200 insetsChangedWindows); 201 202 if (notifyInsetsChange) { 203 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 204 mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i)); 205 } 206 } 207 } 208 onDisplayFramesUpdated(boolean notifyInsetsChange)209 void onDisplayFramesUpdated(boolean notifyInsetsChange) { 210 final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); 211 mDisplayContent.forAllWindows(w -> { 212 w.mAboveInsetsState.set(mState, displayCutout()); 213 insetsChangedWindows.add(w); 214 }, true /* traverseTopToBottom */); 215 if (notifyInsetsChange) { 216 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 217 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); 218 } 219 } 220 } 221 onRequestedVisibleTypesChanged(InsetsTarget caller, @InsetsType int changedTypes, @Nullable ImeTracker.Token statsToken)222 void onRequestedVisibleTypesChanged(InsetsTarget caller, @InsetsType int changedTypes, 223 @Nullable ImeTracker.Token statsToken) { 224 boolean changed = false; 225 for (int i = mProviders.size() - 1; i >= 0; i--) { 226 final InsetsSourceProvider provider = mProviders.valueAt(i); 227 final @InsetsType int type = provider.getSource().getType(); 228 final boolean isImeProvider = type == WindowInsets.Type.ime(); 229 if ((type & changedTypes) != 0) { 230 changed |= provider.updateClientVisibility( 231 caller, isImeProvider ? statsToken : null) 232 // Fake control target cannot change the client visibility, but it should 233 // change the insets with its newly requested visibility. 234 || (caller == provider.getFakeControlTarget()); 235 } else if (isImeProvider && android.view.inputmethod.Flags.refactorInsetsController()) { 236 ImeTracker.forLogging().onCancelled(statsToken, 237 ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); 238 } 239 } 240 if (changed) { 241 notifyInsetsChanged(); 242 mDisplayContent.updateSystemGestureExclusion(); 243 244 mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); 245 } 246 } 247 getFakeControllingTypes(InsetsTarget target)248 @InsetsType int getFakeControllingTypes(InsetsTarget target) { 249 @InsetsType int types = 0; 250 for (int i = mProviders.size() - 1; i >= 0; i--) { 251 final InsetsSourceProvider provider = mProviders.valueAt(i); 252 final InsetsControlTarget fakeControlTarget = provider.getFakeControlTarget(); 253 if (target == fakeControlTarget) { 254 types |= provider.getSource().getType(); 255 } 256 } 257 return types; 258 } 259 onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)260 void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) { 261 262 // Make sure that we always have a control target for the IME, even if the IME target is 263 // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible. 264 InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget; 265 onControlTargetChanged(getImeSourceProvider(), target, false /* fake */); 266 ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s", 267 target != null && target.getWindow() != null ? target.getWindow() : target); 268 notifyPendingInsetsControlChanged(); 269 } 270 271 /** 272 * Called when the focused window that is able to control the system bars changes. 273 * 274 * @param statusControlling The target that is now able to control the status bar appearance 275 * and visibility. 276 * @param navControlling The target that is now able to control the nav bar appearance 277 * and visibility. 278 */ onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)279 void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling, 280 @Nullable InsetsControlTarget fakeStatusControlling, 281 @Nullable InsetsControlTarget navControlling, 282 @Nullable InsetsControlTarget fakeNavControlling) { 283 for (int i = mProviders.size() - 1; i >= 0; i--) { 284 final InsetsSourceProvider provider = mProviders.valueAt(i); 285 final @InsetsType int type = provider.getSource().getType(); 286 if (type == WindowInsets.Type.statusBars()) { 287 onControlTargetChanged(provider, statusControlling, false /* fake */); 288 onControlTargetChanged(provider, fakeStatusControlling, true /* fake */); 289 } else if (type == WindowInsets.Type.navigationBars()) { 290 onControlTargetChanged(provider, navControlling, false /* fake */); 291 onControlTargetChanged(provider, fakeNavControlling, true /* fake */); 292 } 293 } 294 notifyPendingInsetsControlChanged(); 295 } 296 notifyControlTargetChanged(@ullable InsetsControlTarget target, InsetsSourceProvider provider)297 void notifyControlTargetChanged(@Nullable InsetsControlTarget target, 298 InsetsSourceProvider provider) { 299 onControlTargetChanged(provider, target, false /* fake */); 300 notifyPendingInsetsControlChanged(); 301 } 302 notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)303 void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, 304 InsetsSourceProvider provider) { 305 removeFromControlMaps(previousControlTarget, provider, false /* fake */); 306 } 307 onControlTargetChanged(InsetsSourceProvider provider, @Nullable InsetsControlTarget target, boolean fake)308 private void onControlTargetChanged(InsetsSourceProvider provider, 309 @Nullable InsetsControlTarget target, boolean fake) { 310 final InsetsControlTarget lastTarget = fake 311 ? mIdFakeControlTargetMap.get(provider.getSource().getId()) 312 : mIdControlTargetMap.get(provider.getSource().getId()); 313 if (target == lastTarget) { 314 return; 315 } 316 if (!provider.isControllable()) { 317 return; 318 } 319 if (fake) { 320 // The fake target updated here will be used to pretend to the app that it's still under 321 // control of the bars while it's not really, but we still need to find out the apps 322 // intentions around showing/hiding. For example, when the transient bars are showing, 323 // and the fake target requests to show system bars, the transient state will be 324 // aborted. 325 provider.updateFakeControlTarget(target); 326 } else { 327 // TODO(b/353463205) if the IME controlTarget changes, any pending requests should fail 328 provider.updateControlForTarget(target, false /* force */, 329 null /* TODO(b/353463205) check if needed here */); 330 331 // Get control target again in case the provider didn't accept the one we passed to it. 332 target = provider.getControlTarget(); 333 if (target == lastTarget) { 334 return; 335 } 336 } 337 if (lastTarget != null) { 338 removeFromControlMaps(lastTarget, provider, fake); 339 addToPendingControlMaps(lastTarget, provider); 340 } 341 if (target != null) { 342 addToControlMaps(target, provider, fake); 343 addToPendingControlMaps(target, provider); 344 } 345 } 346 removeFromControlMaps(@onNull InsetsControlTarget target, InsetsSourceProvider provider, boolean fake)347 private void removeFromControlMaps(@NonNull InsetsControlTarget target, 348 InsetsSourceProvider provider, boolean fake) { 349 final ArrayList<InsetsSourceProvider> array = mControlTargetProvidersMap.get(target); 350 if (array == null) { 351 return; 352 } 353 array.remove(provider); 354 if (array.isEmpty()) { 355 mControlTargetProvidersMap.remove(target); 356 } 357 if (fake) { 358 mIdFakeControlTargetMap.remove(provider.getSource().getId()); 359 } else { 360 mIdControlTargetMap.remove(provider.getSource().getId()); 361 } 362 } 363 addToControlMaps(@onNull InsetsControlTarget target, InsetsSourceProvider provider, boolean fake)364 private void addToControlMaps(@NonNull InsetsControlTarget target, 365 InsetsSourceProvider provider, boolean fake) { 366 final ArrayList<InsetsSourceProvider> array = mControlTargetProvidersMap.computeIfAbsent( 367 target, key -> new ArrayList<>()); 368 array.add(provider); 369 if (fake) { 370 mIdFakeControlTargetMap.put(provider.getSource().getId(), target); 371 } else { 372 mIdControlTargetMap.put(provider.getSource().getId(), target); 373 } 374 } 375 addToPendingControlMaps(@onNull InsetsControlTarget target, InsetsSourceProvider provider)376 private void addToPendingControlMaps(@NonNull InsetsControlTarget target, 377 InsetsSourceProvider provider) { 378 final ArrayList<InsetsSourceProvider> array = 379 mPendingTargetProvidersMap.computeIfAbsent(target, key -> new ArrayList<>()); 380 array.add(provider); 381 } 382 notifyControlChanged(@onNull InsetsControlTarget target, InsetsSourceProvider provider)383 void notifyControlChanged(@NonNull InsetsControlTarget target, InsetsSourceProvider provider) { 384 addToPendingControlMaps(target, provider); 385 notifyPendingInsetsControlChanged(); 386 } 387 notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready)388 void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) { 389 if (ready) { 390 mSurfaceTransactionIds.put(provider.getSource().getId(), id); 391 } else { 392 mSurfaceTransactionIds.delete(provider.getSource().getId()); 393 } 394 } 395 onAnimatingTypesChanged(InsetsControlTarget target, @Nullable ImeTracker.Token statsToken)396 void onAnimatingTypesChanged(InsetsControlTarget target, 397 @Nullable ImeTracker.Token statsToken) { 398 for (int i = mProviders.size() - 1; i >= 0; i--) { 399 final InsetsSourceProvider provider = mProviders.valueAt(i); 400 final boolean isImeProvider = provider.getSource().getType() == WindowInsets.Type.ime(); 401 provider.onAnimatingTypesChanged(target, isImeProvider ? statsToken : null); 402 } 403 } 404 notifyPendingInsetsControlChanged()405 private void notifyPendingInsetsControlChanged() { 406 if (mPendingTargetProvidersMap.isEmpty()) { 407 return; 408 } 409 final int size = mSurfaceTransactionIds.size(); 410 final SparseLongArray surfaceTransactionIds = new SparseLongArray(size); 411 for (int i = 0; i < size; i++) { 412 surfaceTransactionIds.append( 413 mSurfaceTransactionIds.keyAt(i), mSurfaceTransactionIds.valueAt(i)); 414 } 415 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 416 for (int i = 0; i < size; i++) { 417 final int sourceId = surfaceTransactionIds.keyAt(i); 418 final InsetsSourceProvider provider = mProviders.get(sourceId); 419 if (provider == null) { 420 continue; 421 } 422 provider.onSurfaceTransactionCommitted(surfaceTransactionIds.valueAt(i)); 423 } 424 final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>(); 425 int displayId = mDisplayContent.getDisplayId(); 426 final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>> pendingControlMap = 427 mPendingTargetProvidersMap; 428 for (int i = pendingControlMap.size() - 1; i >= 0; i--) { 429 final InsetsControlTarget target = pendingControlMap.keyAt(i); 430 final ArrayList<InsetsSourceProvider> providers = pendingControlMap.valueAt(i); 431 for (int p = providers.size() - 1; p >= 0; p--) { 432 final InsetsSourceProvider provider = providers.get(p); 433 if (provider.isLeashInitialized() || provider.getControlTarget() != target) { 434 // Stop waiting for this provider. 435 providers.remove(p); 436 } 437 } 438 if (providers.isEmpty()) { 439 pendingControlMap.removeAt(i); 440 441 // All controls of this target are ready to be dispatched. 442 target.notifyInsetsControlChanged(displayId); 443 if (mControlTargetProvidersMap.containsKey(target)) { 444 // We only collect targets who get controls, not lose controls. 445 newControlTargets.add(target); 446 } 447 } 448 } 449 450 // This updates the insets visibilities AFTER sending current insets state and controls 451 // to the clients, so that the clients can change the current visibilities to the 452 // requested visibilities with animations. 453 for (int i = newControlTargets.size() - 1; i >= 0; i--) { 454 // TODO(b/353463205) the statsToken shouldn't be null as it is used later in the 455 // IME provider. Check if we have to create a new request here 456 onRequestedVisibleTypesChanged(newControlTargets.valueAt(i), 457 WindowInsets.Type.all(), null /* statsToken */); 458 } 459 newControlTargets.clear(); 460 if (!android.view.inputmethod.Flags.refactorInsetsController()) { 461 // Check for and try to run the scheduled show IME request (if it exists), as we 462 // now applied the surface transaction and notified the target of the new control. 463 getImeSourceProvider().checkAndStartShowImePostLayout(); 464 } 465 }); 466 } 467 notifyInsetsChanged()468 void notifyInsetsChanged() { 469 mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); 470 } 471 notifyInsetsChanged(ArraySet<WindowState> changedWindows)472 void notifyInsetsChanged(ArraySet<WindowState> changedWindows) { 473 for (int i = changedWindows.size() - 1; i >= 0; i--) { 474 mDispatchInsetsChanged.accept(changedWindows.valueAt(i)); 475 } 476 } 477 478 /** 479 * Checks if the control target has pending controls. 480 * 481 * @param target the control target to check. 482 */ hasPendingControls(@onNull InsetsControlTarget target)483 boolean hasPendingControls(@NonNull InsetsControlTarget target) { 484 return mPendingTargetProvidersMap.containsKey(target); 485 } 486 dump(String prefix, PrintWriter pw)487 void dump(String prefix, PrintWriter pw) { 488 pw.println(prefix + "WindowInsetsStateController"); 489 prefix = prefix + " "; 490 mState.dump(prefix, pw); 491 pw.println(prefix + "Control map:"); 492 for (int i = mControlTargetProvidersMap.size() - 1; i >= 0; i--) { 493 final InsetsControlTarget controlTarget = mControlTargetProvidersMap.keyAt(i); 494 pw.print(prefix + " "); 495 pw.print(controlTarget); 496 pw.println(":"); 497 final ArrayList<InsetsSourceProvider> providers = mControlTargetProvidersMap.valueAt(i); 498 for (int j = providers.size() - 1; j >= 0; j--) { 499 final InsetsSourceProvider provider = providers.get(j); 500 if (provider != null) { 501 pw.print(prefix + " "); 502 if (controlTarget == provider.getFakeControlTarget()) { 503 pw.print("(fake) "); 504 } 505 pw.println(provider.getControl(controlTarget)); 506 } 507 } 508 } 509 if (mControlTargetProvidersMap.isEmpty()) { 510 pw.print(prefix + " none"); 511 } 512 pw.println(prefix + "InsetsSourceProviders:"); 513 for (int i = mProviders.size() - 1; i >= 0; i--) { 514 mProviders.valueAt(i).dump(pw, prefix + " "); 515 } 516 if (mForcedConsumingTypes != 0) { 517 pw.println(prefix + "mForcedConsumingTypes=" 518 + WindowInsets.Type.toString(mForcedConsumingTypes)); 519 } 520 } 521 dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel)522 void dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) { 523 for (int i = mProviders.size() - 1; i >= 0; i--) { 524 final InsetsSourceProvider provider = mProviders.valueAt(i); 525 provider.dumpDebug(proto, 526 provider.getSource().getType() == ime() 527 ? IME_INSETS_SOURCE_PROVIDER 528 : INSETS_SOURCE_PROVIDERS, 529 logLevel); 530 } 531 } 532 } 533