• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_PINNED;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
22 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
23 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
24 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
25 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
26 import static android.view.InsetsState.ITYPE_IME;
27 import static android.view.InsetsState.ITYPE_INVALID;
28 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
29 import static android.view.InsetsState.ITYPE_STATUS_BAR;
30 import static android.view.WindowInsets.Type.displayCutout;
31 import static android.view.WindowInsets.Type.mandatorySystemGestures;
32 import static android.view.WindowInsets.Type.systemGestures;
33 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
34 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
35 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
36 
37 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
38 
39 import android.annotation.NonNull;
40 import android.annotation.Nullable;
41 import android.app.WindowConfiguration;
42 import android.app.WindowConfiguration.WindowingMode;
43 import android.os.Trace;
44 import android.util.ArrayMap;
45 import android.util.ArraySet;
46 import android.util.SparseArray;
47 import android.view.InsetsSource;
48 import android.view.InsetsSourceControl;
49 import android.view.InsetsState;
50 import android.view.InsetsState.InternalInsetsType;
51 import android.view.WindowManager;
52 import android.view.WindowManager.LayoutParams.WindowType;
53 
54 import com.android.internal.protolog.common.ProtoLog;
55 import com.android.server.inputmethod.InputMethodManagerInternal;
56 
57 import java.io.PrintWriter;
58 import java.util.ArrayList;
59 import java.util.function.Consumer;
60 
61 /**
62  * Manages global window inset state in the system represented by {@link InsetsState}.
63  */
64 class InsetsStateController {
65 
66     private final InsetsState mLastState = new InsetsState();
67     private final InsetsState mState = new InsetsState();
68     private final DisplayContent mDisplayContent;
69 
70     private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
71     private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
72             new ArrayMap<>();
73     private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
74 
75     /** @see #onControlFakeTargetChanged */
76     private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>();
77 
78     private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
79 
80     private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
81         if (w.isReadyToDispatchInsetsState()) {
82             w.notifyInsetsChanged();
83         }
84     };
85     private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() {
86         @Override
87         public void notifyInsetsControlChanged() {
88             InsetsSourceControl[] controls = getControlsForDispatch(this);
89             if (controls == null) {
90                 return;
91             }
92             for (InsetsSourceControl control : controls) {
93                 if (control.getType() == ITYPE_IME) {
94                     mDisplayContent.mWmService.mH.post(() ->
95                             InputMethodManagerInternal.get().removeImeSurface());
96                 }
97             }
98         }
99     };
100 
InsetsStateController(DisplayContent displayContent)101     InsetsStateController(DisplayContent displayContent) {
102         mDisplayContent = displayContent;
103     }
104 
105     /**
106      * Gets the insets state from the perspective of the target. When performing layout of the
107      * target or dispatching insets to the target, we need to exclude sources which should not be
108      * visible to the target. e.g., the source which represents the target window itself, and the
109      * IME source when the target is above IME. We also need to exclude certain types of insets
110      * source for client within specific windowing modes.
111      * This is to get the insets for a window layout on the screen. If the window is not there, use
112      * the {@link #getInsetsForWindowMetrics} to get insets instead.
113      *
114      * @param target The window associate with the perspective.
115      * @return The state stripped of the necessary information.
116      */
getInsetsForWindow(@onNull WindowState target)117     InsetsState getInsetsForWindow(@NonNull WindowState target) {
118         final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState();
119         if (rotatedState != null) {
120             return rotatedState;
121         }
122         final InsetsSourceProvider provider = target.getControllableInsetProvider();
123         final @InternalInsetsType int type = provider != null
124                 ? provider.getSource().getType() : ITYPE_INVALID;
125         return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
126                 target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
127                         (target.mAttrs.receiveInsetsIgnoringZOrder ? mState :
128                          target.mAboveInsetsState));
129     }
130 
getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)131     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
132         final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
133         final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
134         if (token != null) {
135             final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
136             if (rotatedState != null) {
137                 return rotatedState;
138             }
139         }
140         final @WindowingMode int windowingMode = token != null
141                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
142         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
143         return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
144     }
145 
146     private static @InternalInsetsType
getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs)147             int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) {
148         @WindowType int type = attrs.type;
149         switch (type) {
150             case TYPE_STATUS_BAR:
151                 return ITYPE_STATUS_BAR;
152             case TYPE_NAVIGATION_BAR:
153                 return ITYPE_NAVIGATION_BAR;
154             case TYPE_INPUT_METHOD:
155                 return ITYPE_IME;
156         }
157 
158         // If not one of the types above, check whether an internal inset mapping is specified.
159         if (attrs.providesInsetsTypes != null) {
160             for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
161                 switch (insetsType) {
162                     case ITYPE_STATUS_BAR:
163                     case ITYPE_NAVIGATION_BAR:
164                     case ITYPE_CLIMATE_BAR:
165                     case ITYPE_EXTRA_NAVIGATION_BAR:
166                         return insetsType;
167                 }
168             }
169         }
170 
171         return ITYPE_INVALID;
172     }
173 
174     /**
175      * @see #getInsetsForWindow
176      * @see #getInsetsForWindowMetrics
177      */
getInsetsForTarget(@nternalInsetsType int type, @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state)178     private InsetsState getInsetsForTarget(@InternalInsetsType int type,
179             @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
180         boolean stateCopied = false;
181 
182         if (type != ITYPE_INVALID) {
183             state = new InsetsState(state);
184             stateCopied = true;
185             state.removeSource(type);
186 
187             // Navigation bar doesn't get influenced by anything else
188             if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
189                 state.removeSource(ITYPE_IME);
190                 state.removeSource(ITYPE_STATUS_BAR);
191                 state.removeSource(ITYPE_CLIMATE_BAR);
192                 state.removeSource(ITYPE_CAPTION_BAR);
193                 state.removeSource(ITYPE_NAVIGATION_BAR);
194                 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
195             }
196 
197             // Status bar doesn't get influenced by caption bar
198             if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
199                 state.removeSource(ITYPE_CAPTION_BAR);
200             }
201 
202             // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
203             if (type == ITYPE_IME) {
204                 for (int i = mProviders.size() - 1; i >= 0; i--) {
205                     InsetsSourceProvider otherProvider = mProviders.valueAt(i);
206                     if (otherProvider.overridesImeFrame()) {
207                         InsetsSource override =
208                                 new InsetsSource(
209                                         state.getSource(otherProvider.getSource().getType()));
210                         override.setFrame(otherProvider.getImeOverrideFrame());
211                         state.addSource(override);
212                     }
213                 }
214             }
215         }
216 
217         if (WindowConfiguration.isFloating(windowingMode)
218                 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
219             if (!stateCopied) {
220                 state = new InsetsState(state);
221                 stateCopied = true;
222             }
223             state.removeSource(ITYPE_STATUS_BAR);
224             state.removeSource(ITYPE_NAVIGATION_BAR);
225             state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
226             if (windowingMode == WINDOWING_MODE_PINNED) {
227                 state.removeSource(ITYPE_IME);
228             }
229         }
230 
231         return state;
232     }
233 
getRawInsetsState()234     InsetsState getRawInsetsState() {
235         return mState;
236     }
237 
getControlsForDispatch(InsetsControlTarget target)238     @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) {
239         ArrayList<Integer> controlled = mControlTargetTypeMap.get(target);
240         if (controlled == null) {
241             return null;
242         }
243         final int size = controlled.size();
244         final InsetsSourceControl[] result = new InsetsSourceControl[size];
245         for (int i = 0; i < size; i++) {
246             result[i] = mProviders.get(controlled.get(i)).getControl(target);
247         }
248         return result;
249     }
250 
addProvidersToTransition()251     public void addProvidersToTransition() {
252         for (int i = mProviders.size() - 1; i >= 0; --i) {
253             final InsetsSourceProvider p = mProviders.valueAt(i);
254             if (p == null) continue;
255             final WindowContainer wc = p.mWin;
256             if (wc == null) continue;
257             mDisplayContent.mAtmService.getTransitionController().collect(wc);
258         }
259     }
260 
261     /**
262      * @return The provider of a specific type.
263      */
getSourceProvider(@nternalInsetsType int type)264     InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
265         if (type == ITYPE_IME) {
266             return mProviders.computeIfAbsent(type,
267                     key -> new ImeInsetsSourceProvider(
268                             mState.getSource(key), this, mDisplayContent));
269         } else {
270             return mProviders.computeIfAbsent(type,
271                     key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
272         }
273     }
274 
getImeSourceProvider()275     ImeInsetsSourceProvider getImeSourceProvider() {
276         return (ImeInsetsSourceProvider) getSourceProvider(ITYPE_IME);
277     }
278 
279     /**
280      * @return The provider of a specific type or null if we don't have it.
281      */
peekSourceProvider(@nternalInsetsType int type)282     @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
283         return mProviders.get(type);
284     }
285 
286     /**
287      * Called when a layout pass has occurred.
288      */
onPostLayout()289     void onPostLayout() {
290         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ISC.onPostLayout");
291         for (int i = mProviders.size() - 1; i >= 0; i--) {
292             mProviders.valueAt(i).onPostLayout();
293         }
294         final ArrayList<WindowState> winInsetsChanged = mDisplayContent.mWinInsetsChanged;
295         if (!mLastState.equals(mState)) {
296             mLastState.set(mState, true /* copySources */);
297             notifyInsetsChanged();
298         } else {
299             // The global insets state has not changed but there might be windows whose conditions
300             // (e.g., z-order) have changed. They can affect the insets states that we dispatch to
301             // the clients.
302             for (int i = winInsetsChanged.size() - 1; i >= 0; i--) {
303                 mDispatchInsetsChanged.accept(winInsetsChanged.get(i));
304             }
305         }
306         winInsetsChanged.clear();
307         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
308     }
309 
310     /**
311      * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
312      * z-order of a window is changed.
313      *
314      * @param win The window whose z-order has changed.
315      * @param notifyInsetsChange {@code true} if the clients should be notified about the change.
316      */
updateAboveInsetsState(WindowState win, boolean notifyInsetsChange)317     void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
318         if (win == null || win.getDisplayContent() != mDisplayContent) {
319             return;
320         }
321         final boolean[] aboveWin = { true };
322         final InsetsState aboveInsetsState = new InsetsState();
323         aboveInsetsState.set(mState,
324                 displayCutout() | systemGestures() | mandatorySystemGestures());
325         final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources;
326         final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
327         mDisplayContent.forAllWindows(w -> {
328             if (aboveWin[0]) {
329                 if (w == win) {
330                     aboveWin[0] = false;
331                     if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
332                         win.mAboveInsetsState.set(aboveInsetsState);
333                         insetsChangedWindows.add(win);
334                     }
335                     return winProvidedSources.size() == 0;
336                 } else {
337                     final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
338                     for (int i = providedSources.size() - 1; i >= 0; i--) {
339                         aboveInsetsState.addSource(providedSources.valueAt(i));
340                     }
341                     if (winProvidedSources.size() == 0) {
342                         return false;
343                     }
344                     boolean changed = false;
345                     for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
346                         changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
347                     }
348                     if (changed) {
349                         insetsChangedWindows.add(w);
350                     }
351                 }
352             } else {
353                 for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
354                     w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
355                 }
356                 insetsChangedWindows.add(w);
357             }
358             return false;
359         }, true /* traverseTopToBottom */);
360         if (notifyInsetsChange) {
361             for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
362                 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
363             }
364         }
365     }
366 
onDisplayInfoUpdated(boolean notifyInsetsChange)367     void onDisplayInfoUpdated(boolean notifyInsetsChange) {
368         final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
369         mDisplayContent.forAllWindows(w -> {
370             w.mAboveInsetsState.set(mState, displayCutout());
371             insetsChangedWindows.add(w);
372         }, true /* traverseTopToBottom */);
373         if (notifyInsetsChange) {
374             for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
375                 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
376             }
377         }
378     }
379 
onInsetsModified(InsetsControlTarget caller)380     void onInsetsModified(InsetsControlTarget caller) {
381         boolean changed = false;
382         for (int i = mProviders.size() - 1; i >= 0; i--) {
383             changed |= mProviders.valueAt(i).updateClientVisibility(caller);
384         }
385         if (changed) {
386             notifyInsetsChanged();
387             mDisplayContent.updateSystemGestureExclusion();
388             mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw();
389         }
390     }
391 
392     /**
393      * Computes insets state of the insets provider window in the display frames.
394      *
395      * @param win The owner window of insets provider.
396      * @param displayFrames The display frames to create insets source.
397      * @param windowFrames The specified frames to represent the owner window.
398      */
computeSimulatedState(WindowState win, DisplayFrames displayFrames, WindowFrames windowFrames)399     void computeSimulatedState(WindowState win, DisplayFrames displayFrames,
400             WindowFrames windowFrames) {
401         final InsetsState state = displayFrames.mInsetsState;
402         for (int i = mProviders.size() - 1; i >= 0; i--) {
403             final InsetsSourceProvider provider = mProviders.valueAt(i);
404             if (provider.mWin == win) {
405                 state.addSource(provider.createSimulatedSource(displayFrames, windowFrames));
406             }
407         }
408     }
409 
isFakeTarget(@nternalInsetsType int type, InsetsControlTarget target)410     boolean isFakeTarget(@InternalInsetsType int type, InsetsControlTarget target) {
411         return mTypeFakeControlTargetMap.get(type) == target;
412     }
413 
onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)414     void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) {
415 
416         // Make sure that we always have a control target for the IME, even if the IME target is
417         // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible.
418         InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget;
419         onControlChanged(ITYPE_IME, target);
420         ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s",
421                 target != null ? target.getWindow() : "null");
422         notifyPendingInsetsControlChanged();
423     }
424 
425     /**
426      * Called when the focused window that is able to control the system bars changes.
427      *
428      * @param statusControlling The target that is now able to control the status bar appearance
429      *                          and visibility.
430      * @param navControlling The target that is now able to control the nav bar appearance
431      *                       and visibility.
432      */
onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)433     void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling,
434             @Nullable InsetsControlTarget fakeStatusControlling,
435             @Nullable InsetsControlTarget navControlling,
436             @Nullable InsetsControlTarget fakeNavControlling) {
437         onControlChanged(ITYPE_STATUS_BAR, statusControlling);
438         onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);
439         onControlChanged(ITYPE_CLIMATE_BAR, statusControlling);
440         onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling);
441         onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
442         onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
443         onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling);
444         onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling);
445         notifyPendingInsetsControlChanged();
446     }
447 
notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)448     void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
449             InsetsSourceProvider provider) {
450         removeFromControlMaps(previousControlTarget, provider.getSource().getType(),
451                 false /* fake */);
452     }
453 
onControlChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget target)454     private void onControlChanged(@InternalInsetsType int type,
455             @Nullable InsetsControlTarget target) {
456         final InsetsControlTarget previous = mTypeControlTargetMap.get(type);
457         if (target == previous) {
458             return;
459         }
460         final InsetsSourceProvider provider = mProviders.get(type);
461         if (provider == null) {
462             return;
463         }
464         if (!provider.isControllable()) {
465             return;
466         }
467         provider.updateControlForTarget(target, false /* force */);
468         target = provider.getControlTarget();
469         if (previous != null) {
470             removeFromControlMaps(previous, type, false /* fake */);
471             mPendingControlChanged.add(previous);
472         }
473         if (target != null) {
474             addToControlMaps(target, type, false /* fake */);
475             mPendingControlChanged.add(target);
476         }
477     }
478 
479     /**
480      * The fake target saved here will be used to pretend to the app that it's still under control
481      * of the bars while it's not really, but we still need to find out the apps intentions around
482      * showing/hiding. For example, when the transient bars are showing, and the fake target
483      * requests to show system bars, the transient state will be aborted.
484      */
onControlFakeTargetChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget fakeTarget)485     void onControlFakeTargetChanged(@InternalInsetsType int type,
486             @Nullable InsetsControlTarget fakeTarget) {
487         final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type);
488         if (fakeTarget == previous) {
489             return;
490         }
491         final InsetsSourceProvider provider = mProviders.get(type);
492         if (provider == null) {
493             return;
494         }
495         provider.updateControlForFakeTarget(fakeTarget);
496         if (previous != null) {
497             removeFromControlMaps(previous, type, true /* fake */);
498             mPendingControlChanged.add(previous);
499         }
500         if (fakeTarget != null) {
501             addToControlMaps(fakeTarget, type, true /* fake */);
502             mPendingControlChanged.add(fakeTarget);
503         }
504     }
505 
removeFromControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)506     private void removeFromControlMaps(@NonNull InsetsControlTarget target,
507             @InternalInsetsType int type, boolean fake) {
508         final ArrayList<Integer> array = mControlTargetTypeMap.get(target);
509         if (array == null) {
510             return;
511         }
512         array.remove((Integer) type);
513         if (array.isEmpty()) {
514             mControlTargetTypeMap.remove(target);
515         }
516         if (fake) {
517             mTypeFakeControlTargetMap.remove(type);
518         } else {
519             mTypeControlTargetMap.remove(type);
520         }
521     }
522 
addToControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)523     private void addToControlMaps(@NonNull InsetsControlTarget target,
524             @InternalInsetsType int type, boolean fake) {
525         final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target,
526                 key -> new ArrayList<>());
527         array.add(type);
528         if (fake) {
529             mTypeFakeControlTargetMap.put(type, target);
530         } else {
531             mTypeControlTargetMap.put(type, target);
532         }
533     }
534 
notifyControlChanged(InsetsControlTarget target)535     void notifyControlChanged(InsetsControlTarget target) {
536         mPendingControlChanged.add(target);
537         notifyPendingInsetsControlChanged();
538     }
539 
notifyPendingInsetsControlChanged()540     private void notifyPendingInsetsControlChanged() {
541         if (mPendingControlChanged.isEmpty()) {
542             return;
543         }
544         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
545             for (int i = mProviders.size() - 1; i >= 0; i--) {
546                 final InsetsSourceProvider provider = mProviders.valueAt(i);
547                 provider.onSurfaceTransactionApplied();
548             }
549             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
550             for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
551                 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
552                 controlTarget.notifyInsetsControlChanged();
553                 if (mControlTargetTypeMap.containsKey(controlTarget)) {
554                     // We only collect targets who get controls, not lose controls.
555                     newControlTargets.add(controlTarget);
556                 }
557             }
558             mPendingControlChanged.clear();
559 
560             // This updates the insets visibilities AFTER sending current insets state and controls
561             // to the clients, so that the clients can change the current visibilities to the
562             // requested visibilities with animations.
563             for (int i = newControlTargets.size() - 1; i >= 0; i--) {
564                 onInsetsModified(newControlTargets.valueAt(i));
565             }
566             newControlTargets.clear();
567         });
568     }
569 
notifyInsetsChanged()570     void notifyInsetsChanged() {
571         mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged);
572     }
573 
dump(String prefix, PrintWriter pw)574     void dump(String prefix, PrintWriter pw) {
575         pw.println(prefix + "WindowInsetsStateController");
576         prefix = prefix + "  ";
577         mState.dump(prefix, pw);
578         pw.println(prefix + "Control map:");
579         for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) {
580             pw.print(prefix + "  ");
581             pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> "
582                     + mTypeControlTargetMap.valueAt(i));
583         }
584         pw.println(prefix + "InsetsSourceProviders:");
585         for (int i = mProviders.size() - 1; i >= 0; i--) {
586             mProviders.valueAt(i).dump(pw, prefix + "  ");
587         }
588     }
589 }
590