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.InsetsState.ITYPE_CLIMATE_BAR; 21 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 22 import static android.view.InsetsState.ITYPE_IME; 23 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 24 import static android.view.InsetsState.ITYPE_STATUS_BAR; 25 import static android.view.WindowInsets.Type.displayCutout; 26 import static android.view.WindowInsets.Type.mandatorySystemGestures; 27 import static android.view.WindowInsets.Type.systemGestures; 28 29 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME; 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.view.InsetsSource; 38 import android.view.InsetsSourceControl; 39 import android.view.InsetsState; 40 import android.view.InsetsState.InternalInsetsType; 41 42 import com.android.internal.protolog.common.ProtoLog; 43 import com.android.server.inputmethod.InputMethodManagerInternal; 44 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 import java.util.function.Consumer; 48 import java.util.function.Function; 49 50 /** 51 * Manages global window inset state in the system represented by {@link InsetsState}. 52 */ 53 class InsetsStateController { 54 55 private final InsetsState mLastState = new InsetsState(); 56 private final InsetsState mState = new InsetsState(); 57 private final DisplayContent mDisplayContent; 58 59 private final ArrayMap<Integer, WindowContainerInsetsSourceProvider> mProviders = 60 new ArrayMap<>(); 61 private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap = 62 new ArrayMap<>(); 63 private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>(); 64 65 /** @see #onControlFakeTargetChanged */ 66 private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>(); 67 68 private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); 69 70 private final Consumer<WindowState> mDispatchInsetsChanged = w -> { 71 if (w.isReadyToDispatchInsetsState()) { 72 w.notifyInsetsChanged(); 73 } 74 }; 75 private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { 76 @Override 77 public void notifyInsetsControlChanged() { 78 InsetsSourceControl[] controls = getControlsForDispatch(this); 79 if (controls == null) { 80 return; 81 } 82 for (InsetsSourceControl control : controls) { 83 if (control.getType() == ITYPE_IME) { 84 mDisplayContent.mWmService.mH.post(() -> 85 InputMethodManagerInternal.get().removeImeSurface()); 86 } 87 } 88 } 89 }; 90 91 private final Function<Integer, WindowContainerInsetsSourceProvider> mSourceProviderFunc; 92 InsetsStateController(DisplayContent displayContent)93 InsetsStateController(DisplayContent displayContent) { 94 mDisplayContent = displayContent; 95 mSourceProviderFunc = type -> { 96 final InsetsSource source = mState.getSource(type); 97 if (type == ITYPE_IME) { 98 return new ImeInsetsSourceProvider(source, this, mDisplayContent); 99 } 100 return new WindowContainerInsetsSourceProvider(source, this, mDisplayContent); 101 }; 102 } 103 getRawInsetsState()104 InsetsState getRawInsetsState() { 105 return mState; 106 } 107 getControlsForDispatch(InsetsControlTarget target)108 @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { 109 ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); 110 if (controlled == null) { 111 return null; 112 } 113 final int size = controlled.size(); 114 final InsetsSourceControl[] result = new InsetsSourceControl[size]; 115 for (int i = 0; i < size; i++) { 116 result[i] = mProviders.get(controlled.get(i)).getControl(target); 117 } 118 return result; 119 } 120 getSourceProviders()121 ArrayMap<Integer, WindowContainerInsetsSourceProvider> getSourceProviders() { 122 return mProviders; 123 } 124 125 /** 126 * @return The provider of a specific type. 127 */ getSourceProvider(@nternalInsetsType int type)128 WindowContainerInsetsSourceProvider getSourceProvider(@InternalInsetsType int type) { 129 return mProviders.computeIfAbsent(type, mSourceProviderFunc); 130 } 131 getImeSourceProvider()132 ImeInsetsSourceProvider getImeSourceProvider() { 133 return (ImeInsetsSourceProvider) getSourceProvider(ITYPE_IME); 134 } 135 136 /** 137 * @return The provider of a specific type or null if we don't have it. 138 */ 139 @Nullable peekSourceProvider(@nternalInsetsType int type)140 WindowContainerInsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) { 141 return mProviders.get(type); 142 } 143 144 /** 145 * Called when a layout pass has occurred. 146 */ onPostLayout()147 void onPostLayout() { 148 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ISC.onPostLayout"); 149 for (int i = mProviders.size() - 1; i >= 0; i--) { 150 mProviders.valueAt(i).onPostLayout(); 151 } 152 if (!mLastState.equals(mState)) { 153 mLastState.set(mState, true /* copySources */); 154 notifyInsetsChanged(); 155 } 156 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 157 } 158 159 /** 160 * Updates {@link WindowState#mAboveInsetsState} for all windows in the display. 161 * 162 * @param notifyInsetsChange {@code true} if the clients should be notified about the change. 163 */ updateAboveInsetsState(boolean notifyInsetsChange)164 void updateAboveInsetsState(boolean notifyInsetsChange) { 165 final InsetsState aboveInsetsState = new InsetsState(); 166 aboveInsetsState.set(mState, 167 displayCutout() | systemGestures() | mandatorySystemGestures()); 168 final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>(); 169 final SparseArray<InsetsSourceProvider> 170 localInsetsSourceProvidersFromParent = new SparseArray<>(); 171 // This method will iterate on the entire hierarchy in top to bottom z-order manner. The 172 // aboveInsetsState will be modified as per the insets provided by the WindowState being 173 // visited. 174 mDisplayContent.updateAboveInsetsState(aboveInsetsState, 175 localInsetsSourceProvidersFromParent, insetsChangedWindows); 176 if (notifyInsetsChange) { 177 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 178 mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i)); 179 } 180 } 181 } 182 onDisplayFramesUpdated(boolean notifyInsetsChange)183 void onDisplayFramesUpdated(boolean notifyInsetsChange) { 184 final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); 185 mDisplayContent.forAllWindows(w -> { 186 w.mAboveInsetsState.set(mState, displayCutout()); 187 insetsChangedWindows.add(w); 188 }, true /* traverseTopToBottom */); 189 if (notifyInsetsChange) { 190 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 191 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); 192 } 193 } 194 } 195 onInsetsModified(InsetsControlTarget caller)196 void onInsetsModified(InsetsControlTarget caller) { 197 boolean changed = false; 198 for (int i = mProviders.size() - 1; i >= 0; i--) { 199 changed |= mProviders.valueAt(i).updateClientVisibility(caller); 200 } 201 if (changed) { 202 notifyInsetsChanged(); 203 mDisplayContent.updateSystemGestureExclusion(); 204 mDisplayContent.updateKeepClearAreas(); 205 mDisplayContent.getDisplayPolicy().updateSystemBarAttributes(); 206 } 207 } 208 isFakeTarget(@nternalInsetsType int type, InsetsControlTarget target)209 boolean isFakeTarget(@InternalInsetsType int type, InsetsControlTarget target) { 210 return mTypeFakeControlTargetMap.get(type) == target; 211 } 212 onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)213 void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) { 214 215 // Make sure that we always have a control target for the IME, even if the IME target is 216 // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible. 217 InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget; 218 onControlChanged(ITYPE_IME, target); 219 ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s", 220 target != null ? target.getWindow() : "null"); 221 notifyPendingInsetsControlChanged(); 222 } 223 224 /** 225 * Called when the focused window that is able to control the system bars changes. 226 * 227 * @param statusControlling The target that is now able to control the status bar appearance 228 * and visibility. 229 * @param navControlling The target that is now able to control the nav bar appearance 230 * and visibility. 231 */ onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)232 void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling, 233 @Nullable InsetsControlTarget fakeStatusControlling, 234 @Nullable InsetsControlTarget navControlling, 235 @Nullable InsetsControlTarget fakeNavControlling) { 236 onControlChanged(ITYPE_STATUS_BAR, statusControlling); 237 onControlChanged(ITYPE_NAVIGATION_BAR, navControlling); 238 onControlChanged(ITYPE_CLIMATE_BAR, statusControlling); 239 onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling); 240 onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling); 241 onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling); 242 onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling); 243 onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling); 244 notifyPendingInsetsControlChanged(); 245 } 246 notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)247 void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, 248 InsetsSourceProvider provider) { 249 removeFromControlMaps(previousControlTarget, provider.getSource().getType(), 250 false /* fake */); 251 } 252 onControlChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget target)253 private void onControlChanged(@InternalInsetsType int type, 254 @Nullable InsetsControlTarget target) { 255 final InsetsControlTarget previous = mTypeControlTargetMap.get(type); 256 if (target == previous) { 257 return; 258 } 259 final WindowContainerInsetsSourceProvider provider = mProviders.get(type); 260 if (provider == null) { 261 return; 262 } 263 if (!provider.isControllable()) { 264 return; 265 } 266 provider.updateControlForTarget(target, false /* force */); 267 target = provider.getControlTarget(); 268 if (previous != null) { 269 removeFromControlMaps(previous, type, false /* fake */); 270 mPendingControlChanged.add(previous); 271 } 272 if (target != null) { 273 addToControlMaps(target, type, false /* fake */); 274 mPendingControlChanged.add(target); 275 } 276 } 277 278 /** 279 * The fake target saved here will be used to pretend to the app that it's still under control 280 * of the bars while it's not really, but we still need to find out the apps intentions around 281 * showing/hiding. For example, when the transient bars are showing, and the fake target 282 * requests to show system bars, the transient state will be aborted. 283 */ onControlFakeTargetChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget fakeTarget)284 void onControlFakeTargetChanged(@InternalInsetsType int type, 285 @Nullable InsetsControlTarget fakeTarget) { 286 final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type); 287 if (fakeTarget == previous) { 288 return; 289 } 290 final WindowContainerInsetsSourceProvider provider = mProviders.get(type); 291 if (provider == null) { 292 return; 293 } 294 provider.updateControlForFakeTarget(fakeTarget); 295 if (previous != null) { 296 removeFromControlMaps(previous, type, true /* fake */); 297 mPendingControlChanged.add(previous); 298 } 299 if (fakeTarget != null) { 300 addToControlMaps(fakeTarget, type, true /* fake */); 301 mPendingControlChanged.add(fakeTarget); 302 } 303 } 304 removeFromControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)305 private void removeFromControlMaps(@NonNull InsetsControlTarget target, 306 @InternalInsetsType int type, boolean fake) { 307 final ArrayList<Integer> array = mControlTargetTypeMap.get(target); 308 if (array == null) { 309 return; 310 } 311 array.remove((Integer) type); 312 if (array.isEmpty()) { 313 mControlTargetTypeMap.remove(target); 314 } 315 if (fake) { 316 mTypeFakeControlTargetMap.remove(type); 317 } else { 318 mTypeControlTargetMap.remove(type); 319 } 320 } 321 addToControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)322 private void addToControlMaps(@NonNull InsetsControlTarget target, 323 @InternalInsetsType int type, boolean fake) { 324 final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target, 325 key -> new ArrayList<>()); 326 array.add(type); 327 if (fake) { 328 mTypeFakeControlTargetMap.put(type, target); 329 } else { 330 mTypeControlTargetMap.put(type, target); 331 } 332 } 333 notifyControlChanged(InsetsControlTarget target)334 void notifyControlChanged(InsetsControlTarget target) { 335 mPendingControlChanged.add(target); 336 notifyPendingInsetsControlChanged(); 337 } 338 notifyPendingInsetsControlChanged()339 private void notifyPendingInsetsControlChanged() { 340 if (mPendingControlChanged.isEmpty()) { 341 return; 342 } 343 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 344 for (int i = mProviders.size() - 1; i >= 0; i--) { 345 final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i); 346 provider.onSurfaceTransactionApplied(); 347 } 348 final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>(); 349 for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { 350 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); 351 controlTarget.notifyInsetsControlChanged(); 352 if (mControlTargetTypeMap.containsKey(controlTarget)) { 353 // We only collect targets who get controls, not lose controls. 354 newControlTargets.add(controlTarget); 355 } 356 } 357 mPendingControlChanged.clear(); 358 359 // This updates the insets visibilities AFTER sending current insets state and controls 360 // to the clients, so that the clients can change the current visibilities to the 361 // requested visibilities with animations. 362 for (int i = newControlTargets.size() - 1; i >= 0; i--) { 363 onInsetsModified(newControlTargets.valueAt(i)); 364 } 365 newControlTargets.clear(); 366 }); 367 } 368 notifyInsetsChanged()369 void notifyInsetsChanged() { 370 mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); 371 } 372 dump(String prefix, PrintWriter pw)373 void dump(String prefix, PrintWriter pw) { 374 pw.println(prefix + "WindowInsetsStateController"); 375 prefix = prefix + " "; 376 mState.dump(prefix, pw); 377 pw.println(prefix + "Control map:"); 378 for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) { 379 pw.print(prefix + " "); 380 pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> " 381 + mTypeControlTargetMap.valueAt(i)); 382 } 383 pw.println(prefix + "InsetsSourceProviders:"); 384 for (int i = mProviders.size() - 1; i >= 0; i--) { 385 mProviders.valueAt(i).dump(pw, prefix + " "); 386 } 387 } 388 } 389