Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d487e5a authored by Kazuki Takise's avatar Kazuki Takise
Browse files

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
parent e9c2327c
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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();
+7 −0
Original line number Diff line number Diff line
@@ -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);
            }
+7 −0
Original line number Diff line number Diff line
@@ -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);
        }
+35 −0
Original line number Diff line number Diff line
@@ -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());