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