• 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_FULLSCREEN;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
25 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
26 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
27 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
28 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
29 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
30 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
31 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
32 import static android.view.InsetsState.ITYPE_IME;
33 import static android.view.InsetsState.ITYPE_INVALID;
34 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
35 import static android.view.InsetsState.ITYPE_STATUS_BAR;
36 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
37 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
38 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
39 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
40 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
41 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
42 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
43 
44 import android.annotation.NonNull;
45 import android.annotation.Nullable;
46 import android.app.ActivityTaskManager;
47 import android.app.StatusBarManager;
48 import android.app.WindowConfiguration;
49 import android.content.ComponentName;
50 import android.content.res.Resources;
51 import android.graphics.Rect;
52 import android.util.ArrayMap;
53 import android.util.IntArray;
54 import android.util.SparseArray;
55 import android.view.InsetsAnimationControlCallbacks;
56 import android.view.InsetsAnimationControlImpl;
57 import android.view.InsetsAnimationControlRunner;
58 import android.view.InsetsController;
59 import android.view.InsetsFrameProvider;
60 import android.view.InsetsSource;
61 import android.view.InsetsSourceControl;
62 import android.view.InsetsState;
63 import android.view.InsetsState.InternalInsetsType;
64 import android.view.InternalInsetsAnimationController;
65 import android.view.SurfaceControl;
66 import android.view.SyncRtSurfaceTransactionApplier;
67 import android.view.WindowInsets.Type;
68 import android.view.WindowInsetsAnimation;
69 import android.view.WindowInsetsAnimation.Bounds;
70 import android.view.WindowInsetsAnimationControlListener;
71 import android.view.WindowManager;
72 
73 import com.android.internal.R;
74 import com.android.internal.annotations.VisibleForTesting;
75 import com.android.server.DisplayThread;
76 import com.android.server.statusbar.StatusBarManagerInternal;
77 
78 /**
79  * Policy that implements who gets control over the windows generating insets.
80  */
81 class InsetsPolicy {
82 
83     private final InsetsStateController mStateController;
84     private final DisplayContent mDisplayContent;
85     private final DisplayPolicy mPolicy;
86     private final IntArray mShowingTransientTypes = new IntArray();
87 
88     /** For resetting visibilities of insets sources. */
89     private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
90 
91         @Override
92         public void notifyInsetsControlChanged() {
93             boolean hasLeash = false;
94             final InsetsSourceControl[] controls =
95                     mStateController.getControlsForDispatch(this);
96             if (controls == null) {
97                 return;
98             }
99             for (InsetsSourceControl control : controls) {
100                 final @InternalInsetsType int type = control.getType();
101                 if (mShowingTransientTypes.indexOf(type) != -1) {
102                     // The visibilities of transient bars will be handled with animations.
103                     continue;
104                 }
105                 final SurfaceControl leash = control.getLeash();
106                 if (leash != null) {
107                     hasLeash = true;
108 
109                     // We use alpha to control the visibility here which aligns the logic at
110                     // SurfaceAnimator.createAnimationLeash
111                     mDisplayContent.getPendingTransaction().setAlpha(
112                             leash, InsetsState.getDefaultVisibility(type) ? 1f : 0f);
113                 }
114             }
115             if (hasLeash) {
116                 mDisplayContent.scheduleAnimation();
117             }
118         }
119     };
120 
121     private WindowState mFocusedWin;
122     private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
123     private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
124     private boolean mAnimatingShown;
125     /**
126      * Let remote insets controller control system bars regardless of other settings.
127      */
128     private boolean mRemoteInsetsControllerControlsSystemBars;
129     private final boolean mHideNavBarForKeyboard;
130     private final float[] mTmpFloat9 = new float[9];
131 
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent)132     InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
133         mStateController = stateController;
134         mDisplayContent = displayContent;
135         mPolicy = displayContent.getDisplayPolicy();
136         final Resources r = mPolicy.getContext().getResources();
137         mRemoteInsetsControllerControlsSystemBars = r.getBoolean(
138                 R.bool.config_remoteInsetsControllerControlsSystemBars);
139         mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
140     }
141 
getRemoteInsetsControllerControlsSystemBars()142     boolean getRemoteInsetsControllerControlsSystemBars() {
143         return mRemoteInsetsControllerControlsSystemBars;
144     }
145 
146     /**
147      * Used only for testing.
148      */
149     @VisibleForTesting
setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars)150     void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
151         mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
152     }
153 
154     /** Updates the target which can control system bars. */
updateBarControlTarget(@ullable WindowState focusedWin)155     void updateBarControlTarget(@Nullable WindowState focusedWin) {
156         if (mFocusedWin != focusedWin) {
157             abortTransient();
158         }
159         mFocusedWin = focusedWin;
160         final InsetsControlTarget statusControlTarget =
161                 getStatusControlTarget(focusedWin, false /* fake */);
162         final InsetsControlTarget navControlTarget =
163                 getNavControlTarget(focusedWin, false /* fake */);
164         final WindowState notificationShade = mPolicy.getNotificationShade();
165         final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow();
166         mStateController.onBarControlTargetChanged(
167                 statusControlTarget,
168                 statusControlTarget == mDummyControlTarget
169                         ? getStatusControlTarget(focusedWin, true /* fake */)
170                         : statusControlTarget == notificationShade
171                                 ? getStatusControlTarget(topApp, true /* fake */)
172                                 : null,
173                 navControlTarget,
174                 navControlTarget == mDummyControlTarget
175                         ? getNavControlTarget(focusedWin, true /* fake */)
176                         : navControlTarget == notificationShade
177                                 ? getNavControlTarget(topApp, true /* fake */)
178                                 : null);
179         mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
180         mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
181     }
182 
isHidden(@nternalInsetsType int type)183     boolean isHidden(@InternalInsetsType int type) {
184         final WindowContainerInsetsSourceProvider provider = mStateController
185                 .peekSourceProvider(type);
186         return provider != null && provider.hasWindowContainer()
187                 && !provider.getSource().isVisible();
188     }
189 
showTransient(@nternalInsetsType int[] types, boolean isGestureOnSystemBar)190     void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
191         boolean changed = false;
192         for (int i = types.length - 1; i >= 0; i--) {
193             final @InternalInsetsType int type = types[i];
194             if (!isHidden(type)) {
195                 continue;
196             }
197             if (mShowingTransientTypes.indexOf(type) != -1) {
198                 continue;
199             }
200             mShowingTransientTypes.add(type);
201             changed = true;
202         }
203         if (changed) {
204             StatusBarManagerInternal statusBarManagerInternal =
205                     mPolicy.getStatusBarManagerInternal();
206             if (statusBarManagerInternal != null) {
207                 statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
208                         mShowingTransientTypes.toArray(), isGestureOnSystemBar);
209             }
210             updateBarControlTarget(mFocusedWin);
211             dispatchTransientSystemBarsVisibilityChanged(
212                     mFocusedWin,
213                     isTransient(ITYPE_STATUS_BAR) || isTransient(ITYPE_NAVIGATION_BAR),
214                     isGestureOnSystemBar);
215 
216             // The leashes can be created while updating bar control target. The surface transaction
217             // of the new leashes might not be applied yet. The callback posted here ensures we can
218             // get the valid leashes because the surface transaction will be applied in the next
219             // animation frame which will be triggered if a new leash is created.
220             mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
221                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
222                     startAnimation(true /* show */, null /* callback */);
223                 }
224             });
225         }
226     }
227 
hideTransient()228     void hideTransient() {
229         if (mShowingTransientTypes.size() == 0) {
230             return;
231         }
232 
233         dispatchTransientSystemBarsVisibilityChanged(
234                 mFocusedWin,
235                 /* areVisible= */ false,
236                 /* wereRevealedFromSwipeOnSystemBar= */ false);
237 
238         startAnimation(false /* show */, () -> {
239             synchronized (mDisplayContent.mWmService.mGlobalLock) {
240                 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
241                     // We are about to clear mShowingTransientTypes, we don't want the transient bar
242                     // can cause insets on the client. Restore the client visibility.
243                     final @InternalInsetsType int type = mShowingTransientTypes.get(i);
244                     mStateController.getSourceProvider(type).setClientVisible(false);
245                 }
246                 mShowingTransientTypes.clear();
247                 updateBarControlTarget(mFocusedWin);
248             }
249         });
250     }
251 
isTransient(@nternalInsetsType int type)252     boolean isTransient(@InternalInsetsType int type) {
253         return mShowingTransientTypes.indexOf(type) != -1;
254     }
255 
256     /**
257      * Adjusts the sources in {@code originalState} to account for things like transient bars, IME
258      * & rounded corners.
259      */
adjustInsetsForWindow(WindowState target, InsetsState originalState, boolean includesTransient)260     InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState,
261             boolean includesTransient) {
262         InsetsState state;
263         if (!includesTransient) {
264             state = adjustVisibilityForTransientTypes(originalState);
265         } else {
266             state = originalState;
267         }
268         state = adjustVisibilityForIme(target, state, state == originalState);
269         return adjustInsetsForRoundedCorners(target.mToken, state, state == originalState);
270     }
271 
adjustInsetsForWindow(WindowState target, InsetsState originalState)272     InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) {
273         return adjustInsetsForWindow(target, originalState, false);
274     }
275 
276     /**
277      * @see WindowState#getInsetsState()
278      */
getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)279     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
280         final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
281         final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
282         if (token != null) {
283             final InsetsState rotatedState = token.getFixedRotationTransformInsetsState();
284             if (rotatedState != null) {
285                 return rotatedState;
286             }
287         }
288         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
289         // Always use windowing mode fullscreen when get insets for window metrics to make sure it
290         // contains all insets types.
291         final InsetsState originalState = mDisplayContent.getInsetsPolicy()
292                 .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
293                         attrs.type, mStateController.getRawInsetsState());
294         InsetsState state = adjustVisibilityForTransientTypes(originalState);
295         return adjustInsetsForRoundedCorners(token, state, state == originalState);
296     }
297 
298     /**
299      * @param type the internal type of the insets.
300      * @return {@code true} if the given type is controllable, {@code false} otherwise.
301      */
isInsetsTypeControllable(@nternalInsetsType int type)302     static boolean isInsetsTypeControllable(@InternalInsetsType int type) {
303         switch (type) {
304             case ITYPE_STATUS_BAR:
305             case ITYPE_NAVIGATION_BAR:
306             case ITYPE_IME:
307             case ITYPE_CLIMATE_BAR:
308             case ITYPE_EXTRA_NAVIGATION_BAR:
309                 return true;
310             default:
311                 return false;
312         }
313     }
314 
getInsetsTypeForLayoutParams( WindowManager.LayoutParams attrs)315     private static @InternalInsetsType int getInsetsTypeForLayoutParams(
316             WindowManager.LayoutParams attrs) {
317         @WindowManager.LayoutParams.WindowType int type = attrs.type;
318         switch (type) {
319             case TYPE_STATUS_BAR:
320                 return ITYPE_STATUS_BAR;
321             case TYPE_NAVIGATION_BAR:
322                 return ITYPE_NAVIGATION_BAR;
323             case TYPE_INPUT_METHOD:
324                 return ITYPE_IME;
325         }
326 
327         // If not one of the types above, check whether an internal inset mapping is specified.
328         if (attrs.providedInsets != null) {
329             for (InsetsFrameProvider provider : attrs.providedInsets) {
330                 switch (provider.type) {
331                     case ITYPE_STATUS_BAR:
332                     case ITYPE_NAVIGATION_BAR:
333                     case ITYPE_CLIMATE_BAR:
334                     case ITYPE_EXTRA_NAVIGATION_BAR:
335                         return provider.type;
336                 }
337             }
338         }
339 
340         return ITYPE_INVALID;
341     }
342 
343 
344     /**
345      * Modifies the given {@code state} according to the {@code type} (Inset type) provided by
346      * the target.
347      * When performing layout of the target or dispatching insets to the target, we need to exclude
348      * sources which should not be visible to the target. e.g., the source which represents the
349      * target window itself, and the IME source when the target is above IME. We also need to
350      * exclude certain types of insets source for client within specific windowing modes.
351      *
352      * @param type the inset type provided by the target
353      * @param windowingMode the windowing mode of the target
354      * @param isAlwaysOnTop is the target always on top
355      * @param windowType the type of the target
356      * @param state the input inset state containing all the sources
357      * @return The state stripped of the necessary information.
358      */
enforceInsetsPolicyForTarget(@nternalInsetsType int type, @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, int windowType, InsetsState state)359     InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
360             @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
361             int windowType, InsetsState state) {
362         boolean stateCopied = false;
363 
364         if (type != ITYPE_INVALID) {
365             state = new InsetsState(state);
366             stateCopied = true;
367             state.removeSource(type);
368 
369             // Navigation bar doesn't get influenced by anything else
370             if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
371                 state.removeSource(ITYPE_STATUS_BAR);
372                 state.removeSource(ITYPE_CLIMATE_BAR);
373                 state.removeSource(ITYPE_CAPTION_BAR);
374                 state.removeSource(ITYPE_NAVIGATION_BAR);
375                 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
376             }
377 
378             // Status bar doesn't get influenced by caption bar
379             if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
380                 state.removeSource(ITYPE_CAPTION_BAR);
381             }
382         }
383         ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
384                 .getSourceProviders();
385         for (int i = providers.size() - 1; i >= 0; i--) {
386             WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
387             if (otherProvider.overridesFrame(windowType)) {
388                 if (!stateCopied) {
389                     state = new InsetsState(state);
390                     stateCopied = true;
391                 }
392                 InsetsSource override =
393                         new InsetsSource(state.getSource(otherProvider.getSource().getType()));
394                 override.setFrame(otherProvider.getOverriddenFrame(windowType));
395                 state.addSource(override);
396             }
397         }
398 
399         if (WindowConfiguration.isFloating(windowingMode)
400                 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
401             if (!stateCopied) {
402                 state = new InsetsState(state);
403                 stateCopied = true;
404             }
405             state.removeSource(ITYPE_STATUS_BAR);
406             state.removeSource(ITYPE_NAVIGATION_BAR);
407             state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
408             if (windowingMode == WINDOWING_MODE_PINNED) {
409                 state.removeSource(ITYPE_IME);
410             }
411         }
412 
413         return state;
414     }
415 
adjustVisibilityForTransientTypes(InsetsState originalState)416     private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
417         InsetsState state = originalState;
418         for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
419             final @InternalInsetsType int type = mShowingTransientTypes.get(i);
420             final InsetsSource originalSource = state.peekSource(type);
421             if (originalSource != null && originalSource.isVisible()) {
422                 if (state == originalState) {
423                     // The source will be modified, create a non-deep copy to store the new one.
424                     state = new InsetsState(originalState);
425                 }
426                 // Replace the source with a copy in invisible state.
427                 final InsetsSource source = new InsetsSource(originalSource);
428                 source.setVisible(false);
429                 state.addSource(source);
430             }
431         }
432         return state;
433     }
434 
adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState)435     private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
436             boolean copyState) {
437         if (w.mIsImWindow) {
438             // If navigation bar is not hidden by IME, IME should always receive visible
439             // navigation bar insets.
440             final boolean navVisible = !mHideNavBarForKeyboard;
441             final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
442             if (originalNavSource != null && originalNavSource.isVisible() != navVisible) {
443                 final InsetsState state = copyState ? new InsetsState(originalState)
444                         : originalState;
445                 final InsetsSource navSource = new InsetsSource(originalNavSource);
446                 navSource.setVisible(navVisible);
447                 state.addSource(navSource);
448                 return state;
449             }
450         } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
451             // During switching tasks with gestural navigation, before the next IME input target
452             // starts the input, we should adjust and freeze the last IME visibility of the window
453             // in case delivering obsoleted IME insets state during transitioning.
454             final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
455 
456             if (originalImeSource != null) {
457                 final boolean imeVisibility = w.getRequestedVisibility(ITYPE_IME);
458                 final InsetsState state = copyState ? new InsetsState(originalState)
459                         : originalState;
460                 final InsetsSource imeSource = new InsetsSource(originalImeSource);
461                 imeSource.setVisible(imeVisibility);
462                 state.addSource(imeSource);
463                 return state;
464             }
465         }
466         return originalState;
467     }
468 
adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState, boolean copyState)469     private InsetsState adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState,
470             boolean copyState) {
471         if (token != null) {
472             final ActivityRecord activityRecord = token.asActivityRecord();
473             final Task task = activityRecord != null ? activityRecord.getTask() : null;
474             if (task != null && !task.getWindowConfiguration().tasksAreFloating()) {
475                 // Use task bounds to calculating rounded corners if the task is not floating.
476                 final Rect roundedCornerFrame = new Rect(task.getBounds());
477                 final InsetsState state = copyState ? new InsetsState(originalState)
478                         : originalState;
479                 state.setRoundedCornerFrame(roundedCornerFrame);
480                 return state;
481             }
482         }
483         return originalState;
484     }
485 
onInsetsModified(InsetsControlTarget caller)486     void onInsetsModified(InsetsControlTarget caller) {
487         mStateController.onInsetsModified(caller);
488         checkAbortTransient(caller);
489         updateBarControlTarget(mFocusedWin);
490     }
491 
492     /**
493      * Called when a control target modified the insets state. If the target set a insets source to
494      * visible while it is shown transiently, we need to abort the transient state. While IME is
495      * requested visible, we also need to abort the transient state of navigation bar if it is shown
496      * transiently.
497      *
498      * @param caller who changed the insets state.
499      */
checkAbortTransient(InsetsControlTarget caller)500     private void checkAbortTransient(InsetsControlTarget caller) {
501         if (mShowingTransientTypes.size() != 0) {
502             final IntArray abortTypes = new IntArray();
503             final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME);
504             for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
505                 final @InternalInsetsType int type = mShowingTransientTypes.get(i);
506                 if ((mStateController.isFakeTarget(type, caller)
507                                 && caller.getRequestedVisibility(type))
508                         || (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) {
509                     mShowingTransientTypes.remove(i);
510                     abortTypes.add(type);
511                 }
512             }
513             StatusBarManagerInternal statusBarManagerInternal =
514                     mPolicy.getStatusBarManagerInternal();
515             if (abortTypes.size() > 0 && statusBarManagerInternal != null) {
516                 statusBarManagerInternal.abortTransient(
517                         mDisplayContent.getDisplayId(), abortTypes.toArray());
518             }
519         }
520     }
521 
522     /**
523      * If the caller is not {@link #updateBarControlTarget}, it should call
524      * updateBarControlTarget(mFocusedWin) after this invocation.
525      */
abortTransient()526     private void abortTransient() {
527         StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal();
528         if (statusBarManagerInternal != null) {
529             statusBarManagerInternal.abortTransient(
530                     mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
531         }
532         mShowingTransientTypes.clear();
533 
534         dispatchTransientSystemBarsVisibilityChanged(
535                 mFocusedWin,
536                 /* areVisible= */ false,
537                 /* wereRevealedFromSwipeOnSystemBar= */ false);
538     }
539 
getStatusControlTarget(@ullable WindowState focusedWin, boolean fake)540     private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
541             boolean fake) {
542         if (!fake && isShowingTransientTypes(Type.statusBars())) {
543             return mDummyControlTarget;
544         }
545         final WindowState notificationShade = mPolicy.getNotificationShade();
546         if (focusedWin == notificationShade) {
547             // Notification shade has control anyways, no reason to force anything.
548             return focusedWin;
549         }
550         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
551             ComponentName component = focusedWin.mActivityRecord != null
552                     ? focusedWin.mActivityRecord.mActivityComponent : null;
553             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
554                     component, focusedWin.getRequestedVisibilities());
555             return mDisplayContent.mRemoteInsetsControlTarget;
556         }
557         if (mPolicy.areSystemBarsForcedShownLw()) {
558             // Status bar is forcibly shown. We don't want the client to control the status bar, and
559             // we will dispatch the real visibility of status bar to the client.
560             return null;
561         }
562         if (forceShowsStatusBarTransiently() && !fake) {
563             // Status 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 mDummyControlTarget;
567         }
568         if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar()
569                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
570             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
571             // window hides status bar, unless the notification shade can receive keys.
572             return mPolicy.getTopFullscreenOpaqueWindow();
573         }
574         return focusedWin;
575     }
576 
canBeTopFullscreenOpaqueWindow(@ullable WindowState win)577     private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) {
578         // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may
579         // haven't drawn or committed the visibility.
580         final boolean nonAttachedAppWindow = win != null
581                 && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
582                 && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
583         return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent()
584                 && !win.inMultiWindowMode();
585     }
586 
getNavControlTarget(@ullable WindowState focusedWin, boolean fake)587     private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
588             boolean fake) {
589         final WindowState imeWin = mDisplayContent.mInputMethodWindow;
590         if (imeWin != null && imeWin.isVisible() && !mHideNavBarForKeyboard) {
591             // Force showing navigation bar while IME is visible and if navigation bar is not
592             // configured to be hidden by the IME.
593             return null;
594         }
595         if (!fake && isShowingTransientTypes(Type.navigationBars())) {
596             return mDummyControlTarget;
597         }
598         if (focusedWin == mPolicy.getNotificationShade()) {
599             // Notification shade has control anyways, no reason to force anything.
600             return focusedWin;
601         }
602         if (mPolicy.isForceShowNavigationBarEnabled() && focusedWin != null
603                 && focusedWin.getActivityType() == ACTIVITY_TYPE_STANDARD) {
604             // When "force show navigation bar" is enabled, it means both force visible is true, and
605             // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
606             // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
607             // still control the navigation bar in this mode.
608             return null;
609         }
610         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
611             ComponentName component = focusedWin.mActivityRecord != null
612                     ? focusedWin.mActivityRecord.mActivityComponent : null;
613             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
614                     component, focusedWin.getRequestedVisibilities());
615             return mDisplayContent.mRemoteInsetsControlTarget;
616         }
617         if (mPolicy.areSystemBarsForcedShownLw()) {
618             // Navigation bar is forcibly shown. We don't want the client to control the navigation
619             // bar, and we will dispatch the real visibility of navigation bar to the client.
620             return null;
621         }
622         if (forceShowsNavigationBarTransiently() && !fake) {
623             // Navigation bar is forcibly shown transiently, and its new visibility won't be
624             // dispatched to the client so that we can keep the layout stable. We will dispatch the
625             // fake control to the client, so that it can re-show the bar during this scenario.
626             return mDummyControlTarget;
627         }
628         return focusedWin;
629     }
630 
isShowingTransientTypes(@ype.InsetsType int types)631     private boolean isShowingTransientTypes(@Type.InsetsType int types) {
632         final IntArray showingTransientTypes = mShowingTransientTypes;
633         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
634             if ((InsetsState.toPublicType(showingTransientTypes.get(i)) & types) != 0) {
635                 return true;
636             }
637         }
638         return false;
639     }
640 
641     /**
642      * Determines whether the remote insets controller should take control of system bars for all
643      * windows.
644      */
remoteInsetsControllerControlsSystemBars(@ullable WindowState focusedWin)645     boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
646         if (focusedWin == null) {
647             return false;
648         }
649         if (!mRemoteInsetsControllerControlsSystemBars) {
650             return false;
651         }
652         if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
653             // No remote insets control target to take control of insets.
654             return false;
655         }
656         // If necessary, auto can control application windows when
657         // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
658         // where we want to dictate system bar inset state for applications.
659         return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
660                 && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
661     }
662 
forceShowsStatusBarTransiently()663     private boolean forceShowsStatusBarTransiently() {
664         final WindowState win = mPolicy.getStatusBar();
665         return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
666     }
667 
forceShowsNavigationBarTransiently()668     private boolean forceShowsNavigationBarTransiently() {
669         final WindowState win = mPolicy.getNotificationShade();
670         return win != null
671                 && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
672     }
673 
674     @VisibleForTesting
startAnimation(boolean show, Runnable callback)675     void startAnimation(boolean show, Runnable callback) {
676         int typesReady = 0;
677         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
678         final IntArray showingTransientTypes = mShowingTransientTypes;
679         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
680             final @InternalInsetsType int type = showingTransientTypes.get(i);
681             WindowContainerInsetsSourceProvider provider = mStateController.getSourceProvider(type);
682             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
683             if (control == null || control.getLeash() == null) {
684                 continue;
685             }
686             typesReady |= InsetsState.toPublicType(type);
687             controls.put(control.getType(), new InsetsSourceControl(control));
688         }
689         controlAnimationUnchecked(typesReady, controls, show, callback);
690     }
691 
controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback)692     private void controlAnimationUnchecked(int typesReady,
693             SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
694         InsetsPolicyAnimationControlListener listener =
695                 new InsetsPolicyAnimationControlListener(show, callback, typesReady);
696         listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
697     }
698 
dispatchTransientSystemBarsVisibilityChanged( @ullable WindowState focusedWindow, boolean areVisible, boolean wereRevealedFromSwipeOnSystemBar)699     private void dispatchTransientSystemBarsVisibilityChanged(
700             @Nullable WindowState focusedWindow,
701             boolean areVisible,
702             boolean wereRevealedFromSwipeOnSystemBar) {
703         if (focusedWindow == null) {
704             return;
705         }
706 
707         Task task = focusedWindow.getTask();
708         if (task == null) {
709             return;
710         }
711 
712         int taskId = task.mTaskId;
713         boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID;
714         if (!isValidTaskId) {
715             return;
716         }
717 
718         mDisplayContent.mWmService.mTaskSystemBarsListenerController
719                 .dispatchTransientSystemBarVisibilityChanged(
720                         taskId,
721                         areVisible,
722                         wereRevealedFromSwipeOnSystemBar);
723     }
724 
725     private class BarWindow {
726 
727         private final int mId;
728         private  @StatusBarManager.WindowVisibleState int mState =
729                 StatusBarManager.WINDOW_STATE_SHOWING;
730 
BarWindow(int id)731         BarWindow(int id) {
732             mId = id;
733         }
734 
updateVisibility(@ullable InsetsControlTarget controlTarget, @InternalInsetsType int type)735         private void updateVisibility(@Nullable InsetsControlTarget controlTarget,
736                 @InternalInsetsType int type) {
737             setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type));
738         }
739 
setVisible(boolean visible)740         private void setVisible(boolean visible) {
741             final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
742             if (mState != state) {
743                 mState = state;
744                 StatusBarManagerInternal statusBarManagerInternal =
745                         mPolicy.getStatusBarManagerInternal();
746                 if (statusBarManagerInternal != null) {
747                     statusBarManagerInternal.setWindowState(
748                             mDisplayContent.getDisplayId(), mId, state);
749                 }
750             }
751         }
752     }
753 
754     private class InsetsPolicyAnimationControlListener extends
755             InsetsController.InternalAnimationControlListener {
756         Runnable mFinishCallback;
757         InsetsPolicyAnimationControlCallbacks mControlCallbacks;
758 
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types)759         InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
760             super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
761                     false /* disable */, 0 /* floatingImeBottomInsets */, null);
762             mFinishCallback = finishCallback;
763             mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
764         }
765 
766         @Override
onAnimationFinish()767         protected void onAnimationFinish() {
768             super.onAnimationFinish();
769             if (mFinishCallback != null) {
770                 DisplayThread.getHandler().post(mFinishCallback);
771             }
772         }
773 
774         private class InsetsPolicyAnimationControlCallbacks implements
775                 InsetsAnimationControlCallbacks {
776             private InsetsAnimationControlImpl mAnimationControl = null;
777             private InsetsPolicyAnimationControlListener mListener;
778 
InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener)779             InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
780                 mListener = listener;
781             }
782 
controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show)783             private void controlAnimationUnchecked(int typesReady,
784                     SparseArray<InsetsSourceControl> controls, boolean show) {
785                 if (typesReady == 0) {
786                     // nothing to animate.
787                     return;
788                 }
789                 mAnimatingShown = show;
790 
791                 final InsetsState state = mFocusedWin.getInsetsState();
792 
793                 // We are about to playing the default animation. Passing a null frame indicates
794                 // the controlled types should be animated regardless of the frame.
795                 mAnimationControl = new InsetsAnimationControlImpl(controls,
796                         null /* frame */, state, mListener, typesReady, this,
797                         mListener.getDurationMs(), getInsetsInterpolator(),
798                         show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
799                                 ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
800                                 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
801                         null /* translator */);
802                 SurfaceAnimationThread.getHandler().post(
803                         () -> mListener.onReady(mAnimationControl, typesReady));
804             }
805 
806             /** Called on SurfaceAnimationThread without global WM lock held. */
807             @Override
scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)808             public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
809                 if (mAnimationControl.applyChangeInsets(null /* outState */)) {
810                     mAnimationControl.finish(mAnimatingShown);
811                 }
812             }
813 
814             @Override
notifyFinished(InsetsAnimationControlRunner runner, boolean shown)815             public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
816                 // Nothing's needed here. Finish steps is handled in the listener
817                 // onAnimationFinished callback.
818             }
819 
820             /** Called on SurfaceAnimationThread without global WM lock held. */
821             @Override
applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params)822             public void applySurfaceParams(
823                     final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
824                 SurfaceControl.Transaction t = new SurfaceControl.Transaction();
825                 for (int i = params.length - 1; i >= 0; i--) {
826                     SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
827                     applyParams(t, surfaceParams, mTmpFloat9);
828                 }
829                 t.apply();
830                 t.close();
831             }
832 
833             // Since we don't push applySurfaceParams to a Handler-queue we don't need
834             // to push release in this case.
835             @Override
releaseSurfaceControlFromRt(SurfaceControl sc)836             public void releaseSurfaceControlFromRt(SurfaceControl sc) {
837                 sc.release();
838             }
839 
840             @Override
841             public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)842             void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
843                     WindowInsetsAnimation animation,
844                     Bounds bounds) {
845             }
846 
847             @Override
reportPerceptible(int types, boolean perceptible)848             public void reportPerceptible(int types, boolean perceptible) {
849                 // No-op for now - only client windows report perceptibility for now, with policy
850                 // controllers assumed to always be perceptible.
851             }
852         }
853     }
854 }
855