diff options
3 files changed, 165 insertions, 57 deletions
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java index a89f79540c65..dc41b70d1683 100644 --- a/core/java/android/view/ContentRecordingSession.java +++ b/core/java/android/view/ContentRecordingSession.java @@ -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 } @@ -251,6 +287,14 @@ public final class ContentRecordingSession implements Parcelable { } /** + * 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() {} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9663f3aba7f1..88f72f9dbc90 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -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,21 +8800,41 @@ 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 + * activity * @return a token representing the task containing the activity started with the given launch - * cookie, or {@code null} if the token couldn't be found. + * cookie, or {@code null} if the token couldn't be found. */ @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); } /** 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 e86fc366a631..eaeb8049b81a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -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; } |