diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 0c9c87a088008..361788497e116 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; @@ -207,11 +208,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; } @@ -247,11 +258,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, @@ -285,7 +299,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(); @@ -293,6 +307,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 b475846d60edb..fd4289cc83355 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -481,9 +481,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 = mainWindow.getInsetsState(); mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState()); final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), 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 c4ca989e9d6b8..a6744d55af9d5 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -237,11 +237,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 = 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 f2f3cefae8a5e..e4a079d84a2a3 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1531,6 +1531,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 821480daa9306..8c02faf7ac092 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -37,16 +37,21 @@ 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; import androidx.test.filters.SmallTest; +import com.android.server.statusbar.StatusBarManagerInternal; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -205,6 +210,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); } @UseTestDisplay(addWindows = W_ACTIVITY) @@ -295,6 +317,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();