1 /*
2  * Copyright (C) 2019 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.StatusBarManager.WINDOW_STATE_HIDDEN;
20 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
24 import static android.view.InsetsSource.ID_IME;
25 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
26 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
27 
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.app.ActivityTaskManager;
31 import android.app.StatusBarManager;
32 import android.app.WindowConfiguration;
33 import android.content.ComponentName;
34 import android.content.res.Resources;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.util.SparseArray;
38 import android.view.InsetsController;
39 import android.view.InsetsFrameProvider;
40 import android.view.InsetsSource;
41 import android.view.InsetsState;
42 import android.view.SurfaceControl;
43 import android.view.SyncRtSurfaceTransactionApplier;
44 import android.view.WindowInsets;
45 import android.view.WindowInsets.Type;
46 import android.view.WindowInsets.Type.InsetsType;
47 import android.view.WindowInsetsAnimation;
48 import android.view.WindowInsetsAnimation.Bounds;
49 import android.view.WindowManager;
50 import android.view.inputmethod.InputMethodManager;
51 
52 import com.android.internal.R;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.server.statusbar.StatusBarManagerInternal;
55 
56 import java.io.PrintWriter;
57 import java.util.List;
58 
59 /**
60  * Policy that implements who gets control over the windows generating insets.
61  */
62 class InsetsPolicy {
63 
64     public static final int CONTROLLABLE_TYPES = WindowInsets.Type.statusBars()
65             | WindowInsets.Type.navigationBars()
66             | WindowInsets.Type.ime();
67 
68     private final InsetsStateController mStateController;
69     private final DisplayContent mDisplayContent;
70     private final DisplayPolicy mPolicy;
71 
72     /** Used to show system bars transiently. This won't affect the layout. */
73     private final InsetsControlTarget mTransientControlTarget;
74 
75     /** Used to show system bars permanently. This will affect the layout. */
76     private final InsetsControlTarget mPermanentControlTarget;
77 
78     /**
79      * Used to override the visibility of {@link Type#statusBars()} when dispatching insets to
80      * clients.
81      */
82     private InsetsControlTarget mFakeStatusControlTarget;
83 
84     /**
85      * Used to override the visibility of {@link Type#navigationBars()} when dispatching insets to
86      * clients.
87      */
88     private InsetsControlTarget mFakeNavControlTarget;
89 
90     private WindowState mFocusedWin;
91     private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
92     private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
93     private @InsetsType int mShowingTransientTypes;
94     private @InsetsType int mForcedShowingTypes;
95 
96     private final boolean mHideNavBarForKeyboard;
97 
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent)98     InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
99         mStateController = stateController;
100         mDisplayContent = displayContent;
101         mPolicy = displayContent.getDisplayPolicy();
102         final Resources r = mPolicy.getContext().getResources();
103         mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
104         mTransientControlTarget = new ControlTarget(displayContent, "TransientControlTarget");
105         mPermanentControlTarget = new ControlTarget(displayContent, "PermanentControlTarget");
106     }
107 
108     /** Updates the target which can control system bars. */
updateBarControlTarget(@ullable WindowState focusedWin)109     void updateBarControlTarget(@Nullable WindowState focusedWin) {
110         if (mFocusedWin != focusedWin) {
111             abortTransient();
112         }
113         mFocusedWin = focusedWin;
114         final WindowState notificationShade = mPolicy.getNotificationShade();
115         final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
116         final InsetsControlTarget statusControlTarget =
117                 getStatusControlTarget(focusedWin, false /* fake */);
118         mFakeStatusControlTarget = statusControlTarget == mTransientControlTarget
119                 ? getStatusControlTarget(focusedWin, true /* fake */)
120                 : statusControlTarget == notificationShade
121                         ? getStatusControlTarget(topApp, true /* fake */)
122                         : null;
123         final InsetsControlTarget navControlTarget =
124                 getNavControlTarget(focusedWin, false /* fake */);
125         mFakeNavControlTarget = navControlTarget == mTransientControlTarget
126                 ? getNavControlTarget(focusedWin, true /* fake */)
127                 : navControlTarget == notificationShade
128                         ? getNavControlTarget(topApp, true /* fake */)
129                         : null;
130         mStateController.onBarControlTargetChanged(
131                 statusControlTarget, mFakeStatusControlTarget,
132                 navControlTarget, mFakeNavControlTarget);
133         mStatusBar.updateVisibility(statusControlTarget, Type.statusBars());
134         mNavBar.updateVisibility(navControlTarget, Type.navigationBars());
135     }
136 
hasHiddenSources(@nsetsType int types)137     boolean hasHiddenSources(@InsetsType int types) {
138         final InsetsState state = mStateController.getRawInsetsState();
139         for (int i = state.sourceSize() - 1; i >= 0; i--) {
140             final InsetsSource source = state.sourceAt(i);
141             if ((source.getType() & types) == 0) {
142                 continue;
143             }
144             if (!source.getFrame().isEmpty() && !source.isVisible()) {
145                 return true;
146             }
147         }
148         return false;
149     }
150 
showTransient(@nsetsType int types, boolean isGestureOnSystemBar)151     void showTransient(@InsetsType int types, boolean isGestureOnSystemBar) {
152         @InsetsType int showingTransientTypes = mShowingTransientTypes;
153         final InsetsState rawState = mStateController.getRawInsetsState();
154         for (int i = rawState.sourceSize() - 1; i >= 0; i--) {
155             final InsetsSource source = rawState.sourceAt(i);
156             if (source.isVisible()) {
157                 continue;
158             }
159             final @InsetsType int type = source.getType();
160             if ((source.getType() & types) == 0) {
161                 continue;
162             }
163             showingTransientTypes |= type;
164         }
165         if (mShowingTransientTypes != showingTransientTypes) {
166             mShowingTransientTypes = showingTransientTypes;
167             StatusBarManagerInternal statusBarManagerInternal =
168                     mPolicy.getStatusBarManagerInternal();
169             if (statusBarManagerInternal != null) {
170                 statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
171                         showingTransientTypes, isGestureOnSystemBar);
172             }
173             updateBarControlTarget(mFocusedWin);
174             dispatchTransientSystemBarsVisibilityChanged(
175                     mFocusedWin,
176                     (showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0,
177                     isGestureOnSystemBar);
178         }
179     }
180 
181     @VisibleForTesting
getTransientControlTarget()182     InsetsControlTarget getTransientControlTarget() {
183         return  mTransientControlTarget;
184     }
185 
186     @VisibleForTesting
getPermanentControlTarget()187     InsetsControlTarget getPermanentControlTarget() {
188         return  mPermanentControlTarget;
189     }
190 
hideTransient()191     void hideTransient() {
192         if (mShowingTransientTypes == 0) {
193             return;
194         }
195 
196         dispatchTransientSystemBarsVisibilityChanged(
197                 mFocusedWin,
198                 /* areVisible= */ false,
199                 /* wereRevealedFromSwipeOnSystemBar= */ false);
200 
201         mShowingTransientTypes = 0;
202         updateBarControlTarget(mFocusedWin);
203     }
204 
isTransient(@nsetsType int type)205     boolean isTransient(@InsetsType int type) {
206         return (mShowingTransientTypes & type) != 0;
207     }
208 
209     /**
210      * Adjusts the sources in {@code originalState} to account for things like transient bars, IME
211      * & rounded corners.
212      */
adjustInsetsForWindow(WindowState target, InsetsState originalState, boolean includesTransient)213     InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState,
214             boolean includesTransient) {
215         InsetsState state;
216         if (!includesTransient) {
217             state = adjustVisibilityForFakeControllingSources(originalState);
218         } else {
219             state = originalState;
220         }
221         state = adjustVisibilityForIme(target, state, state == originalState);
222         return adjustInsetsForRoundedCorners(target.mToken, state, state == originalState);
223     }
224 
adjustInsetsForWindow(WindowState target, InsetsState originalState)225     InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) {
226         return adjustInsetsForWindow(target, originalState, false);
227     }
228 
229     /**
230      * @see WindowState#getInsetsState()
231      */
getInsetsForWindowMetrics(@ullable WindowToken token, @NonNull InsetsState outInsetsState)232     void getInsetsForWindowMetrics(@Nullable WindowToken token,
233             @NonNull InsetsState outInsetsState) {
234         final InsetsState srcState = token != null && token.isFixedRotationTransforming()
235                 ? token.getFixedRotationTransformInsetsState()
236                 : mStateController.getRawInsetsState();
237         outInsetsState.set(srcState, true /* copySources */);
238         for (int i = outInsetsState.sourceSize() - 1; i >= 0; i--) {
239             final InsetsSource source = outInsetsState.sourceAt(i);
240             if (isTransient(source.getType())) {
241                 source.setVisible(false);
242             }
243         }
244         adjustInsetsForRoundedCorners(token, outInsetsState, false /* copyState */);
245         if (token != null && token.hasSizeCompatBounds()) {
246             outInsetsState.scale(1f / token.getCompatScale());
247         }
248     }
249 
250     /**
251      * Modifies the given {@code state} according to insets provided by the target. When performing
252      * layout of the target or dispatching insets to the target, we need to exclude sources which
253      * should not be received by the target. e.g., the visible (non-gesture-wise) source provided by
254      * the target window itself.
255      *
256      * We also need to exclude certain types of insets source for client within specific windowing
257      * modes.
258      *
259      * @param attrs the LayoutParams of the target
260      * @param windowingMode the windowing mode of the target
261      * @param isAlwaysOnTop is the target always on top
262      * @param state the input inset state containing all the sources
263      * @return The state stripped of the necessary information.
264      */
enforceInsetsPolicyForTarget(WindowManager.LayoutParams attrs, @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state)265     InsetsState enforceInsetsPolicyForTarget(WindowManager.LayoutParams attrs,
266             @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
267             InsetsState state) {
268         final InsetsState originalState = state;
269 
270         // The caller should not receive the visible insets provided by itself.
271         if (attrs.type == TYPE_INPUT_METHOD) {
272             state = new InsetsState(state);
273             state.removeSource(ID_IME);
274         } else if (attrs.providedInsets != null) {
275             for (InsetsFrameProvider provider : attrs.providedInsets) {
276                 if ((provider.getType() & WindowInsets.Type.systemBars()) == 0) {
277                     continue;
278                 }
279                 if (state == originalState) {
280                     state = new InsetsState(state);
281                 }
282                 state.removeSource(provider.getId());
283             }
284         }
285 
286         if (!attrs.isFullscreen() || attrs.getFitInsetsTypes() != 0) {
287             if (state == originalState) {
288                 state = new InsetsState(originalState);
289             }
290             // Explicitly exclude floating windows from receiving caption insets. This is because we
291             // hard code caption insets for windows due to a synchronization issue that leads to
292             // flickering that bypasses insets frame calculation, which consequently needs us to
293             // remove caption insets from floating windows.
294             // TODO(b/254128050): Remove this workaround after we find a way to update window frames
295             //  and caption insets frames simultaneously.
296             for (int i = state.sourceSize() - 1; i >= 0; i--) {
297                 if (state.sourceAt(i).getType() == Type.captionBar()) {
298                     state.removeSourceAt(i);
299                 }
300             }
301         }
302 
303         final SparseArray<InsetsSourceProvider> providers = mStateController.getSourceProviders();
304         final int windowType = attrs.type;
305         for (int i = providers.size() - 1; i >= 0; i--) {
306             final InsetsSourceProvider otherProvider = providers.valueAt(i);
307             if (otherProvider.overridesFrame(windowType)) {
308                 if (state == originalState) {
309                     state = new InsetsState(state);
310                 }
311                 final InsetsSource override = new InsetsSource(otherProvider.getSource());
312                 override.setFrame(otherProvider.getOverriddenFrame(windowType));
313                 state.addSource(override);
314             }
315         }
316 
317         if (WindowConfiguration.isFloating(windowingMode)
318                 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
319             // Keep frames, caption, and IME.
320             int types = WindowInsets.Type.captionBar();
321             if (windowingMode != WINDOWING_MODE_PINNED) {
322                 types |= WindowInsets.Type.ime();
323             }
324             final InsetsState newState = new InsetsState();
325             newState.set(state, types);
326             state = newState;
327         }
328 
329         return state;
330     }
331 
adjustVisibilityForFakeControllingSources(InsetsState originalState)332     private InsetsState adjustVisibilityForFakeControllingSources(InsetsState originalState) {
333         if (mFakeStatusControlTarget == null && mFakeNavControlTarget == null) {
334             return originalState;
335         }
336         InsetsState state = originalState;
337         for (int i = state.sourceSize() - 1; i >= 0; i--) {
338             final InsetsSource source = state.sourceAt(i);
339             state = adjustVisibilityForFakeControllingSource(state, Type.statusBars(), source,
340                     mFakeStatusControlTarget);
341             state = adjustVisibilityForFakeControllingSource(state, Type.navigationBars(), source,
342                     mFakeNavControlTarget);
343         }
344         return state;
345     }
346 
adjustVisibilityForFakeControllingSource(InsetsState originalState, @InsetsType int type, InsetsSource source, InsetsControlTarget target)347     private static InsetsState adjustVisibilityForFakeControllingSource(InsetsState originalState,
348             @InsetsType int type, InsetsSource source, InsetsControlTarget target) {
349         if (source.getType() != type || target == null) {
350             return originalState;
351         }
352         final boolean isRequestedVisible = target.isRequestedVisible(type);
353         if (source.isVisible() == isRequestedVisible) {
354             return originalState;
355         }
356         // The source will be modified, create a non-deep copy to store the new one.
357         final InsetsState state = new InsetsState(originalState);
358 
359         // Replace the source with a copy with the overridden visibility.
360         final InsetsSource outSource = new InsetsSource(source);
361         outSource.setVisible(isRequestedVisible);
362         state.addSource(outSource);
363         return state;
364     }
365 
adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState)366     private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
367             boolean copyState) {
368         if (w.mIsImWindow) {
369             InsetsState state = originalState;
370             // If navigation bar is not hidden by IME, IME should always receive visible
371             // navigation bar insets.
372             final boolean navVisible = !mHideNavBarForKeyboard;
373             for (int i = originalState.sourceSize() - 1; i >= 0; i--) {
374                 final InsetsSource source = originalState.sourceAt(i);
375                 if (source.getType() != Type.navigationBars() || source.isVisible() == navVisible) {
376                     continue;
377                 }
378                 if (state == originalState && copyState) {
379                     state = new InsetsState(originalState);
380                 }
381                 final InsetsSource navSource = new InsetsSource(source);
382                 navSource.setVisible(navVisible);
383                 state.addSource(navSource);
384             }
385             return state;
386         } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
387             // During switching tasks with gestural navigation, before the next IME input target
388             // starts the input, we should adjust and freeze the last IME visibility of the window
389             // in case delivering obsoleted IME insets state during transitioning.
390             final InsetsSource originalImeSource = originalState.peekSource(ID_IME);
391 
392             if (originalImeSource != null) {
393                 final boolean imeVisibility = w.isRequestedVisible(Type.ime());
394                 final InsetsState state = copyState
395                         ? new InsetsState(originalState)
396                         : originalState;
397                 final InsetsSource imeSource = new InsetsSource(originalImeSource);
398                 imeSource.setVisible(imeVisibility);
399                 state.addSource(imeSource);
400                 return state;
401             }
402         } else if (w.mImeInsetsConsumed) {
403             // Set the IME source (if there is one) to be invisible if it has been consumed.
404             final InsetsSource originalImeSource = originalState.peekSource(ID_IME);
405             if (originalImeSource != null && originalImeSource.isVisible()) {
406                 final InsetsState state = copyState
407                         ? new InsetsState(originalState)
408                         : originalState;
409                 final InsetsSource imeSource = new InsetsSource(originalImeSource);
410                 imeSource.setVisible(false);
411                 state.addSource(imeSource);
412                 return state;
413             }
414         }
415         return originalState;
416     }
417 
adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState, boolean copyState)418     private InsetsState adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState,
419             boolean copyState) {
420         if (token != null) {
421             final ActivityRecord activityRecord = token.asActivityRecord();
422             final Task task = activityRecord != null ? activityRecord.getTask() : null;
423             if (task != null && !task.getWindowConfiguration().tasksAreFloating()) {
424                 // Use task bounds to calculating rounded corners if the task is not floating.
425                 final InsetsState state = copyState ? new InsetsState(originalState)
426                         : originalState;
427                 state.setRoundedCornerFrame(token.isFixedRotationTransforming()
428                         ? token.getFixedRotationTransformDisplayBounds()
429                         : task.getBounds());
430                 return state;
431             }
432         }
433         return originalState;
434     }
435 
onRequestedVisibleTypesChanged(InsetsControlTarget caller)436     void onRequestedVisibleTypesChanged(InsetsControlTarget caller) {
437         mStateController.onRequestedVisibleTypesChanged(caller);
438         checkAbortTransient(caller);
439         updateBarControlTarget(mFocusedWin);
440     }
441 
442     /**
443      * Called when a control target modified the insets state. If the target set a insets source to
444      * visible while it is shown transiently, we need to abort the transient state. While IME is
445      * requested visible, we also need to abort the transient state of navigation bar if it is shown
446      * transiently.
447      *
448      * @param caller who changed the insets state.
449      */
checkAbortTransient(InsetsControlTarget caller)450     private void checkAbortTransient(InsetsControlTarget caller) {
451         if (mShowingTransientTypes == 0) {
452             return;
453         }
454         final boolean isImeVisible = mStateController.getImeSourceProvider().isClientVisible();
455         final @InsetsType int fakeControllingTypes =
456                 mStateController.getFakeControllingTypes(caller);
457         final @InsetsType int abortTypes =
458                 (fakeControllingTypes & caller.getRequestedVisibleTypes())
459                 | (isImeVisible ? Type.navigationBars() : 0);
460         mShowingTransientTypes &= ~abortTypes;
461         if (abortTypes != 0) {
462             mDisplayContent.setLayoutNeeded();
463             mDisplayContent.mWmService.requestTraversal();
464             final StatusBarManagerInternal statusBarManager = mPolicy.getStatusBarManagerInternal();
465             if (statusBarManager != null) {
466                 statusBarManager.abortTransient(mDisplayContent.getDisplayId(), abortTypes);
467             }
468         }
469     }
470 
471     /**
472      * If the caller is not {@link #updateBarControlTarget}, it should call
473      * updateBarControlTarget(mFocusedWin) after this invocation.
474      */
abortTransient()475     private void abortTransient() {
476         if (mShowingTransientTypes == 0) {
477             return;
478         }
479         final StatusBarManagerInternal statusBarManager = mPolicy.getStatusBarManagerInternal();
480         if (statusBarManager != null) {
481             statusBarManager.abortTransient(mDisplayContent.getDisplayId(), mShowingTransientTypes);
482         }
483         mShowingTransientTypes = 0;
484         mDisplayContent.setLayoutNeeded();
485         mDisplayContent.mWmService.requestTraversal();
486 
487         dispatchTransientSystemBarsVisibilityChanged(
488                 mFocusedWin,
489                 /* areVisible= */ false,
490                 /* wereRevealedFromSwipeOnSystemBar= */ false);
491     }
492 
getStatusControlTarget(@ullable WindowState focusedWin, boolean fake)493     private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
494             boolean fake) {
495         if (!fake && isTransient(Type.statusBars())) {
496             return mTransientControlTarget;
497         }
498         final WindowState notificationShade = mPolicy.getNotificationShade();
499         if (focusedWin == notificationShade) {
500             // Notification shade has control anyways, no reason to force anything.
501             return focusedWin;
502         }
503         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
504             ComponentName component = focusedWin.mActivityRecord != null
505                     ? focusedWin.mActivityRecord.mActivityComponent : null;
506             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
507                     component, focusedWin.getRequestedVisibleTypes());
508             return mDisplayContent.mRemoteInsetsControlTarget;
509         }
510         if (areTypesForciblyShowing(Type.statusBars())) {
511             // Status bar is forcibly shown. We don't want the client to control the status bar, and
512             // we will dispatch the real visibility of status bar to the client.
513             return mPermanentControlTarget;
514         }
515         if (mPolicy.areTypesForciblyShownTransiently(Type.statusBars()) && !fake) {
516             // Status bar is forcibly shown transiently, and its new visibility won't be
517             // dispatched to the client so that we can keep the layout stable. We will dispatch the
518             // fake control to the client, so that it can re-show the bar during this scenario.
519             return mTransientControlTarget;
520         }
521         if (!canBeTopFullscreenOpaqueWindow(focusedWin)
522                 && mPolicy.topAppHidesSystemBar(Type.statusBars())
523                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
524             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
525             // window hides status bar, unless the notification shade can receive keys.
526             return mPolicy.getTopFullscreenOpaqueWindow();
527         }
528         return focusedWin;
529     }
530 
canBeTopFullscreenOpaqueWindow(@ullable WindowState win)531     private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) {
532         // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may
533         // haven't drawn or committed the visibility.
534         final boolean nonAttachedAppWindow = win != null
535                 && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
536                 && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
537         return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent()
538                 && !win.inMultiWindowMode();
539     }
540 
getNavControlTarget(@ullable WindowState focusedWin, boolean fake)541     private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
542             boolean fake) {
543         final WindowState imeWin = mDisplayContent.mInputMethodWindow;
544         if (imeWin != null && imeWin.isVisible() && !mHideNavBarForKeyboard) {
545             // Force showing navigation bar while IME is visible and if navigation bar is not
546             // configured to be hidden by the IME.
547             return mPermanentControlTarget;
548         }
549         if (!fake && isTransient(Type.navigationBars())) {
550             return mTransientControlTarget;
551         }
552         if (focusedWin == mPolicy.getNotificationShade()) {
553             // Notification shade has control anyways, no reason to force anything.
554             return focusedWin;
555         }
556         if (focusedWin != null) {
557             final InsetsSourceProvider provider = focusedWin.getControllableInsetProvider();
558             if (provider != null && provider.getSource().getType() == Type.navigationBars()) {
559                 // Navigation bar has control if it is focused.
560                 return focusedWin;
561             }
562         }
563         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
564             ComponentName component = focusedWin.mActivityRecord != null
565                     ? focusedWin.mActivityRecord.mActivityComponent : null;
566             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
567                     component, focusedWin.getRequestedVisibleTypes());
568             return mDisplayContent.mRemoteInsetsControlTarget;
569         }
570         if (areTypesForciblyShowing(Type.navigationBars())) {
571             // Navigation bar is forcibly shown. We don't want the client to control the navigation
572             // bar, and we will dispatch the real visibility of navigation bar to the client.
573             return mPermanentControlTarget;
574         }
575         if (mPolicy.areTypesForciblyShownTransiently(Type.navigationBars()) && !fake) {
576             // Navigation bar is forcibly shown transiently, and its new visibility won't be
577             // dispatched to the client so that we can keep the layout stable. We will dispatch the
578             // fake control to the client, so that it can re-show the bar during this scenario.
579             return mTransientControlTarget;
580         }
581         final WindowState notificationShade = mPolicy.getNotificationShade();
582         if (!canBeTopFullscreenOpaqueWindow(focusedWin)
583                 && mPolicy.topAppHidesSystemBar(Type.navigationBars())
584                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
585             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
586             // window hides navigation bar, unless the notification shade can receive keys.
587             return mPolicy.getTopFullscreenOpaqueWindow();
588         }
589         return focusedWin;
590     }
591 
areTypesForciblyShowing(@nsetsType int types)592     boolean areTypesForciblyShowing(@InsetsType int types) {
593         return (mForcedShowingTypes & types) == types;
594     }
595 
updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode)596     void updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode) {
597         mForcedShowingTypes = (inSplitScreenMode || inFreeformMode)
598                 ? (Type.statusBars() | Type.navigationBars())
599                 : forceShowingNavigationBars(win)
600                         ? Type.navigationBars()
601                         : 0;
602 
603         // The client app won't be able to control these types of system bars. Here makes the client
604         // forcibly consume these types to prevent the app content from getting obscured.
605         mStateController.setForcedConsumingTypes(
606                 mForcedShowingTypes | (remoteInsetsControllerControlsSystemBars(win)
607                         ? (Type.statusBars() | Type.navigationBars())
608                         : 0));
609 
610         updateBarControlTarget(win);
611     }
612 
forceShowingNavigationBars(WindowState win)613     private boolean forceShowingNavigationBars(WindowState win) {
614         // When "force show navigation bar" is enabled, it means both force visible is true, and
615         // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
616         // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
617         // still control the navigation bar in this mode.
618         return mPolicy.isForceShowNavigationBarEnabled() && win != null
619                 && win.getActivityType() == ACTIVITY_TYPE_STANDARD;
620     }
621 
622     /**
623      * Determines whether the remote insets controller should take control of system bars for all
624      * windows.
625      */
remoteInsetsControllerControlsSystemBars(@ullable WindowState focusedWin)626     boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
627         if (focusedWin == null) {
628             return false;
629         }
630 
631         if (!mPolicy.isRemoteInsetsControllerControllingSystemBars()) {
632             return false;
633         }
634         if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
635             // No remote insets control target to take control of insets.
636             return false;
637         }
638         // If necessary, auto can control application windows when
639         // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
640         // where we want to dictate system bar inset state for applications.
641         return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
642                 && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
643     }
644 
dispatchTransientSystemBarsVisibilityChanged( @ullable WindowState focusedWindow, boolean areVisible, boolean wereRevealedFromSwipeOnSystemBar)645     private void dispatchTransientSystemBarsVisibilityChanged(
646             @Nullable WindowState focusedWindow,
647             boolean areVisible,
648             boolean wereRevealedFromSwipeOnSystemBar) {
649         if (focusedWindow == null) {
650             return;
651         }
652 
653         Task task = focusedWindow.getTask();
654         if (task == null) {
655             return;
656         }
657 
658         int taskId = task.mTaskId;
659         boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID;
660         if (!isValidTaskId) {
661             return;
662         }
663 
664         mDisplayContent.mWmService.mTaskSystemBarsListenerController
665                 .dispatchTransientSystemBarVisibilityChanged(
666                         taskId,
667                         areVisible,
668                         wereRevealedFromSwipeOnSystemBar);
669     }
670 
dump(String prefix, PrintWriter pw)671     void dump(String prefix, PrintWriter pw) {
672         pw.println(prefix + "InsetsPolicy");
673         prefix = prefix + "  ";
674         pw.println(prefix + "status: " + StatusBarManager.windowStateToString(mStatusBar.mState));
675         pw.println(prefix + "nav: " + StatusBarManager.windowStateToString(mNavBar.mState));
676         if (mShowingTransientTypes != 0) {
677             pw.println(prefix + "mShowingTransientTypes="
678                     + WindowInsets.Type.toString(mShowingTransientTypes));
679         }
680         if (mForcedShowingTypes != 0) {
681             pw.println(prefix + "mForcedShowingTypes="
682                     + WindowInsets.Type.toString(mForcedShowingTypes));
683         }
684     }
685 
686     private class BarWindow {
687 
688         private final int mId;
689         private  @StatusBarManager.WindowVisibleState int mState =
690                 StatusBarManager.WINDOW_STATE_SHOWING;
691 
BarWindow(int id)692         BarWindow(int id) {
693             mId = id;
694         }
695 
updateVisibility(@ullable InsetsControlTarget controlTarget, @InsetsType int type)696         private void updateVisibility(@Nullable InsetsControlTarget controlTarget,
697                 @InsetsType int type) {
698             setVisible(controlTarget == null || controlTarget.isRequestedVisible(type));
699         }
700 
setVisible(boolean visible)701         private void setVisible(boolean visible) {
702             final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
703             if (mState != state) {
704                 mState = state;
705                 StatusBarManagerInternal statusBarManagerInternal =
706                         mPolicy.getStatusBarManagerInternal();
707                 if (statusBarManagerInternal != null) {
708                     statusBarManagerInternal.setWindowState(
709                             mDisplayContent.getDisplayId(), mId, state);
710                 }
711             }
712         }
713     }
714 
715     private static class ControlTarget implements InsetsControlTarget, Runnable {
716 
717         private final Handler mHandler;
718         private final Object mGlobalLock;
719         private final InsetsState mState = new InsetsState();
720         private final InsetsStateController mStateController;
721         private final InsetsController mInsetsController;
722         private final String mName;
723 
ControlTarget(DisplayContent displayContent, String name)724         ControlTarget(DisplayContent displayContent, String name) {
725             mHandler = displayContent.mWmService.mH;
726             mGlobalLock = displayContent.mWmService.mGlobalLock;
727             mStateController = displayContent.getInsetsStateController();
728             mInsetsController = new InsetsController(new Host(mHandler, name));
729             mName = name;
730         }
731 
732         @Override
notifyInsetsControlChanged(int displayId)733         public void notifyInsetsControlChanged(int displayId) {
734             mHandler.post(this);
735         }
736 
737         @Override
run()738         public void run() {
739             synchronized (mGlobalLock) {
740                 mState.set(mStateController.getRawInsetsState(), true /* copySources */);
741                 mInsetsController.onStateChanged(mState);
742                 mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this));
743             }
744         }
745 
746         @Override
toString()747         public String toString() {
748             return mName;
749         }
750     }
751 
752     private static class Host implements InsetsController.Host {
753 
754         private final float[] mTmpFloat9 = new float[9];
755         private final Handler mHandler;
756         private final String mName;
757 
758         private boolean mInsetsAnimationRunning;
759 
Host(Handler handler, String name)760         Host(Handler handler, String name) {
761             mHandler = handler;
762             mName = name;
763         }
764 
765         @Override
getHandler()766         public Handler getHandler() {
767             return mHandler;
768         }
769 
770         @Override
notifyInsetsChanged()771         public void notifyInsetsChanged() { }
772 
773         @Override
dispatchWindowInsetsAnimationPrepare( @onNull WindowInsetsAnimation animation)774         public void dispatchWindowInsetsAnimationPrepare(
775                 @NonNull WindowInsetsAnimation animation) { }
776 
777         @Override
dispatchWindowInsetsAnimationStart( @onNull WindowInsetsAnimation animation, @NonNull Bounds bounds)778         public Bounds dispatchWindowInsetsAnimationStart(
779                 @NonNull WindowInsetsAnimation animation,
780                 @NonNull Bounds bounds) {
781             return bounds;
782         }
783 
784         @Override
dispatchWindowInsetsAnimationProgress( @onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)785         public WindowInsets dispatchWindowInsetsAnimationProgress(
786                 @NonNull WindowInsets insets,
787                 @NonNull List<WindowInsetsAnimation> runningAnimations) {
788             return insets;
789         }
790 
791         @Override
dispatchWindowInsetsAnimationEnd( @onNull WindowInsetsAnimation animation)792         public void dispatchWindowInsetsAnimationEnd(
793                 @NonNull WindowInsetsAnimation animation) { }
794 
795         @Override
applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p)796         public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) {
797             final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
798             for (int i = p.length - 1; i >= 0; i--) {
799                 SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9);
800             }
801             t.apply();
802             t.close();
803         }
804 
805         @Override
updateRequestedVisibleTypes(int types)806         public void updateRequestedVisibleTypes(int types) { }
807 
808         @Override
hasAnimationCallbacks()809         public boolean hasAnimationCallbacks() {
810             return false;
811         }
812 
813         @Override
setSystemBarsAppearance(int appearance, int mask)814         public void setSystemBarsAppearance(int appearance, int mask) { }
815 
816         @Override
getSystemBarsAppearance()817         public int getSystemBarsAppearance() {
818             return 0;
819         }
820 
821         @Override
setSystemBarsBehavior(int behavior)822         public void setSystemBarsBehavior(int behavior) { }
823 
824         @Override
getSystemBarsBehavior()825         public int getSystemBarsBehavior() {
826             return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
827         }
828 
829         @Override
releaseSurfaceControlFromRt(SurfaceControl surfaceControl)830         public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
831             surfaceControl.release();
832         }
833 
834         @Override
addOnPreDrawRunnable(Runnable r)835         public void addOnPreDrawRunnable(Runnable r) { }
836 
837         @Override
postInsetsAnimationCallback(Runnable r)838         public void postInsetsAnimationCallback(Runnable r) { }
839 
840         @Override
getInputMethodManager()841         public InputMethodManager getInputMethodManager() {
842             return null;
843         }
844 
845         @Nullable
846         @Override
getRootViewTitle()847         public String getRootViewTitle() {
848             return mName;
849         }
850 
851         @Override
dipToPx(int dips)852         public int dipToPx(int dips) {
853             return 0;
854         }
855 
856         @Nullable
857         @Override
getWindowToken()858         public IBinder getWindowToken() {
859             return null;
860         }
861 
862         @Override
notifyAnimationRunningStateChanged(boolean running)863         public void notifyAnimationRunningStateChanged(boolean running) {
864             mInsetsAnimationRunning = running;
865         }
866     }
867 }
868