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

Commit c30f220f authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge "[Media Projection] Add target uid to ContentRecordingSession" into main

parents 31799013 0af6129b
Loading
Loading
Loading
Loading
+93 −18
Original line number Diff line number Diff line
@@ -52,6 +52,12 @@ public final class ContentRecordingSession implements Parcelable {
     */
    public static final int RECORD_CONTENT_TASK = 1;

    /** Full screen sharing (app is not selected). */
    public static final int TARGET_UID_FULL_SCREEN = -1;

    /** Can't report (e.g. side loaded app). */
    public static final int TARGET_UID_UNKNOWN = -2;

    /**
     * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
     * recorded content rendered to its surface.
@@ -89,27 +95,36 @@ public final class ContentRecordingSession implements Parcelable {
     */
    private boolean mWaitingForConsent = false;

    /** UID of the package that is captured if selected. */
    private int mTargetUid = TARGET_UID_UNKNOWN;

    /**
     * Default instance, with recording the display.
     */
    private ContentRecordingSession() {
    }

    /**
     * Returns an instance initialized for recording the indicated display.
     */
    /** Returns an instance initialized for recording the indicated display. */
    public static ContentRecordingSession createDisplaySession(int displayToMirror) {
        return new ContentRecordingSession().setDisplayToRecord(displayToMirror)
                .setContentToRecord(RECORD_CONTENT_DISPLAY);
        return new ContentRecordingSession()
                .setDisplayToRecord(displayToMirror)
                .setContentToRecord(RECORD_CONTENT_DISPLAY)
                .setTargetUid(TARGET_UID_FULL_SCREEN);
    }

    /**
     * Returns an instance initialized for task recording.
     */
    /** Returns an instance initialized for task recording. */
    public static ContentRecordingSession createTaskSession(
            @NonNull IBinder taskWindowContainerToken) {
        return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_TASK)
                .setTokenToRecord(taskWindowContainerToken);
        return createTaskSession(taskWindowContainerToken, TARGET_UID_UNKNOWN);
    }

    /** Returns an instance initialized for task recording. */
    public static ContentRecordingSession createTaskSession(
            @NonNull IBinder taskWindowContainerToken, int targetUid) {
        return new ContentRecordingSession()
                .setContentToRecord(RECORD_CONTENT_TASK)
                .setTokenToRecord(taskWindowContainerToken)
                .setTargetUid(targetUid);
    }

    /**
@@ -175,13 +190,33 @@ public final class ContentRecordingSession implements Parcelable {
        }
    }

    @IntDef(prefix = "TARGET_UID_", value = {
        TARGET_UID_FULL_SCREEN,
        TARGET_UID_UNKNOWN
    })
    @Retention(RetentionPolicy.SOURCE)
    @DataClass.Generated.Member
    public @interface TargetUid {}

    @DataClass.Generated.Member
    public static String targetUidToString(@TargetUid int value) {
        switch (value) {
            case TARGET_UID_FULL_SCREEN:
                    return "TARGET_UID_FULL_SCREEN";
            case TARGET_UID_UNKNOWN:
                    return "TARGET_UID_UNKNOWN";
            default: return Integer.toHexString(value);
        }
    }

    @DataClass.Generated.Member
    /* package-private */ ContentRecordingSession(
            int virtualDisplayId,
            @RecordContent int contentToRecord,
            int displayToRecord,
            @Nullable IBinder tokenToRecord,
            boolean waitingForConsent) {
            boolean waitingForConsent,
            int targetUid) {
        this.mVirtualDisplayId = virtualDisplayId;
        this.mContentToRecord = contentToRecord;

@@ -196,6 +231,7 @@ public final class ContentRecordingSession implements Parcelable {
        this.mDisplayToRecord = displayToRecord;
        this.mTokenToRecord = tokenToRecord;
        this.mWaitingForConsent = waitingForConsent;
        this.mTargetUid = targetUid;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -250,6 +286,14 @@ public final class ContentRecordingSession implements Parcelable {
        return mWaitingForConsent;
    }

    /**
     * UID of the package that is captured if selected.
     */
    @DataClass.Generated.Member
    public int getTargetUid() {
        return mTargetUid;
    }

    /**
     * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
     * recorded content rendered to its surface.
@@ -314,6 +358,15 @@ public final class ContentRecordingSession implements Parcelable {
        return this;
    }

    /**
     * UID of the package that is captured if selected.
     */
    @DataClass.Generated.Member
    public @NonNull ContentRecordingSession setTargetUid( int value) {
        mTargetUid = value;
        return this;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -325,7 +378,8 @@ public final class ContentRecordingSession implements Parcelable {
                "contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
                "displayToRecord = " + mDisplayToRecord + ", " +
                "tokenToRecord = " + mTokenToRecord + ", " +
                "waitingForConsent = " + mWaitingForConsent +
                "waitingForConsent = " + mWaitingForConsent + ", " +
                "targetUid = " + mTargetUid +
        " }";
    }

@@ -346,7 +400,8 @@ public final class ContentRecordingSession implements Parcelable {
                && mContentToRecord == that.mContentToRecord
                && mDisplayToRecord == that.mDisplayToRecord
                && java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
                && mWaitingForConsent == that.mWaitingForConsent;
                && mWaitingForConsent == that.mWaitingForConsent
                && mTargetUid == that.mTargetUid;
    }

    @Override
@@ -361,6 +416,7 @@ public final class ContentRecordingSession implements Parcelable {
        _hash = 31 * _hash + mDisplayToRecord;
        _hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
        _hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
        _hash = 31 * _hash + mTargetUid;
        return _hash;
    }

@@ -378,6 +434,7 @@ public final class ContentRecordingSession implements Parcelable {
        dest.writeInt(mContentToRecord);
        dest.writeInt(mDisplayToRecord);
        if (mTokenToRecord != null) dest.writeStrongBinder(mTokenToRecord);
        dest.writeInt(mTargetUid);
    }

    @Override
@@ -397,6 +454,7 @@ public final class ContentRecordingSession implements Parcelable {
        int contentToRecord = in.readInt();
        int displayToRecord = in.readInt();
        IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder();
        int targetUid = in.readInt();

        this.mVirtualDisplayId = virtualDisplayId;
        this.mContentToRecord = contentToRecord;
@@ -412,6 +470,7 @@ public final class ContentRecordingSession implements Parcelable {
        this.mDisplayToRecord = displayToRecord;
        this.mTokenToRecord = tokenToRecord;
        this.mWaitingForConsent = waitingForConsent;
        this.mTargetUid = targetUid;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -442,6 +501,7 @@ public final class ContentRecordingSession implements Parcelable {
        private int mDisplayToRecord;
        private @Nullable IBinder mTokenToRecord;
        private boolean mWaitingForConsent;
        private int mTargetUid;

        private long mBuilderFieldsSet = 0L;

@@ -513,10 +573,21 @@ public final class ContentRecordingSession implements Parcelable {
            return this;
        }

        /**
         * UID of the package that is captured if selected.
         */
        @DataClass.Generated.Member
        public @NonNull Builder setTargetUid(int value) {
            checkNotUsed();
            mBuilderFieldsSet |= 0x20;
            mTargetUid = value;
            return this;
        }

        /** Builds the instance. This builder should not be touched after calling this! */
        public @NonNull ContentRecordingSession build() {
            checkNotUsed();
            mBuilderFieldsSet |= 0x20; // Mark builder used
            mBuilderFieldsSet |= 0x40; // Mark builder used

            if ((mBuilderFieldsSet & 0x1) == 0) {
                mVirtualDisplayId = INVALID_DISPLAY;
@@ -533,17 +604,21 @@ public final class ContentRecordingSession implements Parcelable {
            if ((mBuilderFieldsSet & 0x10) == 0) {
                mWaitingForConsent = false;
            }
            if ((mBuilderFieldsSet & 0x20) == 0) {
                mTargetUid = TARGET_UID_UNKNOWN;
            }
            ContentRecordingSession o = new ContentRecordingSession(
                    mVirtualDisplayId,
                    mContentToRecord,
                    mDisplayToRecord,
                    mTokenToRecord,
                    mWaitingForConsent);
                    mWaitingForConsent,
                    mTargetUid);
            return o;
        }

        private void checkNotUsed() {
            if ((mBuilderFieldsSet & 0x20) != 0) {
            if ((mBuilderFieldsSet & 0x40) != 0) {
                throw new IllegalStateException(
                        "This Builder should not be reused. Use a new Builder instance instead");
            }
@@ -551,10 +626,10 @@ public final class ContentRecordingSession implements Parcelable {
    }

    @DataClass.Generated(
            time = 1683628463074L,
            time = 1697456140720L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\npublic static final  int TARGET_UID_FULL_SCREEN\npublic static final  int TARGET_UID_UNKNOWN\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\nprivate  int mTargetUid\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
    @Deprecated
    private void __metadata() {}

+33 −11
Original line number Diff line number Diff line
@@ -8461,16 +8461,18 @@ public class WindowManagerService extends IWindowManager.Stub
                    return true;
                }
                // For a task session, find the activity identified by the launch cookie.
                final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie(
                final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie(
                        incomingSession.getTokenToRecord());
                if (wct == null) {
                if (wci == null) {
                    Slog.w(TAG, "Handling a new recording session; unable to find the "
                            + "WindowContainerToken");
                    return false;
                }
                // Replace the launch cookie in the session details with the task's
                // WindowContainerToken.
                incomingSession.setTokenToRecord(wct.asBinder());
                incomingSession.setTokenToRecord(wci.getToken().asBinder());
                // Also replace the UNKNOWN target UID with the actual UID.
                incomingSession.setTargetUid(wci.getUid());
                mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
                        WindowManagerService.this);
                return true;
@@ -8798,9 +8800,29 @@ public class WindowManagerService extends IWindowManager.Stub
        mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
    }

    @VisibleForTesting
    static class WindowContainerInfo {
        private final int mUid;
        @NonNull private final WindowContainerToken mToken;

        private WindowContainerInfo(int uid, @NonNull WindowContainerToken token) {
            this.mUid = uid;
            this.mToken = token;
        }

        public int getUid() {
            return mUid;
        }

        @NonNull
        public WindowContainerToken getToken() {
            return mToken;
        }
    }

    /**
     * Retrieve the {@link WindowContainerToken} of the task that contains the activity started
     * with the given launch cookie.
     * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with
     * the given launch cookie.
     *
     * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an
     *     activity
@@ -8809,10 +8831,10 @@ public class WindowManagerService extends IWindowManager.Stub
     */
    @VisibleForTesting
    @Nullable
    WindowContainerToken getTaskWindowContainerTokenForLaunchCookie(@NonNull IBinder launchCookie) {
    WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) {
        // Find the activity identified by the launch cookie.
        final ActivityRecord targetActivity = mRoot.getActivity(
                activity -> activity.mLaunchCookie == launchCookie);
        final ActivityRecord targetActivity =
                mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
        if (targetActivity == null) {
            Slog.w(TAG, "Unable to find the activity for this launch cookie");
            return null;
@@ -8827,7 +8849,7 @@ public class WindowManagerService extends IWindowManager.Stub
            Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName());
            return null;
        }
        return taskWindowContainerToken;
        return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken);
    }

    /**
+39 −28
Original line number Diff line number Diff line
@@ -100,6 +100,9 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerService.WindowContainerInfo;

import com.google.common.truth.Expect;

import org.junit.Rule;
import org.junit.Test;
@@ -125,6 +128,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
            InstrumentationRegistry.getInstrumentation().getUiAutomation(),
            ADD_TRUSTED_DISPLAY);

    @Rule
    public Expect mExpect = Expect.create();

    @Test
    public void testIsRequestedOrientationMapped() {
        mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true,
@@ -674,64 +680,68 @@ public class WindowManagerServiceTests extends WindowTestsBase {

    @Test
    public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null);
        assertThat(wct).isNull();
        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(null);
        assertThat(wci).isNull();
    }

    @Test
    public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
        Binder cookie = new Binder("test cookie");
        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
        assertThat(wct).isNull();
        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
        assertThat(wci).isNull();

        final ActivityRecord testActivity = new ActivityBuilder(mAtm)
                .setCreateTask(true)
                .build();

        wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
        assertThat(wct).isNull();
        wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
        assertThat(wci).isNull();
    }

    @Test
    public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
        final Binder cookie = new Binder("ginger cookie");
        final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
        setupActivityWithLaunchCookie(cookie, launchRootTask);
        final int uid = 123;
        setupActivityWithLaunchCookie(cookie, launchRootTask, uid);

        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
        assertThat(wct).isEqualTo(launchRootTask);
        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
        mExpect.that(wci.getToken()).isEqualTo(launchRootTask);
        mExpect.that(wci.getUid()).isEqualTo(uid);
    }

    @Test
    public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
        final Binder cookie1 = new Binder("ginger cookie");
        final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
        setupActivityWithLaunchCookie(cookie1, launchRootTask1);
        final int uid1 = 123;
        setupActivityWithLaunchCookie(cookie1, launchRootTask1, uid1);

        setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
                mock(WindowContainerToken.class));
                mock(WindowContainerToken.class), /* uid= */ 456);

        setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
                mock(WindowContainerToken.class));
                mock(WindowContainerToken.class), /* uid= */ 789);

        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1);
        assertThat(wct).isEqualTo(launchRootTask1);
        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie1);
        mExpect.that(wci.getToken()).isEqualTo(launchRootTask1);
        mExpect.that(wci.getUid()).isEqualTo(uid1);
    }

    @Test
    public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
        setupActivityWithLaunchCookie(new Binder("ginger cookie"),
                mock(WindowContainerToken.class));
                mock(WindowContainerToken.class), /* uid= */ 123);

        setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
                mock(WindowContainerToken.class));
                mock(WindowContainerToken.class), /* uid= */ 456);

        setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
                mock(WindowContainerToken.class));
                mock(WindowContainerToken.class), /* uid= */ 789);

        WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(
        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(
                new Binder("some other cookie"));
        assertThat(wct).isNull();
        assertThat(wci).isNull();
    }

    @Test
