diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index e5d031b7a51c4..beaa99fcef4c0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -841,7 +841,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return mLastDispatchedState; } - @VisibleForTesting public boolean onStateChanged(InsetsState state) { boolean stateChanged = false; if (!CAPTION_ON_SHELL) { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 4620266c981e0..835c92d0a30de 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -21,12 +21,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.view.InsetsController.ANIMATION_TYPE_HIDE; -import static android.view.InsetsController.ANIMATION_TYPE_SHOW; -import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; -import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; import static android.view.InsetsSource.ID_IME; -import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; @@ -39,16 +34,13 @@ import android.app.StatusBarManager; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.res.Resources; +import android.os.Handler; +import android.os.IBinder; import android.util.SparseArray; -import android.view.InsetsAnimationControlCallbacks; -import android.view.InsetsAnimationControlImpl; -import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; import android.view.InsetsFrameProvider; import android.view.InsetsSource; -import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.InternalInsetsAnimationController; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.WindowInsets; @@ -56,15 +48,15 @@ import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; -import android.view.WindowInsetsAnimationControlListener; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.DisplayThread; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; +import java.util.List; /** * Policy that implements who gets control over the windows generating insets. @@ -79,48 +71,19 @@ class InsetsPolicy { private final DisplayContent mDisplayContent; private final DisplayPolicy mPolicy; - /** For resetting visibilities of insets sources. */ - private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { + /** Used to show system bars transiently. This won't affect the layout. */ + private final InsetsControlTarget mTransientControlTarget; - @Override - public void notifyInsetsControlChanged() { - boolean hasLeash = false; - final InsetsSourceControl[] controls = - mStateController.getControlsForDispatch(this); - if (controls == null) { - return; - } - for (InsetsSourceControl control : controls) { - if (isTransient(control.getType())) { - // The visibilities of transient bars will be handled with animations. - continue; - } - final SurfaceControl leash = control.getLeash(); - if (leash != null) { - hasLeash = true; - - // We use alpha to control the visibility here which aligns the logic at - // SurfaceAnimator.createAnimationLeash - final boolean visible = - (control.getType() & WindowInsets.Type.defaultVisible()) != 0; - mDisplayContent.getPendingTransaction().setAlpha(leash, visible ? 1f : 0f); - } - } - if (hasLeash) { - mDisplayContent.scheduleAnimation(); - } - } - }; + /** Used to show system bars permanently. This will affect the layout. */ + private final InsetsControlTarget mPermanentControlTarget; private WindowState mFocusedWin; private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); private @InsetsType int mShowingTransientTypes; private @InsetsType int mForcedShowingTypes; - private boolean mAnimatingShown; private final boolean mHideNavBarForKeyboard; - private final float[] mTmpFloat9 = new float[9]; InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { mStateController = stateController; @@ -128,6 +91,10 @@ class InsetsPolicy { mPolicy = displayContent.getDisplayPolicy(); final Resources r = mPolicy.getContext().getResources(); mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); + mTransientControlTarget = new ControlTarget( + stateController, displayContent.mWmService.mH, "TransientControlTarget"); + mPermanentControlTarget = new ControlTarget( + stateController, displayContent.mWmService.mH, "PermanentControlTarget"); } /** Updates the target which can control system bars. */ @@ -144,13 +111,13 @@ class InsetsPolicy { final WindowState topApp = mPolicy.getTopFullscreenOpaqueWindow(); mStateController.onBarControlTargetChanged( statusControlTarget, - statusControlTarget == mDummyControlTarget + statusControlTarget == mTransientControlTarget ? getStatusControlTarget(focusedWin, true /* fake */) : statusControlTarget == notificationShade ? getStatusControlTarget(topApp, true /* fake */) : null, navControlTarget, - navControlTarget == mDummyControlTarget + navControlTarget == mTransientControlTarget ? getNavControlTarget(focusedWin, true /* fake */) : navControlTarget == notificationShade ? getNavControlTarget(topApp, true /* fake */) @@ -200,19 +167,19 @@ class InsetsPolicy { mFocusedWin, (showingTransientTypes & (Type.statusBars() | Type.navigationBars())) != 0, isGestureOnSystemBar); - - // The leashes can be created while updating bar control target. The surface transaction - // of the new leashes might not be applied yet. The callback posted here ensures we can - // get the valid leashes because the surface transaction will be applied in the next - // animation frame which will be triggered if a new leash is created. - mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { - synchronized (mDisplayContent.mWmService.mGlobalLock) { - startAnimation(true /* show */, null /* callback */); - } - }); } } + @VisibleForTesting + InsetsControlTarget getTransientControlTarget() { + return mTransientControlTarget; + } + + @VisibleForTesting + InsetsControlTarget getPermanentControlTarget() { + return mPermanentControlTarget; + } + void hideTransient() { if (mShowingTransientTypes == 0) { return; @@ -223,23 +190,8 @@ class InsetsPolicy { /* areVisible= */ false, /* wereRevealedFromSwipeOnSystemBar= */ false); - startAnimation(false /* show */, () -> { - synchronized (mDisplayContent.mWmService.mGlobalLock) { - final SparseArray providers = - mStateController.getSourceProviders(); - for (int i = providers.size() - 1; i >= 0; i--) { - final InsetsSourceProvider provider = providers.valueAt(i); - if (!isTransient(provider.getSource().getType())) { - continue; - } - // We are about to clear mShowingTransientTypes, we don't want the transient bar - // can cause insets on the client. Restore the client visibility. - provider.setClientVisible(false); - } - mShowingTransientTypes = 0; - updateBarControlTarget(mFocusedWin); - } - }); + mShowingTransientTypes = 0; + updateBarControlTarget(mFocusedWin); } boolean isTransient(@InsetsType int type) { @@ -502,7 +454,7 @@ class InsetsPolicy { private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, boolean fake) { if (!fake && isTransient(Type.statusBars())) { - return mDummyControlTarget; + return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (focusedWin == notificationShade) { @@ -519,13 +471,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.statusBars())) { // Status bar is forcibly shown. We don't want the client to control the status bar, and // we will dispatch the real visibility of status bar to the client. - return null; + return mPermanentControlTarget; } if (forceShowsStatusBarTransiently() && !fake) { // Status bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. - return mDummyControlTarget; + return mTransientControlTarget; } if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesSystemBar(Type.statusBars()) @@ -556,7 +508,7 @@ class InsetsPolicy { return null; } if (!fake && isTransient(Type.navigationBars())) { - return mDummyControlTarget; + return mTransientControlTarget; } if (focusedWin == mPolicy.getNotificationShade()) { // Notification shade has control anyways, no reason to force anything. @@ -579,13 +531,13 @@ class InsetsPolicy { if (areTypesForciblyShowing(Type.navigationBars())) { // Navigation bar is forcibly shown. We don't want the client to control the navigation // bar, and we will dispatch the real visibility of navigation bar to the client. - return null; + return mPermanentControlTarget; } if (forceShowsNavigationBarTransiently() && !fake) { // Navigation bar is forcibly shown transiently, and its new visibility won't be // dispatched to the client so that we can keep the layout stable. We will dispatch the // fake control to the client, so that it can re-show the bar during this scenario. - return mDummyControlTarget; + return mTransientControlTarget; } final WindowState notificationShade = mPolicy.getNotificationShade(); if (!canBeTopFullscreenOpaqueWindow(focusedWin) @@ -662,34 +614,6 @@ class InsetsPolicy { && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; } - @VisibleForTesting - void startAnimation(boolean show, Runnable callback) { - @InsetsType int typesReady = 0; - final SparseArray controlsReady = new SparseArray<>(); - final InsetsSourceControl[] controls = - mStateController.getControlsForDispatch(mDummyControlTarget); - if (controls == null) { - if (callback != null) { - DisplayThread.getHandler().post(callback); - } - return; - } - for (InsetsSourceControl control : controls) { - if (isTransient(control.getType()) && control.getLeash() != null) { - typesReady |= control.getType(); - controlsReady.put(control.getId(), new InsetsSourceControl(control)); - } - } - controlAnimationUnchecked(typesReady, controlsReady, show, callback); - } - - private void controlAnimationUnchecked(int typesReady, - SparseArray controls, boolean show, Runnable callback) { - InsetsPolicyAnimationControlListener listener = - new InsetsPolicyAnimationControlListener(show, callback, typesReady); - listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); - } - private void dispatchTransientSystemBarsVisibilityChanged( @Nullable WindowState focusedWindow, boolean areVisible, @@ -760,105 +684,138 @@ class InsetsPolicy { } } - private class InsetsPolicyAnimationControlListener extends - InsetsController.InternalAnimationControlListener { - Runnable mFinishCallback; - InsetsPolicyAnimationControlCallbacks mControlCallbacks; + private static class ControlTarget implements InsetsControlTarget { - InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { - super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, - false /* disable */, 0 /* floatingImeBottomInsets */, - null /* loggingListener */, null /* jankContext */); - mFinishCallback = finishCallback; - mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); + private final InsetsState mState = new InsetsState(); + private final InsetsController mInsetsController; + private final InsetsStateController mStateController; + private final String mName; + + ControlTarget(InsetsStateController stateController, Handler handler, String name) { + mStateController = stateController; + mInsetsController = new InsetsController(new Host(handler, name)); + mName = name; } @Override - protected void onAnimationFinish() { - super.onAnimationFinish(); - if (mFinishCallback != null) { - DisplayThread.getHandler().post(mFinishCallback); - } + public void notifyInsetsControlChanged() { + mState.set(mStateController.getRawInsetsState(), true /* copySources */); + mInsetsController.onStateChanged(mState); + mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); } - private class InsetsPolicyAnimationControlCallbacks implements - InsetsAnimationControlCallbacks { - private InsetsAnimationControlImpl mAnimationControl = null; - private InsetsPolicyAnimationControlListener mListener; + @Override + public String toString() { + return mName; + } + } - InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { - mListener = listener; + private static class Host implements InsetsController.Host { + + private final float[] mTmpFloat9 = new float[9]; + private final Handler mHandler; + private final String mName; + + Host(Handler handler, String name) { + mHandler = handler; + mName = name; + } + + @Override + public Handler getHandler() { + return mHandler; + } + + @Override + public void notifyInsetsChanged() { } + + @Override + public void dispatchWindowInsetsAnimationPrepare( + @NonNull WindowInsetsAnimation animation) { } + + @Override + public Bounds dispatchWindowInsetsAnimationStart( + @NonNull WindowInsetsAnimation animation, + @NonNull Bounds bounds) { + return bounds; + } + + @Override + public WindowInsets dispatchWindowInsetsAnimationProgress( + @NonNull WindowInsets insets, + @NonNull List runningAnimations) { + return insets; + } + + @Override + public void dispatchWindowInsetsAnimationEnd( + @NonNull WindowInsetsAnimation animation) { } + + @Override + public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... p) { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (int i = p.length - 1; i >= 0; i--) { + SyncRtSurfaceTransactionApplier.applyParams(t, p[i], mTmpFloat9); } + t.apply(); + t.close(); + } - private void controlAnimationUnchecked(int typesReady, - SparseArray controls, boolean show) { - if (typesReady == 0) { - // nothing to animate. - return; - } - mAnimatingShown = show; + @Override + public void updateRequestedVisibleTypes(int types) { } - final InsetsState state = mFocusedWin.getInsetsState(); + @Override + public boolean hasAnimationCallbacks() { + return false; + } - // We are about to playing the default animation. Passing a null frame indicates - // the controlled types should be animated regardless of the frame. - mAnimationControl = new InsetsAnimationControlImpl(controls, - null /* frame */, state, mListener, typesReady, this, - mListener.getDurationMs(), getInsetsInterpolator(), - show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show - ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN - : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, - null /* translator */, null /* statsToken */); - SurfaceAnimationThread.getHandler().post( - () -> mListener.onReady(mAnimationControl, typesReady)); - } + @Override + public void setSystemBarsAppearance(int appearance, int mask) { } - /** Called on SurfaceAnimationThread without global WM lock held. */ - @Override - public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { - if (mAnimationControl.applyChangeInsets(null /* outState */)) { - mAnimationControl.finish(mAnimatingShown); - } - } + @Override + public int getSystemBarsAppearance() { + return 0; + } - @Override - public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { - // Nothing's needed here. Finish steps is handled in the listener - // onAnimationFinished callback. - } + @Override + public void setSystemBarsBehavior(int behavior) { } - /** Called on SurfaceAnimationThread without global WM lock held. */ - @Override - public void applySurfaceParams( - final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - for (int i = params.length - 1; i >= 0; i--) { - SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; - applyParams(t, surfaceParams, mTmpFloat9); - } - t.apply(); - t.close(); - } + @Override + public int getSystemBarsBehavior() { + return BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + } - // Since we don't push applySurfaceParams to a Handler-queue we don't need - // to push release in this case. - @Override - public void releaseSurfaceControlFromRt(SurfaceControl sc) { - sc.release(); - } + @Override + public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { + surfaceControl.release(); + } - @Override - public - void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, - WindowInsetsAnimation animation, - Bounds bounds) { - } + @Override + public void addOnPreDrawRunnable(Runnable r) { } - @Override - public void reportPerceptible(int types, boolean perceptible) { - // No-op for now - only client windows report perceptibility for now, with policy - // controllers assumed to always be perceptible. - } + @Override + public void postInsetsAnimationCallback(Runnable r) { } + + @Override + public InputMethodManager getInputMethodManager() { + return null; + } + + @Nullable + @Override + public String getRootViewTitle() { + return mName; + } + + @Override + public int dipToPx(int dips) { + return 0; + } + + @Nullable + @Override + public IBinder getWindowToken() { + return null; } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 204cbf79dba97..994dcf1e2ea50 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -31,17 +31,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import android.app.StatusBarManager; @@ -268,8 +263,6 @@ public class InsetsPolicyTest extends WindowTestsBase { navBar.setHasSurface(true); navBarProvider.setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); - spyOn(policy); - doNothing().when(policy).startAnimation(anyBoolean(), any()); // Make both system bars invisible. mAppWindow.setRequestedVisibleTypes( @@ -305,8 +298,6 @@ public class InsetsPolicyTest extends WindowTestsBase { addNavigationBar().getControllableInsetProvider().setServerVisible(true); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); - spyOn(policy); - doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); @@ -341,8 +332,6 @@ public class InsetsPolicyTest extends WindowTestsBase { mAppWindow.mAboveInsetsState.addSource(navBarSource); mAppWindow.mAboveInsetsState.addSource(statusBarSource); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); - spyOn(policy); - doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); @@ -390,8 +379,6 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState app2 = addWindow(TYPE_APPLICATION, "app"); final InsetsPolicy policy = mDisplayContent.getInsetsPolicy(); - spyOn(policy); - doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(app); policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index 4639ee0ae33d0..45ecc3f762ec8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -77,6 +77,10 @@ class TestDisplayContent extends DisplayContent { spyOn(inputMonitor); doNothing().when(inputMonitor).resumeDispatchingLw(any()); + final InsetsPolicy insetsPolicy = getInsetsPolicy(); + WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); + WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); + // For devices that set the sysprop ro.bootanim.set_orientation_ // See DisplayRotation#readDefaultDisplayRotation for context. // Without that, meaning of height and width in context of the tests can be swapped if diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index be8ee7832a5dc..d5547ec692476 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -222,6 +222,10 @@ class WindowTestsBase extends SystemServiceTestsBase { displayPolicy.finishWindowsDrawn(); displayPolicy.finishScreenTurningOn(); + final InsetsPolicy insetsPolicy = mDefaultDisplay.getInsetsPolicy(); + suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); + suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); + mTransaction = mSystemServicesTestRule.mTransaction; mMockSession = mock(Session.class); @@ -278,6 +282,15 @@ class WindowTestsBase extends SystemServiceTestsBase { checkDeviceSpecificOverridesNotApplied(); } + /** + * The test doesn't create real SurfaceControls, but mocked ones. This prevents the target from + * controlling them, or it will cause {@link NullPointerException}. + */ + static void suppressInsetsAnimation(InsetsControlTarget target) { + spyOn(target); + Mockito.doNothing().when(target).notifyInsetsControlChanged(); + } + @After public void tearDown() throws Exception { if (mUseFakeSettingsProvider) {