diff options
author | 2025-01-31 10:06:29 +0900 | |
---|---|---|
committer | 2025-02-05 18:50:14 +0900 | |
commit | 8d487e5a40a26fcaf31e3b5b0f2bc40c92cd9831 (patch) | |
tree | c0a18d3bb53b627d1163de32df5150c3ae414d94 | |
parent | e9c2327ceacd6559dfb1dc9746d370f92a10fe90 (diff) |
Stop all activities behind presentation
There's a security concern with Presentation API and Connected
Displays (CD), where malicious apps can show a presentation on other
apps and do "tap jacking". This is a new scenario with CD because
apps can now be launched on external displays in extended/projected
mode.
To prevent this problem, we've agreed that we should stop all the
activities below a presentation on the same display. This way,
even if a transparent presentation is created, other apps become
completely hidden, and the user won't inject input events intended
for other apps.
Flag: com.android.window.flags.enable_presentation_for_connected_displays
Bug: 393454633
Test: WindowManagerServiceTests#testPresentationHidesActivitiesBehind
Change-Id: I26108aa0bc0cac320c8fb50ea67543981ab77e23
5 files changed, 63 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 89b46bc4eba4..84ee942e84d9 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -248,6 +248,7 @@ 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.WindowManagerService.sEnableShellTransitions; 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; @@ -6224,6 +6225,16 @@ 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) { + return false; + } + // Check if the activity is on a sleeping display and keyguard is not going away (to // align with TaskFragment#shouldSleepActivities), canTurnScreenOn will also check keyguard // visibility diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a0d2d260b39e..1847be2b7c09 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -547,6 +547,9 @@ 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3a1d652f82d4..6bfb9468a381 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -157,6 +157,7 @@ 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; @@ -1924,6 +1925,12 @@ public class WindowManagerService extends IWindowManager.Stub 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); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a8f22eaeabb8..84d8f840d849 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -182,6 +182,7 @@ 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; @@ -2317,6 +2318,12 @@ 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); } 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 be79160c3a09..7bc2c477f144 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -26,6 +26,7 @@ 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; @@ -53,6 +54,7 @@ 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; @@ -99,6 +101,7 @@ 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; @@ -1405,6 +1408,38 @@ 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()); |