• 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.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