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