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