diff options
author | Jorim Jaggi <jjaggi@google.com> | 2019-01-02 23:21:49 +0100 |
---|---|---|
committer | Tiger Huang <tigerhuang@google.com> | 2019-09-05 18:20:06 +0800 |
commit | 2862047fd7d8945d4eb7f9927554efeb5f5f524a (patch) | |
tree | 4f7d86d27819821c83d8a9f9ef754f0a6da9a44e | |
parent | 2d46334c82aca43fb6421ce24ab73caac2f9026f (diff) |
Window Manager Flag Migration (2/n)
Adds policy to decide when focused window can control bars. And also
generalizes control target for:
a) Normal case: The focused window is the control target.
b) Transient bar case: The control target is a special object in WM
that controls the transient animation.
Bug: 118118435
Test: atest InsetsPolicyTest InsetsStateControllerTest WindowStateTests
Change-Id: I4819d85eec745d56abe23dbca0905979da854e5e
13 files changed, 417 insertions, 99 deletions
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index bdd3038cfee5..be2c4e96fdd7 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -86,7 +86,7 @@ public class InsetsAnimationControlImplTest { } @AfterClass - public static void tearDownOnce() throws Exception { + public static void tearDownOnce() { sInsetsModeSession.close(); } diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 90bb494232c7..5c4332dc8a6a 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -27,6 +27,7 @@ import android.os.SystemClock; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.View; +import android.view.ViewRootImpl; import android.view.WindowManager; import com.android.server.LocalServices; @@ -89,6 +90,10 @@ public class BarController { } void setWindow(WindowState win) { + if (ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) { + // BarController gets replaced with InsetsPolicy in the full insets mode. + return; + } mWin = win; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 126765221c51..5ec167e495e0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -549,6 +549,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final PointerEventDispatcher mPointerEventDispatcher; private final InsetsStateController mInsetsStateController; + private final InsetsPolicy mInsetsPolicy; /** @see #getParentWindow() */ private WindowState mParentWindow; @@ -968,6 +969,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWmService.mAnimator.addDisplayLocked(mDisplayId); mInputMonitor = new InputMonitor(service, mDisplayId); mInsetsStateController = new InsetsStateController(this); + mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this); } boolean isReady() { @@ -1126,6 +1128,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mInsetsStateController; } + InsetsPolicy getInsetsPolicy() { + return mInsetsPolicy; + } + @Surface.Rotation int getRotation() { return mDisplayRotation.getRotation(); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 8328770b6cb2..aecbca391735 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1029,6 +1029,14 @@ public class DisplayPolicy { displayFrames.mDisplayCutoutSafe.top); } + WindowState getStatusBar() { + return mStatusBar; + } + + WindowState getNavigationBar() { + return mNavigationBar; + } + /** * Control the animation to run when a window's state changes. Return a * non-0 number to force the animation to a specific resource ID, or 0 @@ -3108,8 +3116,7 @@ public class DisplayPolicy { return 0; } - mDisplayContent.getInsetsStateController().onBarControllingWindowChanged( - mTopFullscreenOpaqueWindowState); + mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) & ~mResettingSystemUiFlags diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java new file mode 100644 index 000000000000..3db6dcf07abf --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +/** + * Generalization of an object that can control insets state. + */ +interface InsetsControlTarget { + void notifyInsetsControlChanged(); +} diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java new file mode 100644 index 000000000000..2dc50d86eaea --- /dev/null +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; + +import android.annotation.Nullable; + +/** + * Policy that implements who gets control over the windows generating insets. + */ +class InsetsPolicy { + + private final InsetsStateController mStateController; + private final DisplayContent mDisplayContent; + private final DisplayPolicy mPolicy; + + InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { + mStateController = stateController; + mDisplayContent = displayContent; + mPolicy = displayContent.getDisplayPolicy(); + } + + /** Updates the target which can control system bars. */ + void updateBarControlTarget(@Nullable WindowState focusedWin) { + mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin), + getNavControlTarget(focusedWin)); + } + + private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) { + if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) { + return null; + } + return focusedWin; + } + + private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) { + if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) { + return null; + } + return focusedWin; + } + + private boolean isStatusBarForciblyVisible() { + final WindowState statusBar = mPolicy.getStatusBar(); + if (statusBar == null) { + return false; + } + final int privateFlags = statusBar.mAttrs.privateFlags; + + // TODO: Pretend to the app that it's still able to control it? + if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { + return true; + } + if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + return true; + } + return false; + } + + private boolean isNavBarForciblyVisible() { + final WindowState statusBar = mPolicy.getStatusBar(); + if (statusBar == null) { + return false; + } + if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) { + return true; + } + return false; + } + + private boolean areSystemBarsForciblyVisible() { + final boolean isDockedStackVisible = + mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final boolean isFreeformStackVisible = + mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); + final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing(); + + // We need to force system bars when the docked stack is visible, when the freeform stack + // is visible but also when we are resizing for the transitions when docked stack + // visibility changes. + return isDockedStackVisible || isFreeformStackVisible || isResizing; + } + +} diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index b7835aa1c04e..1b7b92bca250 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -52,7 +52,7 @@ class InsetsSourceProvider { private final DisplayContent mDisplayContent; private final InsetsStateController mStateController; private @Nullable InsetsSourceControl mControl; - private @Nullable WindowState mControllingWin; + private @Nullable InsetsControlTarget mControlTarget; private @Nullable ControlAdapter mAdapter; private WindowState mWin; private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; @@ -119,8 +119,8 @@ class InsetsSourceProvider { mSource.setFrame(new Rect()); } else { mWin.setInsetProvider(this); - if (mControllingWin != null) { - updateControlForTarget(mControllingWin, true /* force */); + if (mControlTarget != null) { + updateControlForTarget(mControlTarget, true /* force */); } } } @@ -143,19 +143,19 @@ class InsetsSourceProvider { if (mControl != null) { final Rect frame = mWin.getWindowFrames().mFrame; if (mControl.setSurfacePosition(frame.left, frame.top)) { - mStateController.notifyControlChanged(mControllingWin); + mStateController.notifyControlChanged(mControlTarget); } } setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy() && !mWin.mGivenInsetsPending); } - void updateControlForTarget(@Nullable WindowState target, boolean force) { + void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { if (mWin == null) { - mControllingWin = target; + mControlTarget = target; return; } - if (target == mControllingWin && !force) { + if (target == mControlTarget && !force) { return; } if (target == null) { @@ -167,13 +167,13 @@ class InsetsSourceProvider { setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter, !mClientVisible /* hidden */); - mControllingWin = target; + mControlTarget = target; mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash, new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); } boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) { - if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) { + if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) { return false; } setClientVisible(modifiedSource.isVisible()); @@ -232,10 +232,10 @@ class InsetsSourceProvider { @Override public void onAnimationCancelled(SurfaceControl animationLeash) { if (mAdapter == this) { - mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this); + mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this); setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); mControl = null; - mControllingWin = null; + mControlTarget = null; mAdapter = null; } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index a1b52f424fee..bb704957a55a 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -16,12 +16,10 @@ package com.android.server.wm; +import static android.view.InsetsState.InternalInsetType; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; -import static android.view.ViewRootImpl.sNewInsetsMode; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,7 +29,6 @@ import android.util.SparseArray; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.ViewRootImpl; import java.io.PrintWriter; import java.util.ArrayList; @@ -46,10 +43,11 @@ class InsetsStateController { private final InsetsState mState = new InsetsState(); private final DisplayContent mDisplayContent; - private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>(); - private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>(); - private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>(); - private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>(); + private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>(); + private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap = + new ArrayMap<>(); + private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>(); + private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { if (w.isVisible()) { @@ -87,15 +85,15 @@ class InsetsStateController { return state; } - @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) { - ArrayList<Integer> controlled = mWinControlTypeMap.get(target); + @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { + ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); if (controlled == null) { return null; } final int size = controlled.size(); final InsetsSourceControl[] result = new InsetsSourceControl[size]; for (int i = 0; i < size; i++) { - result[i] = mControllers.get(controlled.get(i)).getControl(); + result[i] = mProviders.get(controlled.get(i)).getControl(); } return result; } @@ -103,8 +101,8 @@ class InsetsStateController { /** * @return The provider of a specific type. */ - InsetsSourceProvider getSourceProvider(int type) { - return mControllers.computeIfAbsent(type, + InsetsSourceProvider getSourceProvider(@InternalInsetType int type) { + return mProviders.computeIfAbsent(type, key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); } @@ -113,8 +111,8 @@ class InsetsStateController { */ void onPostLayout() { mState.setDisplayFrame(mDisplayContent.getBounds()); - for (int i = mControllers.size() - 1; i>= 0; i--) { - mControllers.valueAt(i).onPostLayout(); + for (int i = mProviders.size() - 1; i >= 0; i--) { + mProviders.valueAt(i).onPostLayout(); } if (!mLastState.equals(mState)) { mLastState.set(mState, true /* copySources */); @@ -126,7 +124,7 @@ class InsetsStateController { boolean changed = false; for (int i = state.getSourcesCount() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); - final InsetsSourceProvider provider = mControllers.get(source.getType()); + final InsetsSourceProvider provider = mProviders.get(source.getType()); if (provider == null) { continue; } @@ -137,75 +135,77 @@ class InsetsStateController { } } - void onImeTargetChanged(@Nullable WindowState imeTarget) { + void onImeTargetChanged(@Nullable InsetsControlTarget imeTarget) { onControlChanged(TYPE_IME, imeTarget); notifyPendingInsetsControlChanged(); } /** - * Called when the top opaque fullscreen window that is able to control the system bars changes. + * Called when the focused window that is able to control the system bars changes. * - * @param controllingWindow The window that is now able to control the system bars appearance - * and visibility. + * @param topControlling The target that is now able to control the top bar appearance + * and visibility. + * @param navControlling The target that is now able to control the nav bar appearance + * and visibility. */ - void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) { - // TODO: Apply policy that determines whether controllingWindow is able to control system - // bars - - // TODO: Depending on the form factor, mapping is different - onControlChanged(TYPE_TOP_BAR, controllingWindow); - onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow); + void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling, + @Nullable InsetsControlTarget navControlling) { + onControlChanged(TYPE_TOP_BAR, topControlling); + onControlChanged(TYPE_NAVIGATION_BAR, navControlling); notifyPendingInsetsControlChanged(); } - void notifyControlRevoked(@NonNull WindowState previousControllingWin, + void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider) { - removeFromControlMaps(previousControllingWin, provider.getSource().getType()); + removeFromControlMaps(previousControlTarget, provider.getSource().getType()); } - private void onControlChanged(int type, @Nullable WindowState win) { - final WindowState previous = mTypeWinControlMap.get(type); - if (win == previous) { + private void onControlChanged(@InternalInsetType int type, + @Nullable InsetsControlTarget target) { + final InsetsControlTarget previous = mTypeControlTargetMap.get(type); + if (target == previous) { return; } - final InsetsSourceProvider controller = getSourceProvider(type); - if (controller == null) { + final InsetsSourceProvider provider = getSourceProvider(type); + if (provider == null) { return; } - if (!controller.isControllable()) { + if (!provider.isControllable()) { return; } - controller.updateControlForTarget(win, false /* force */); + provider.updateControlForTarget(target, false /* force */); if (previous != null) { removeFromControlMaps(previous, type); mPendingControlChanged.add(previous); } - if (win != null) { - addToControlMaps(win, type); - mPendingControlChanged.add(win); + if (target != null) { + addToControlMaps(target, type); + mPendingControlChanged.add(target); } } - private void removeFromControlMaps(@NonNull WindowState win, int type) { - final ArrayList<Integer> array = mWinControlTypeMap.get(win); + private void removeFromControlMaps(@NonNull InsetsControlTarget target, + @InternalInsetType int type) { + final ArrayList<Integer> array = mControlTargetTypeMap.get(target); if (array == null) { return; } array.remove((Integer) type); if (array.isEmpty()) { - mWinControlTypeMap.remove(win); + mControlTargetTypeMap.remove(target); } - mTypeWinControlMap.remove(type); + mTypeControlTargetMap.remove(type); } - private void addToControlMaps(@NonNull WindowState win, int type) { - final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win, + private void addToControlMaps(@NonNull InsetsControlTarget target, + @InternalInsetType int type) { + final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target, key -> new ArrayList<>()); array.add(type); - mTypeWinControlMap.put(type, win); + mTypeControlTargetMap.put(type, target); } - void notifyControlChanged(WindowState target) { + void notifyControlChanged(InsetsControlTarget target) { mPendingControlChanged.add(target); notifyPendingInsetsControlChanged(); } @@ -216,8 +216,8 @@ class InsetsStateController { } mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { - final WindowState controllingWin = mPendingControlChanged.valueAt(i); - controllingWin.notifyInsetsControlChanged(); + final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); + controlTarget.notifyInsetsControlChanged(); } mPendingControlChanged.clear(); }); @@ -231,10 +231,10 @@ class InsetsStateController { pw.println(prefix + "WindowInsetsStateController"); mState.dump(prefix + " ", pw); pw.println(prefix + " " + "Control map:"); - for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) { + for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) { pw.print(prefix + " "); - pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> " - + mTypeWinControlMap.valueAt(i)); + pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> " + + mTypeControlTargetMap.valueAt(i)); } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4900869f209d..cbb0b3aab687 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -217,7 +217,8 @@ import java.util.List; import java.util.function.Predicate; /** A window in the window manager. */ -class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState { +class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState, + InsetsControlTarget { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM; // The minimal size of a window within the usable area of the freeform stack. @@ -3329,7 +3330,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - void notifyInsetsControlChanged() { + @Override + public void notifyInsetsControlChanged() { final InsetsStateController stateController = getDisplayContent().getInsetsStateController(); try { diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java new file mode 100644 index 000000000000..f3a8e1a0bd5e --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.platform.test.annotations.Presubmit; +import android.view.InsetsSourceControl; +import android.view.test.InsetsModeSession; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@FlakyTest(detail = "Promote to pre-submit once confirmed stable.") +@Presubmit +@RunWith(WindowTestRunner.class) +public class InsetsPolicyTest extends WindowTestsBase { + private static InsetsModeSession sInsetsModeSession; + + @BeforeClass + public static void setUpOnce() { + // To let the insets provider control the insets visibility, the insets mode has to be + // NEW_INSETS_MODE_FULL. + sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL); + } + + @AfterClass + public static void tearDownOnce() { + sInsetsModeSession.close(); + } + + @Test + public void testControlsForDispatch_regular() { + addWindow(TYPE_STATUS_BAR, "topBar"); + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The app can control both system bars. + assertNotNull(controls); + assertEquals(2, controls.length); + } + + @Test + public void testControlsForDispatch_dockedStackVisible() { + addWindow(TYPE_STATUS_BAR, "topBar"); + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final WindowState win = createWindowOnStack(null, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, + ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app"); + final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win); + + // The app must not control any system bars. + assertNull(controls); + } + + @Test + public void testControlsForDispatch_freeformStackVisible() { + addWindow(TYPE_STATUS_BAR, "topBar"); + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final WindowState win = createWindowOnStack(null, WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app"); + final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win); + + // The app must not control any bars. + assertNull(controls); + } + + @Test + public void testControlsForDispatch_dockedDividerControllerResizing() { + addWindow(TYPE_STATUS_BAR, "topBar"); + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + mDisplayContent.getDockedDividerController().setResizing(true); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The app must not control any system bars. + assertNull(controls); + } + + @Test + public void testControlsForDispatch_keyguard() { + addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |= PRIVATE_FLAG_KEYGUARD; + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The app must not control the top bar. + assertNotNull(controls); + assertEquals(1, controls.length); + } + + // TODO: adjust this test if we pretend to the app that it's still able to control it. + @Test + public void testControlsForDispatch_forceStatusBarVisibleTransparent() { + addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |= + PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The app must not control the top bar. + assertNotNull(controls); + assertEquals(1, controls.length); + } + + @Test + public void testControlsForDispatch_statusBarForceShowNavigation() { + addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |= + PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The app must not control the navigation bar. + assertNotNull(controls); + assertEquals(1, controls.length); + } + + private WindowState addWindow(int type, String name) { + final WindowState win = createWindow(null, type, name); + mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs); + return win; + } + + private InsetsSourceControl[] addAppWindowAndGetControlsForDispatch() { + return addWindowAndGetControlsForDispatch(addWindow(TYPE_APPLICATION, "app")); + } + + private InsetsSourceControl[] addWindowAndGetControlsForDispatch(WindowState win) { + mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); + return mDisplayContent.getInsetsStateController().getControlsForDispatch(win); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 9fce78b292f5..81ea32bb4f73 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -23,13 +23,15 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import android.platform.test.annotations.Presubmit; +import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.ViewRootImpl; +import android.view.test.InsetsModeSession; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -37,90 +39,91 @@ import androidx.test.filters.SmallTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest +@FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Presubmit +@RunWith(WindowTestRunner.class) public class InsetsStateControllerTest extends WindowTestsBase { - private static int sPreviousNewInsetsMode; + private static InsetsModeSession sInsetsModeSession; @BeforeClass public static void setUpOnce() { - // TODO: Make use of SettingsSession when it becomes feasible for this. - sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode; // To let the insets provider control the insets visibility, the insets mode has to be // NEW_INSETS_MODE_FULL. - ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; + sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL); } @AfterClass public static void tearDownOnce() { - ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode; + sInsetsModeSession.close(); } @Test @FlakyTest(bugId = 131005232) public void testStripForDispatch_notOwn() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null); topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR)); assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR)); } - @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_own() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) .setWindow(topBar, null); topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR)); - assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar)); + final InsetsState state = getController().getInsetsForDispatch(topBar); + for (int i = state.getSourcesCount() - 1; i >= 0; i--) { + final InsetsSource source = state.sourceAt(i); + assertNotEquals(TYPE_TOP_BAR, source.getType()); + } } - @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_navBar() { - final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null); getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null); getController().getSourceProvider(TYPE_IME).setWindow(ime, null); - assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar)); + assertEquals(0, getController().getInsetsForDispatch(navBar).getSourcesCount()); } - @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testBarControllingWinChanged() { - final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null); getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null); - getController().onBarControllingWindowChanged(app); + getController().onBarControlTargetChanged(app, app); InsetsSourceControl[] controls = getController().getControlsForDispatch(app); assertEquals(2, controls.length); } - @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testControlRevoked() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null); - getController().onBarControllingWindowChanged(app); + getController().onBarControlTargetChanged(app, null); assertNotNull(getController().getControlsForDispatch(app)); - getController().onBarControllingWindowChanged(null); + getController().onBarControlTargetChanged(null, null); assertNull(getController().getControlsForDispatch(app)); } @FlakyTest(bugId = 124088319) @Test public void testControlRevoked_animation() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null); - getController().onBarControllingWindowChanged(app); + getController().onBarControlTargetChanged(app, null); assertNotNull(getController().getControlsForDispatch(app)); topBar.cancelAnimation(); assertNull(getController().getControlsForDispatch(app)); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index d5e84405edd1..f41f126593de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -403,7 +403,7 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(topBar.isVisible()); mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) .setWindow(topBar, null); - mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app); + mDisplayContent.getInsetsStateController().onBarControlTargetChanged(app, app); mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR)); waitUntilHandlersIdle(); diff --git a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java index c83dfa41d260..e05fdce0ca0c 100644 --- a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java +++ b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java @@ -31,7 +31,7 @@ public class InsetsModeSession implements AutoCloseable { } @Override - public void close() throws Exception { + public void close() { ViewRootImpl.sNewInsetsMode = mOldMode; } } |