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() { 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()); 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 (changed) { 225 notifyInsetsChanged(); 226 mDisplayContent.updateSystemGestureExclusion(); 227 mDisplayContent.updateKeepClearAreas(); 228 mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); 229 } 230 } 231 getFakeControllingTypes(InsetsControlTarget target)232 @InsetsType int getFakeControllingTypes(InsetsControlTarget target) { 233 @InsetsType int types = 0; 234 for (int i = mProviders.size() - 1; i >= 0; i--) { 235 final InsetsSourceProvider provider = mProviders.valueAt(i); 236 final InsetsControlTarget fakeControlTarget = provider.getFakeControlTarget(); 237 if (target == fakeControlTarget) { 238 types |= provider.getSource().getType(); 239 } 240 } 241 return types; 242 } 243 onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)244 void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) { 245 246 // Make sure that we always have a control target for the IME, even if the IME target is 247 // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible. 248 InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget; 249 onControlTargetChanged(getImeSourceProvider(), target, false /* fake */); 250 ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s", 251 target != null ? target.getWindow() : "null"); 252 notifyPendingInsetsControlChanged(); 253 } 254 255 /** 256 * Called when the focused window that is able to control the system bars changes. 257 * 258 * @param statusControlling The target that is now able to control the status bar appearance 259 * and visibility. 260 * @param navControlling The target that is now able to control the nav bar appearance 261 * and visibility. 262 */ onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)263 void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling, 264 @Nullable InsetsControlTarget fakeStatusControlling, 265 @Nullable InsetsControlTarget navControlling, 266 @Nullable InsetsControlTarget fakeNavControlling) { 267 for (int i = mProviders.size() - 1; i >= 0; i--) { 268 final InsetsSourceProvider provider = mProviders.valueAt(i); 269 final @InsetsType int type = provider.getSource().getType(); 270 if (type == WindowInsets.Type.statusBars()) { 271 onControlTargetChanged(provider, statusControlling, false /* fake */); 272 onControlTargetChanged(provider, fakeStatusControlling, true /* fake */); 273 } else if (type == WindowInsets.Type.navigationBars()) { 274 onControlTargetChanged(provider, navControlling, false /* fake */); 275 onControlTargetChanged(provider, fakeNavControlling, true /* fake */); 276 } 277 } 278 notifyPendingInsetsControlChanged(); 279 } 280 notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)281 void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, 282 InsetsSourceProvider provider) { 283 removeFromControlMaps(previousControlTarget, provider, false /* fake */); 284 } 285 onControlTargetChanged(InsetsSourceProvider provider, @Nullable InsetsControlTarget target, boolean fake)286 private void onControlTargetChanged(InsetsSourceProvider provider, 287 @Nullable InsetsControlTarget target, boolean fake) { 288 final InsetsControlTarget lastTarget = fake 289 ? mIdFakeControlTargetMap.get(provider.getSource().getId()) 290 : mIdControlTargetMap.get(provider.getSource().getId()); 291 if (target == lastTarget) { 292 return; 293 } 294 if (!provider.isControllable()) { 295 return; 296 } 297 if (fake) { 298 // The fake target updated here will be used to pretend to the app that it's still under 299 // control of the bars while it's not really, but we still need to find out the apps 300 // intentions around showing/hiding. For example, when the transient bars are showing, 301 // and the fake target requests to show system bars, the transient state will be 302 // aborted. 303 provider.updateFakeControlTarget(target); 304 } else { 305 provider.updateControlForTarget(target, false /* force */); 306 307 // Get control target again in case the provider didn't accept the one we passed to it. 308 target = provider.getControlTarget(); 309 if (target == lastTarget) { 310 return; 311 } 312 } 313 if (lastTarget != null) { 314 removeFromControlMaps(lastTarget, provider, fake); 315 mPendingControlChanged.add(lastTarget); 316 } 317 if (target != null) { 318 addToControlMaps(target, provider, fake); 319 mPendingControlChanged.add(target); 320 } 321 } 322 removeFromControlMaps(@onNull InsetsControlTarget target, InsetsSourceProvider provider, boolean fake)323 private void removeFromControlMaps(@NonNull InsetsControlTarget target, 324 InsetsSourceProvider provider, boolean fake) { 325 final ArrayList<InsetsSourceProvider> array = mControlTargetProvidersMap.get(target); 326 if (array == null) { 327 return; 328 } 329 array.remove(provider); 330 if (array.isEmpty()) { 331 mControlTargetProvidersMap.remove(target); 332 } 333 if (fake) { 334 mIdFakeControlTargetMap.remove(provider.getSource().getId()); 335 } else { 336 mIdControlTargetMap.remove(provider.getSource().getId()); 337 } 338 } 339 addToControlMaps(@onNull InsetsControlTarget target, InsetsSourceProvider provider, boolean fake)340 private void addToControlMaps(@NonNull InsetsControlTarget target, 341 InsetsSourceProvider provider, boolean fake) { 342 final ArrayList<InsetsSourceProvider> array = mControlTargetProvidersMap.computeIfAbsent( 343 target, key -> new ArrayList<>()); 344 array.add(provider); 345 if (fake) { 346 mIdFakeControlTargetMap.put(provider.getSource().getId(), target); 347 } else { 348 mIdControlTargetMap.put(provider.getSource().getId(), target); 349 } 350 } 351 notifyControlChanged(InsetsControlTarget target)352 void notifyControlChanged(InsetsControlTarget target) { 353 mPendingControlChanged.add(target); 354 notifyPendingInsetsControlChanged(); 355 } 356 notifyPendingInsetsControlChanged()357 private void notifyPendingInsetsControlChanged() { 358 if (mPendingControlChanged.isEmpty()) { 359 return; 360 } 361 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 362 for (int i = mProviders.size() - 1; i >= 0; i--) { 363 final InsetsSourceProvider provider = mProviders.valueAt(i); 364 provider.onSurfaceTransactionApplied(); 365 } 366 final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>(); 367 for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { 368 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); 369 controlTarget.notifyInsetsControlChanged(); 370 if (mControlTargetProvidersMap.containsKey(controlTarget)) { 371 // We only collect targets who get controls, not lose controls. 372 newControlTargets.add(controlTarget); 373 } 374 } 375 mPendingControlChanged.clear(); 376 377 // This updates the insets visibilities AFTER sending current insets state and controls 378 // to the clients, so that the clients can change the current visibilities to the 379 // requested visibilities with animations. 380 for (int i = newControlTargets.size() - 1; i >= 0; i--) { 381 onRequestedVisibleTypesChanged(newControlTargets.valueAt(i)); 382 } 383 newControlTargets.clear(); 384 }); 385 } 386 notifyInsetsChanged()387 void notifyInsetsChanged() { 388 mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); 389 } 390 dump(String prefix, PrintWriter pw)391 void dump(String prefix, PrintWriter pw) { 392 pw.println(prefix + "WindowInsetsStateController"); 393 prefix = prefix + " "; 394 mState.dump(prefix, pw); 395 pw.println(prefix + "Control map:"); 396 for (int i = mControlTargetProvidersMap.size() - 1; i >= 0; i--) { 397 final InsetsControlTarget controlTarget = mControlTargetProvidersMap.keyAt(i); 398 pw.print(prefix + " "); 399 pw.print(controlTarget); 400 pw.println(":"); 401 final ArrayList<InsetsSourceProvider> providers = mControlTargetProvidersMap.valueAt(i); 402 for (int j = providers.size() - 1; j >= 0; j--) { 403 final InsetsSourceProvider provider = providers.get(j); 404 if (provider != null) { 405 pw.print(prefix + " "); 406 if (controlTarget == provider.getFakeControlTarget()) { 407 pw.print("(fake) "); 408 } 409 pw.println(provider.getControl(controlTarget)); 410 } 411 } 412 } 413 if (mControlTargetProvidersMap.isEmpty()) { 414 pw.print(prefix + " none"); 415 } 416 pw.println(prefix + "InsetsSourceProviders:"); 417 for (int i = mProviders.size() - 1; i >= 0; i--) { 418 mProviders.valueAt(i).dump(pw, prefix + " "); 419 } 420 if (mForcedConsumingTypes != 0) { 421 pw.println(prefix + "mForcedConsumingTypes=" 422 + WindowInsets.Type.toString(mForcedConsumingTypes)); 423 } 424 } 425 dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel)426 void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) { 427 for (int i = mProviders.size() - 1; i >= 0; i--) { 428 final InsetsSourceProvider provider = mProviders.valueAt(i); 429 provider.dumpDebug(proto, 430 provider.getSource().getType() == ime() 431 ? IME_INSETS_SOURCE_PROVIDER 432 : INSETS_SOURCE_PROVIDERS, 433 logLevel); 434 } 435 } 436 } 437