diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index fd67db1d5cc9f..be1d0fc0ecc8f 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -36,6 +36,7 @@ import android.view.InsetsAnimationControlCallbacks; import android.view.InsetsAnimationControlImpl; import android.view.InsetsAnimationControlRunner; import android.view.InsetsController; +import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; @@ -212,11 +213,21 @@ class InsetsPolicy { * @see InsetsStateController#getInsetsForDispatch */ InsetsState getInsetsForDispatch(WindowState target) { - InsetsState originalState = mStateController.getInsetsForDispatch(target); + final InsetsState originalState = mStateController.getInsetsForDispatch(target); InsetsState state = originalState; for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { - state = new InsetsState(state); - state.setSourceVisible(mShowingTransientTypes.get(i), false); + final int type = mShowingTransientTypes.get(i); + final InsetsSource originalSource = state.peekSource(type); + if (originalSource != null && originalSource.isVisible()) { + if (state == originalState) { + // The source will be modified, create a non-deep copy to store the new one. + state = new InsetsState(originalState); + } + // Replace the source with a copy in invisible state. + final InsetsSource source = new InsetsSource(originalSource); + source.setVisible(false); + state.addSource(source); + } } return state; } @@ -252,11 +263,14 @@ class InsetsPolicy { } } + /** + * If the caller is not {@link #updateBarControlTarget}, it should call + * updateBarControlTarget(mFocusedWin) after this invocation. + */ private void abortTransient() { mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray()); mShowingTransientTypes.clear(); - updateBarControlTarget(mFocusedWin); } private @Nullable InsetsControlTarget getFakeControlTarget(@Nullable WindowState focused, @@ -290,7 +304,7 @@ class InsetsPolicy { // fake control to the client, so that it can re-show the bar during this scenario. return mDummyControlTarget; } - if (mPolicy.topAppHidesStatusBar()) { + if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar()) { // Non-fullscreen focused window should not break the state that the top-fullscreen-app // window hides status bar. return mPolicy.getTopFullscreenOpaqueWindow(); @@ -298,6 +312,16 @@ class InsetsPolicy { return focusedWin; } + private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) { + // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may + // haven't drawn or committed the visibility. + final boolean nonAttachedAppWindow = win != null + && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent() + && !win.inMultiWindowMode(); + } + private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, boolean forceShowsSystemBarsForWindowingMode) { if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index e85049cad2f49..3c0cb17af6030 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -480,9 +480,7 @@ class TaskSnapshotController { final int color = ColorUtils.setAlphaComponent( task.getTaskDescription().getBackgroundColor(), 255); final LayoutParams attrs = mainWindow.getAttrs(); - final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy(); - final InsetsState insetsState = - new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow)); + final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState()); mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState()); final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState); final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 6cc0ba5e209cf..94229b94eb64c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -245,11 +245,7 @@ class TaskSnapshotSurface implements StartingSurface { task.getBounds(taskBounds); currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation; activityType = activity.getActivityType(); - - final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent() - .getInsetsPolicy(); - insetsState = - new InsetsState(insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow)); + insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState()); mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState()); } try { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 451aa4cf541de..bab83dc6ea705 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1525,6 +1525,10 @@ class WindowState extends WindowContainer implements WindowManagerP return getDisplayContent().getDisplayInfo(); } + /** + * Returns the insets state for the client. Its sources may be the copies with visibility + * modification according to the state of transient bars. + */ InsetsState getInsetsState() { return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this); } 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 90af8a1b31998..7d84920187a04 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -38,10 +38,13 @@ 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.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.app.StatusBarManager; import android.platform.test.annotations.Presubmit; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -49,6 +52,8 @@ import android.view.test.InsetsModeSession; import androidx.test.filters.SmallTest; +import com.android.server.statusbar.StatusBarManagerInternal; + import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -222,6 +227,23 @@ public class InsetsPolicyTest extends WindowTestsBase { assertNotNull(fullscreenAppControls); assertEquals(1, fullscreenAppControls.length); assertEquals(ITYPE_STATUS_BAR, fullscreenAppControls[0].getType()); + + // Assume mFocusedWindow is updated but mTopFullscreenOpaqueWindowState hasn't. + final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp"); + final InsetsState newRequestedState = new InsetsState(); + newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true); + newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState); + // Make sure status bar is hidden by previous insets state. + mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp); + + final StatusBarManagerInternal sbmi = + mDisplayContent.getDisplayPolicy().getStatusBarManagerInternal(); + clearInvocations(sbmi); + mDisplayContent.getInsetsPolicy().updateBarControlTarget(newFocusedFullscreenApp); + // The status bar should be shown by newFocusedFullscreenApp even + // mTopFullscreenOpaqueWindowState is still fullscreenApp. + verify(sbmi).setWindowState(mDisplayContent.mDisplayId, StatusBarManager.WINDOW_STATUS_BAR, + StatusBarManager.WINDOW_STATE_SHOWING); } @Test @@ -309,6 +331,15 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsState state = policy.getInsetsForDispatch(mAppWindow); state.setSourceVisible(ITYPE_STATUS_BAR, true); state.setSourceVisible(ITYPE_NAVIGATION_BAR, true); + + final InsetsState clientState = mAppWindow.getInsetsState(); + // The transient bar states for client should be invisible. + assertFalse(clientState.getSource(ITYPE_STATUS_BAR).isVisible()); + assertFalse(clientState.getSource(ITYPE_NAVIGATION_BAR).isVisible()); + // The original state shouldn't be modified. + assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible()); + assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible()); + policy.onInsetsModified(mAppWindow, state); waitUntilWindowAnimatorIdle();