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.view.InsetsController.ANIMATION_TYPE_HIDE;
22 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
23 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
24 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
25 import static android.view.InsetsState.ITYPE_IME;
26 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
27 import static android.view.InsetsState.ITYPE_STATUS_BAR;
28 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
29 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
30 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
31 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
32 
33 import android.annotation.NonNull;
34 import android.annotation.Nullable;
35 import android.app.StatusBarManager;
36 import android.util.IntArray;
37 import android.util.SparseArray;
38 import android.view.InsetsAnimationControlCallbacks;
39 import android.view.InsetsAnimationControlImpl;
40 import android.view.InsetsAnimationControlRunner;
41 import android.view.InsetsController;
42 import android.view.InsetsSource;
43 import android.view.InsetsSourceControl;
44 import android.view.InsetsState;
45 import android.view.InsetsState.InternalInsetsType;
46 import android.view.InternalInsetsAnimationController;
47 import android.view.SurfaceControl;
48 import android.view.SyncRtSurfaceTransactionApplier;
49 import android.view.WindowInsets.Type;
50 import android.view.WindowInsetsAnimation;
51 import android.view.WindowInsetsAnimation.Bounds;
52 import android.view.WindowInsetsAnimationControlListener;
53 import android.view.WindowInsetsAnimationController;
54 import android.view.WindowManager;
55 
56 import com.android.internal.R;
57 import com.android.internal.annotations.VisibleForTesting;
58 import com.android.server.DisplayThread;
59 import com.android.server.statusbar.StatusBarManagerInternal;
60 
61 /**
62  * Policy that implements who gets control over the windows generating insets.
63  */
64 class InsetsPolicy {
65 
66     private final InsetsStateController mStateController;
67     private final DisplayContent mDisplayContent;
68     private final DisplayPolicy mPolicy;
69     private final IntArray mShowingTransientTypes = new IntArray();
70 
71     /** For resetting visibilities of insets sources. */
72     private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() {
73 
74         @Override
75         public void notifyInsetsControlChanged() {
76             boolean hasLeash = false;
77             final InsetsSourceControl[] controls =
78                     mStateController.getControlsForDispatch(this);
79             if (controls == null) {
80                 return;
81             }
82             for (InsetsSourceControl control : controls) {
83                 final @InternalInsetsType int type = control.getType();
84                 if (mShowingTransientTypes.indexOf(type) != -1) {
85                     // The visibilities of transient bars will be handled with animations.
86                     continue;
87                 }
88                 final SurfaceControl leash = control.getLeash();
89                 if (leash != null) {
90                     hasLeash = true;
91 
92                     // We use alpha to control the visibility here which aligns the logic at
93                     // SurfaceAnimator.createAnimationLeash
94                     mDisplayContent.getPendingTransaction().setAlpha(
95                             leash, InsetsState.getDefaultVisibility(type) ? 1f : 0f);
96                 }
97             }
98             if (hasLeash) {
99                 mDisplayContent.scheduleAnimation();
100             }
101         }
102     };
103 
104     private WindowState mFocusedWin;
105     private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
106     private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
107     private boolean mAnimatingShown;
108     /**
109      * Let remote insets controller control system bars regardless of other settings.
110      */
111     private boolean mRemoteInsetsControllerControlsSystemBars;
112     private final float[] mTmpFloat9 = new float[9];
113 
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent)114     InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
115         mStateController = stateController;
116         mDisplayContent = displayContent;
117         mPolicy = displayContent.getDisplayPolicy();
118         mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
119                 R.bool.config_remoteInsetsControllerControlsSystemBars);
120     }
121 
getRemoteInsetsControllerControlsSystemBars()122     boolean getRemoteInsetsControllerControlsSystemBars() {
123         return mRemoteInsetsControllerControlsSystemBars;
124     }
125 
126     /**
127      * Used only for testing.
128      */
129     @VisibleForTesting
setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars)130     void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
131         mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
132     }
133 
134     /** Updates the target which can control system bars. */
updateBarControlTarget(@ullable WindowState focusedWin)135     void updateBarControlTarget(@Nullable WindowState focusedWin) {
136         if (mFocusedWin != focusedWin){
137             abortTransient();
138         }
139         mFocusedWin = focusedWin;
140         final InsetsControlTarget statusControlTarget =
141                 getStatusControlTarget(focusedWin, false /* fake */);
142         final InsetsControlTarget navControlTarget =
143                 getNavControlTarget(focusedWin, false /* fake */);
144         mStateController.onBarControlTargetChanged(
145                 statusControlTarget,
146                 statusControlTarget == mDummyControlTarget
147                         ? getStatusControlTarget(focusedWin, true /* fake */)
148                         : null,
149                 navControlTarget,
150                 navControlTarget == mDummyControlTarget
151                         ? getNavControlTarget(focusedWin, true /* fake */)
152                         : null);
153         mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
154         mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
155     }
156 
isHidden(@nternalInsetsType int type)157     boolean isHidden(@InternalInsetsType int type) {
158         final InsetsSourceProvider provider =  mStateController.peekSourceProvider(type);
159         return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
160     }
161 
showTransient(@nternalInsetsType int[] types, boolean isGestureOnSystemBar)162     void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
163         boolean changed = false;
164         for (int i = types.length - 1; i >= 0; i--) {
165             final @InternalInsetsType int type = types[i];
166             if (!isHidden(type)) {
167                 continue;
168             }
169             if (mShowingTransientTypes.indexOf(type) != -1) {
170                 continue;
171             }
172             mShowingTransientTypes.add(type);
173             changed = true;
174         }
175         if (changed) {
176             StatusBarManagerInternal statusBarManagerInternal =
177                     mPolicy.getStatusBarManagerInternal();
178             if (statusBarManagerInternal != null) {
179                 statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
180                         mShowingTransientTypes.toArray(), isGestureOnSystemBar);
181             }
182             updateBarControlTarget(mFocusedWin);
183 
184             // The leashes can be created while updating bar control target. The surface transaction
185             // of the new leashes might not be applied yet. The callback posted here ensures we can
186             // get the valid leashes because the surface transaction will be applied in the next
187             // animation frame which will be triggered if a new leash is created.
188             mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> {
189                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
190                     startAnimation(true /* show */, null /* callback */);
191                 }
192             });
193         }
194     }
195 
hideTransient()196     void hideTransient() {
197         if (mShowingTransientTypes.size() == 0) {
198             return;
199         }
200         startAnimation(false /* show */, () -> {
201             synchronized (mDisplayContent.mWmService.mGlobalLock) {
202                 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
203                     // We are about to clear mShowingTransientTypes, we don't want the transient bar
204                     // can cause insets on the client. Restore the client visibility.
205                     final @InternalInsetsType int type = mShowingTransientTypes.get(i);
206                     mStateController.getSourceProvider(type).setClientVisible(false);
207                 }
208                 mShowingTransientTypes.clear();
209                 updateBarControlTarget(mFocusedWin);
210             }
211         });
212     }
213 
isTransient(@nternalInsetsType int type)214     boolean isTransient(@InternalInsetsType int type) {
215         return mShowingTransientTypes.indexOf(type) != -1;
216     }
217 
218     /**
219      * @see InsetsStateController#getInsetsForWindow
220      */
getInsetsForWindow(WindowState target)221     InsetsState getInsetsForWindow(WindowState target) {
222         final InsetsState originalState = mStateController.getInsetsForWindow(target);
223         final InsetsState state = adjustVisibilityForTransientTypes(originalState);
224         return adjustVisibilityForIme(target, state, state == originalState);
225     }
226 
227     /**
228      * @see InsetsStateController#getInsetsForWindowMetrics
229      */
getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)230     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
231         final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs);
232         return adjustVisibilityForTransientTypes(originalState);
233     }
234 
adjustVisibilityForTransientTypes(InsetsState originalState)235     private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) {
236         InsetsState state = originalState;
237         for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
238             final @InternalInsetsType int type = mShowingTransientTypes.get(i);
239             final InsetsSource originalSource = state.peekSource(type);
240             if (originalSource != null && originalSource.isVisible()) {
241                 if (state == originalState) {
242                     // The source will be modified, create a non-deep copy to store the new one.
243                     state = new InsetsState(originalState);
244                 }
245                 // Replace the source with a copy in invisible state.
246                 final InsetsSource source = new InsetsSource(originalSource);
247                 source.setVisible(false);
248                 state.addSource(source);
249             }
250         }
251         return state;
252     }
253 
adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState)254     private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
255             boolean copyState) {
256         if (w.mIsImWindow) {
257             // Navigation bar insets is always visible to IME.
258             final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
259             if (originalNavSource != null && !originalNavSource.isVisible()) {
260                 final InsetsState state = copyState ? new InsetsState(originalState)
261                         : originalState;
262                 final InsetsSource navSource = new InsetsSource(originalNavSource);
263                 navSource.setVisible(true);
264                 state.addSource(navSource);
265                 return state;
266             }
267         } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
268             // During switching tasks with gestural navigation, if the IME is attached to
269             // one app window on that time, even the next app window is behind the IME window,
270             // conceptually the window should not receive the IME insets if the next window is
271             // not eligible IME requester and ready to show IME on top of it.
272             final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
273             final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
274 
275             if (shouldImeAttachedToApp && originalImeSource != null) {
276                 final boolean imeVisibility =
277                         w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME);
278                 final InsetsState state = copyState ? new InsetsState(originalState)
279                         : originalState;
280                 final InsetsSource imeSource = new InsetsSource(originalImeSource);
281                 imeSource.setVisible(imeVisibility);
282                 state.addSource(imeSource);
283                 return state;
284             }
285         }
286         return originalState;
287     }
288 
onInsetsModified(InsetsControlTarget caller)289     void onInsetsModified(InsetsControlTarget caller) {
290         mStateController.onInsetsModified(caller);
291         checkAbortTransient(caller);
292         updateBarControlTarget(mFocusedWin);
293     }
294 
295     /**
296      * Called when a control target modified the insets state. If the target set a insets source to
297      * visible while it is shown transiently, we need to abort the transient state. While IME is
298      * requested visible, we also need to abort the transient state of navigation bar if it is shown
299      * transiently.
300      *
301      * @param caller who changed the insets state.
302      */
checkAbortTransient(InsetsControlTarget caller)303     private void checkAbortTransient(InsetsControlTarget caller) {
304         if (mShowingTransientTypes.size() != 0) {
305             final IntArray abortTypes = new IntArray();
306             final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME);
307             for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
308                 final @InternalInsetsType int type = mShowingTransientTypes.get(i);
309                 if ((mStateController.isFakeTarget(type, caller)
310                                 && caller.getRequestedVisibility(type))
311                         || (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) {
312                     mShowingTransientTypes.remove(i);
313                     abortTypes.add(type);
314                 }
315             }
316             StatusBarManagerInternal statusBarManagerInternal =
317                     mPolicy.getStatusBarManagerInternal();
318             if (abortTypes.size() > 0 && statusBarManagerInternal != null) {
319                 statusBarManagerInternal.abortTransient(
320                         mDisplayContent.getDisplayId(), abortTypes.toArray());
321             }
322         }
323     }
324 
325     /**
326      * If the caller is not {@link #updateBarControlTarget}, it should call
327      * updateBarControlTarget(mFocusedWin) after this invocation.
328      */
abortTransient()329     private void abortTransient() {
330         StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal();
331         if (statusBarManagerInternal != null) {
332             statusBarManagerInternal.abortTransient(
333                     mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
334         }
335         mShowingTransientTypes.clear();
336     }
337 
getStatusControlTarget(@ullable WindowState focusedWin, boolean fake)338     private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
339             boolean fake) {
340         if (!fake && isShowingTransientTypes(Type.statusBars())) {
341             return mDummyControlTarget;
342         }
343         final WindowState notificationShade = mPolicy.getNotificationShade();
344         if (focusedWin == notificationShade) {
345             // Notification shade has control anyways, no reason to force anything.
346             return focusedWin;
347         }
348         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
349             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
350                     focusedWin.mAttrs.packageName);
351             return mDisplayContent.mRemoteInsetsControlTarget;
352         }
353         if (mPolicy.areSystemBarsForcedShownLw()) {
354             // Status bar is forcibly shown. We don't want the client to control the status bar, and
355             // we will dispatch the real visibility of status bar to the client.
356             return null;
357         }
358         if (forceShowsStatusBarTransiently() && !fake) {
359             // Status bar is forcibly shown transiently, and its new visibility won't be
360             // dispatched to the client so that we can keep the layout stable. We will dispatch the
361             // fake control to the client, so that it can re-show the bar during this scenario.
362             return mDummyControlTarget;
363         }
364         if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar()
365                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
366             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
367             // window hides status bar, unless the notification shade can receive keys.
368             return mPolicy.getTopFullscreenOpaqueWindow();
369         }
370         return focusedWin;
371     }
372 
canBeTopFullscreenOpaqueWindow(@ullable WindowState win)373     private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) {
374         // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may
375         // haven't drawn or committed the visibility.
376         final boolean nonAttachedAppWindow = win != null
377                 && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
378                 && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
379         return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent()
380                 && !win.inMultiWindowMode();
381     }
382 
getNavControlTarget(@ullable WindowState focusedWin, boolean fake)383     private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
384             boolean fake) {
385         final WindowState imeWin = mDisplayContent.mInputMethodWindow;
386         if (imeWin != null && imeWin.isVisible()) {
387             // Force showing navigation bar while IME is visible.
388             return null;
389         }
390         if (!fake && isShowingTransientTypes(Type.navigationBars())) {
391             return mDummyControlTarget;
392         }
393         if (focusedWin == mPolicy.getNotificationShade()) {
394             // Notification shade has control anyways, no reason to force anything.
395             return focusedWin;
396         }
397         if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
398             mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
399                     focusedWin.mAttrs.packageName);
400             return mDisplayContent.mRemoteInsetsControlTarget;
401         }
402         if (mPolicy.areSystemBarsForcedShownLw()) {
403             // Navigation bar is forcibly shown. We don't want the client to control the navigation
404             // bar, and we will dispatch the real visibility of navigation bar to the client.
405             return null;
406         }
407         if (forceShowsNavigationBarTransiently() && !fake) {
408             // Navigation bar is forcibly shown transiently, and its new visibility won't be
409             // dispatched to the client so that we can keep the layout stable. We will dispatch the
410             // fake control to the client, so that it can re-show the bar during this scenario.
411             return mDummyControlTarget;
412         }
413         return focusedWin;
414     }
415 
isShowingTransientTypes(@ype.InsetsType int types)416     private boolean isShowingTransientTypes(@Type.InsetsType int types) {
417         final IntArray showingTransientTypes = mShowingTransientTypes;
418         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
419             if ((InsetsState.toPublicType(showingTransientTypes.get(i)) & types) != 0) {
420                 return true;
421             }
422         }
423         return false;
424     }
425 
426     /**
427      * Determines whether the remote insets controller should take control of system bars for all
428      * windows.
429      */
remoteInsetsControllerControlsSystemBars(@ullable WindowState focusedWin)430     boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
431         if (focusedWin == null) {
432             return false;
433         }
434         if (!mRemoteInsetsControllerControlsSystemBars) {
435             return false;
436         }
437         if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
438             // No remote insets control target to take control of insets.
439             return false;
440         }
441         // If necessary, auto can control application windows when
442         // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
443         // where we want to dictate system bar inset state for applications.
444         return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
445                 && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
446     }
447 
forceShowsStatusBarTransiently()448     private boolean forceShowsStatusBarTransiently() {
449         final WindowState win = mPolicy.getStatusBar();
450         return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
451     }
452 
forceShowsNavigationBarTransiently()453     private boolean forceShowsNavigationBarTransiently() {
454         final WindowState win = mPolicy.getNotificationShade();
455         return win != null
456                 && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
457     }
458 
459     @VisibleForTesting
startAnimation(boolean show, Runnable callback)460     void startAnimation(boolean show, Runnable callback) {
461         int typesReady = 0;
462         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
463         final IntArray showingTransientTypes = mShowingTransientTypes;
464         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
465             final @InternalInsetsType int type = showingTransientTypes.get(i);
466             InsetsSourceProvider provider = mStateController.getSourceProvider(type);
467             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
468             if (control == null || control.getLeash() == null) {
469                 continue;
470             }
471             typesReady |= InsetsState.toPublicType(type);
472             controls.put(control.getType(), new InsetsSourceControl(control));
473         }
474         controlAnimationUnchecked(typesReady, controls, show, callback);
475     }
476 
controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback)477     private void controlAnimationUnchecked(int typesReady,
478             SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
479         InsetsPolicyAnimationControlListener listener =
480                 new InsetsPolicyAnimationControlListener(show, callback, typesReady);
481         listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
482     }
483 
484     private class BarWindow {
485 
486         private final int mId;
487         private  @StatusBarManager.WindowVisibleState int mState =
488                 StatusBarManager.WINDOW_STATE_SHOWING;
489 
BarWindow(int id)490         BarWindow(int id) {
491             mId = id;
492         }
493 
updateVisibility(@ullable InsetsControlTarget controlTarget, @InternalInsetsType int type)494         private void updateVisibility(@Nullable InsetsControlTarget controlTarget,
495                 @InternalInsetsType int type) {
496             setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type));
497         }
498 
setVisible(boolean visible)499         private void setVisible(boolean visible) {
500             final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
501             if (mState != state) {
502                 mState = state;
503                 StatusBarManagerInternal statusBarManagerInternal =
504                         mPolicy.getStatusBarManagerInternal();
505                 if (statusBarManagerInternal != null) {
506                     statusBarManagerInternal.setWindowState(
507                             mDisplayContent.getDisplayId(), mId, state);
508                 }
509             }
510         }
511     }
512 
513     private class InsetsPolicyAnimationControlListener extends
514             InsetsController.InternalAnimationControlListener {
515         Runnable mFinishCallback;
516         InsetsPolicyAnimationControlCallbacks mControlCallbacks;
517 
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types)518         InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
519             super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
520                     false /* disable */, 0 /* floatingImeBottomInsets */);
521             mFinishCallback = finishCallback;
522             mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
523         }
524 
525         @Override
onAnimationFinish()526         protected void onAnimationFinish() {
527             super.onAnimationFinish();
528             if (mFinishCallback != null) {
529                 DisplayThread.getHandler().post(mFinishCallback);
530             }
531         }
532 
533         private class InsetsPolicyAnimationControlCallbacks implements
534                 InsetsAnimationControlCallbacks {
535             private InsetsAnimationControlImpl mAnimationControl = null;
536             private InsetsPolicyAnimationControlListener mListener;
537 
InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener)538             InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
539                 mListener = listener;
540             }
541 
controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show)542             private void controlAnimationUnchecked(int typesReady,
543                     SparseArray<InsetsSourceControl> controls, boolean show) {
544                 if (typesReady == 0) {
545                     // nothing to animate.
546                     return;
547                 }
548                 mAnimatingShown = show;
549 
550                 final InsetsState state = getInsetsForWindow(mFocusedWin);
551 
552                 // We are about to playing the default animation. Passing a null frame indicates
553                 // the controlled types should be animated regardless of the frame.
554                 mAnimationControl = new InsetsAnimationControlImpl(controls,
555                         null /* frame */, state, mListener, typesReady, this,
556                         mListener.getDurationMs(), getInsetsInterpolator(),
557                         show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show
558                                 ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
559                                 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
560                         null /* translator */);
561                 SurfaceAnimationThread.getHandler().post(
562                         () -> mListener.onReady(mAnimationControl, typesReady));
563             }
564 
565             /** Called on SurfaceAnimationThread without global WM lock held. */
566             @Override
scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)567             public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
568                 if (mAnimationControl.applyChangeInsets(null /* outState */)) {
569                     mAnimationControl.finish(mAnimatingShown);
570                 }
571             }
572 
573             @Override
notifyFinished(InsetsAnimationControlRunner runner, boolean shown)574             public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
575                 // Nothing's needed here. Finish steps is handled in the listener
576                 // onAnimationFinished callback.
577             }
578 
579             /** Called on SurfaceAnimationThread without global WM lock held. */
580             @Override
applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params)581             public void applySurfaceParams(
582                     final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
583                 SurfaceControl.Transaction t = new SurfaceControl.Transaction();
584                 for (int i = params.length - 1; i >= 0; i--) {
585                     SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
586                     applyParams(t, surfaceParams, mTmpFloat9);
587                 }
588                 t.apply();
589                 t.close();
590             }
591 
592             // Since we don't push applySurfaceParams to a Handler-queue we don't need
593             // to push release in this case.
594             @Override
releaseSurfaceControlFromRt(SurfaceControl sc)595             public void releaseSurfaceControlFromRt(SurfaceControl sc) {
596                 sc.release();
597             }
598 
599             @Override
600             public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)601             void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
602                     WindowInsetsAnimation animation,
603                     Bounds bounds) {
604             }
605 
606             @Override
reportPerceptible(int types, boolean perceptible)607             public void reportPerceptible(int types, boolean perceptible) {
608                 // No-op for now - only client windows report perceptibility for now, with policy
609                 // controllers assumed to always be perceptible.
610             }
611         }
612     }
613 }
614