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