diff options
author | 2025-02-10 01:50:37 -0800 | |
---|---|---|
committer | 2025-02-10 01:50:37 -0800 | |
commit | 673ab751d24ad4c4a7124609912bc972405a09ac (patch) | |
tree | 0f384c82fc5562c19bb619a51766ef4efc8963c6 | |
parent | 28aae60c1521aa4df6853bb8c76dc5c1d5c297bd (diff) | |
parent | a4936735bdd605a95738adacfb61b4be81ccb558 (diff) |
Merge "Create PresentationController" into main
7 files changed, 179 insertions, 67 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 20917ba21439..d84016b3816e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -247,7 +247,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; -import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -6208,13 +6207,8 @@ final class ActivityRecord extends WindowToken { return false; } - // Hide all activities on the presenting display so that malicious apps can't do tap - // jacking (b/391466268). - // For now, this should only be applied to external displays because presentations can only - // be shown on them. - // TODO(b/390481621): Disallow a presentation from covering its controlling activity so that - // the presentation won't stop its controlling activity. - if (enablePresentationForConnectedDisplays() && mDisplayContent.mIsPresenting) { + // A presentation stopps all activities behind on the same display. + if (mWmService.mPresentationController.shouldOccludeActivities(getDisplayId())) { return false; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bf54c737b7be..c87087f84399 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -547,9 +547,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // TODO(multi-display): remove some of the usages. boolean isDefaultDisplay; - /** Indicates whether any presentation is shown on this display. */ - boolean mIsPresenting; - /** Save allocating when calculating rects */ private final Rect mTmpRect = new Rect(); private final Region mTmpRegion = new Region(); diff --git a/services/core/java/com/android/server/wm/PresentationController.java b/services/core/java/com/android/server/wm/PresentationController.java new file mode 100644 index 000000000000..9630b8fd524b --- /dev/null +++ b/services/core/java/com/android/server/wm/PresentationController.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 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 com.android.window.flags.Flags.enablePresentationForConnectedDisplays; + +import android.annotation.NonNull; +import android.util.IntArray; + +/** + * Manages presentation windows. + */ +class PresentationController { + + // TODO(b/395475549): Add support for display add/remove, and activity move across displays. + private final IntArray mPresentingDisplayIds = new IntArray(); + + PresentationController() {} + + private boolean isPresenting(int displayId) { + return mPresentingDisplayIds.contains(displayId); + } + + boolean shouldOccludeActivities(int displayId) { + // All activities on the presenting display must be hidden so that malicious apps can't do + // tap jacking (b/391466268). + // For now, this should only be applied to external displays because presentations can only + // be shown on them. + // TODO(b/390481621): Disallow a presentation from covering its controlling activity so that + // the presentation won't stop its controlling activity. + return enablePresentationForConnectedDisplays() && isPresenting(displayId); + } + + void onPresentationAdded(@NonNull WindowState win) { + final int displayId = win.getDisplayId(); + if (isPresenting(displayId)) { + return; + } + mPresentingDisplayIds.add(displayId); + if (enablePresentationForConnectedDisplays()) { + // A presentation hides all activities behind on the same display. + win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, + /*notifyClients=*/ true); + } + win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ true); + } + + void onPresentationRemoved(@NonNull WindowState win) { + final int displayId = win.getDisplayId(); + if (!isPresenting(displayId)) { + return; + } + // TODO(b/393945496): Make sure that there's one presentation at most per display. + final int displayIdIndex = mPresentingDisplayIds.indexOf(displayId); + if (displayIdIndex != -1) { + mPresentingDisplayIds.remove(displayIdIndex); + } + if (enablePresentationForConnectedDisplays()) { + // A presentation hides all activities behind on the same display. + win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null, + /*notifyClients=*/ true); + } + win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ false); + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c54e7c15530c..bb669915e366 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -157,7 +157,6 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY; import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER; import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID; import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; -import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays; import static com.android.window.flags.Flags.multiCrop; import static com.android.window.flags.Flags.setScPropertiesInClient; @@ -503,6 +502,8 @@ public class WindowManagerService extends IWindowManager.Stub final StartingSurfaceController mStartingSurfaceController; + final PresentationController mPresentationController; + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { @Override public void onVrStateChanged(boolean enabled) { @@ -1433,6 +1434,7 @@ public class WindowManagerService extends IWindowManager.Stub setGlobalShadowSettings(); mAnrController = new AnrController(this); mStartingSurfaceController = new StartingSurfaceController(this); + mPresentationController = new PresentationController(); mBlurController = new BlurController(mContext, mPowerManager); mTaskFpsCallbackController = new TaskFpsCallbackController(mContext); @@ -1937,16 +1939,8 @@ public class WindowManagerService extends IWindowManager.Stub } outSizeCompatScale[0] = win.getCompatScaleForClient(); - if (res >= ADD_OKAY - && (type == TYPE_PRESENTATION || type == TYPE_PRIVATE_PRESENTATION)) { - displayContent.mIsPresenting = true; - if (enablePresentationForConnectedDisplays()) { - // A presentation hides all activities behind on the same display. - displayContent.ensureActivitiesVisible(/*starting=*/ null, - /*notifyClients=*/ true); - } - mDisplayManagerInternal.onPresentation(displayContent.getDisplay().getDisplayId(), - /*isShown=*/ true); + if (res >= ADD_OKAY && win.isPresentation()) { + mPresentationController.onPresentationAdded(win); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 81895c462a63..589724182980 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -182,7 +182,6 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; -import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays; import static com.android.window.flags.Flags.surfaceTrustedOverlay; import android.annotation.CallSuper; @@ -2300,15 +2299,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int type = mAttrs.type; - if (type == TYPE_PRESENTATION || type == TYPE_PRIVATE_PRESENTATION) { - // TODO(b/393945496): Make sure that there's one presentation at most per display. - dc.mIsPresenting = false; - if (enablePresentationForConnectedDisplays()) { - // A presentation hides all activities behind on the same display. - dc.ensureActivitiesVisible(/*starting=*/ null, /*notifyClients=*/ true); - } - mWmService.mDisplayManagerInternal.onPresentation(dc.getDisplay().getDisplayId(), - /*isShown=*/ false); + if (isPresentation()) { + mWmService.mPresentationController.onPresentationRemoved(this); } // Check if window provides non decor insets before clearing its provided insets. final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets(); @@ -3337,6 +3329,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + boolean isPresentation() { + return mAttrs.type == TYPE_PRESENTATION || mAttrs.type == TYPE_PRIVATE_PRESENTATION; + } + private boolean isOnVirtualDisplay() { return getDisplayContent().mDisplay.getType() == Display.TYPE_VIRTUAL; } diff --git a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java new file mode 100644 index 000000000000..db90c28ec7df --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2025 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.view.Display.FLAG_PRESENTATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.window.flags.Flags.FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; + +import android.graphics.Rect; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; +import android.view.IWindow; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest WmTests:PresentationControllerTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class PresentationControllerTests extends WindowTestsBase { + + @EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS) + @Test + public void testPresentationHidesActivitiesBehind() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.flags = FLAG_PRESENTATION; + final DisplayContent dc = createNewDisplay(displayInfo); + final int displayId = dc.getDisplayId(); + doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId); + final ActivityRecord activity = createActivityRecord(createTask(dc)); + assertTrue(activity.isVisible()); + + doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); + final int uid = 100000; // uid for non-system user + final Session session = createTestSession(mAtm, 1234 /* pid */, uid); + final int userId = UserHandle.getUserId(uid); + doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId)); + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_PRESENTATION); + + final IWindow clientWindow = new TestIWindow(); + final int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId, + userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(), + new InsetsSourceControl.Array(), new Rect(), new float[1]); + assertTrue(result >= WindowManagerGlobal.ADD_OKAY); + assertFalse(activity.isVisible()); + + final WindowState window = mWm.windowForClientLocked(session, clientWindow, false); + window.removeImmediately(); + assertTrue(activity.isVisible()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 1323d8a59cef..71e84c0f1821 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -26,7 +26,6 @@ import static android.permission.flags.Flags.FLAG_SENSITIVE_CONTENT_RECENTS_SCRE import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_OWN_FOCUS; -import static android.view.Display.FLAG_PRESENTATION; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; @@ -55,7 +54,6 @@ import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_ import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; -import static com.android.window.flags.Flags.FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS; import static com.google.common.truth.Truth.assertThat; @@ -102,7 +100,6 @@ import android.provider.Settings; import android.util.ArraySet; import android.util.MergedConfiguration; import android.view.ContentRecordingSession; -import android.view.DisplayInfo; import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; @@ -1409,38 +1406,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { assertEquals(activityWindowInfo2, activityWindowInfo3); } - @EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS) - @Test - public void testPresentationHidesActivitiesBehind() { - DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.copyFrom(mDisplayInfo); - displayInfo.flags = FLAG_PRESENTATION; - DisplayContent dc = createNewDisplay(displayInfo); - int displayId = dc.getDisplayId(); - doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId); - ActivityRecord activity = createActivityRecord(createTask(dc)); - assertTrue(activity.isVisible()); - - doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); - int uid = 100000; // uid for non-system user - Session session = createTestSession(mAtm, 1234 /* pid */, uid); - int userId = UserHandle.getUserId(uid); - doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId)); - WindowManager.LayoutParams params = new WindowManager.LayoutParams( - LayoutParams.TYPE_PRESENTATION); - - final IWindow clientWindow = new TestIWindow(); - int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId, - userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(), - new InsetsSourceControl.Array(), new Rect(), new float[1]); - assertTrue(result >= WindowManagerGlobal.ADD_OKAY); - assertFalse(activity.isVisible()); - - final WindowState window = mWm.windowForClientLocked(session, clientWindow, false); - window.removeImmediately(); - assertTrue(activity.isVisible()); - } - @Test public void testAddOverlayWindowToUnassignedDisplay_notAllowed_ForVisibleBackgroundUsers() { doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); |