@@ -778,17 +788,18 @@ public class WindowManagerServiceTests extends WindowTestsBase {
    }

    @Test
    public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
    public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerInfo() {
        WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
        Task task = createTask(mDefaultDisplay);
        ActivityRecord activityRecord = createActivityRecord(task);
        ContentRecordingSession session = ContentRecordingSession.createTaskSession(
                activityRecord.mLaunchCookie);
        ContentRecordingSession session =
                ContentRecordingSession.createTaskSession(activityRecord.mLaunchCookie);

        wmInternal.setContentRecordingSession(session);

        assertThat(session.getTokenToRecord()).isEqualTo(
                task.mRemoteToken.toWindowContainerToken().asBinder());
        mExpect.that(session.getTokenToRecord())
                .isEqualTo(task.mRemoteToken.toWindowContainerToken().asBinder());
        mExpect.that(session.getTargetUid()).isEqualTo(activityRecord.getUid());
    }

    @Test
@@ -1010,12 +1021,12 @@ public class WindowManagerServiceTests extends WindowTestsBase {
        }
    }

    private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
    private void setupActivityWithLaunchCookie(
            IBinder launchCookie, WindowContainerToken wct, int uid) {
        final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
        when(remoteToken.toWindowContainerToken()).thenReturn(wct);
        final ActivityRecord testActivity = new ActivityBuilder(mAtm)
                .setCreateTask(true)
                .build();
        final ActivityRecord testActivity =
                new ActivityBuilder(mAtm).setCreateTask(true).setUid(uid).build();
        testActivity.mLaunchCookie = launchCookie;
        testActivity.getTask().mRemoteToken = remoteToken;
    }