diff options
15 files changed, 247 insertions, 83 deletions
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java index dc41b70d1683..952c63b936c7 100644 --- a/core/java/android/view/ContentRecordingSession.java +++ b/core/java/android/view/ContentRecordingSession.java @@ -58,6 +58,15 @@ public final class ContentRecordingSession implements Parcelable { /** Can't report (e.g. side loaded app). */ public static final int TARGET_UID_UNKNOWN = -2; + /** Task id is not set either because full screen capture or launching a new app */ + public static final int TASK_ID_UNKNOWN = -1; + + /** + * Id of Task that is launched to be captured for a single app capture session. The value may be + * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture. + */ + private int mTaskId = TASK_ID_UNKNOWN; + /** * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has * recorded content rendered to its surface. @@ -115,16 +124,16 @@ public final class ContentRecordingSession implements Parcelable { /** Returns an instance initialized for task recording. */ public static ContentRecordingSession createTaskSession( @NonNull IBinder taskWindowContainerToken) { - return createTaskSession(taskWindowContainerToken, TARGET_UID_UNKNOWN); + return createTaskSession(taskWindowContainerToken, TASK_ID_UNKNOWN); } /** Returns an instance initialized for task recording. */ public static ContentRecordingSession createTaskSession( - @NonNull IBinder taskWindowContainerToken, int targetUid) { + @NonNull IBinder taskWindowContainerToken, int taskId) { return new ContentRecordingSession() .setContentToRecord(RECORD_CONTENT_TASK) .setTokenToRecord(taskWindowContainerToken) - .setTargetUid(targetUid); + .setTaskId(taskId); } /** @@ -211,12 +220,14 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member /* package-private */ ContentRecordingSession( + int taskId, int virtualDisplayId, @RecordContent int contentToRecord, int displayToRecord, @Nullable IBinder tokenToRecord, boolean waitingForConsent, int targetUid) { + this.mTaskId = taskId; this.mVirtualDisplayId = virtualDisplayId; this.mContentToRecord = contentToRecord; @@ -237,6 +248,15 @@ public final class ContentRecordingSession implements Parcelable { } /** + * Id of Task that is launched to be captured for a single app capture session. The value may be + * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture. + */ + @DataClass.Generated.Member + public int getTaskId() { + return mTaskId; + } + + /** * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has * recorded content rendered to its surface. */ @@ -295,6 +315,16 @@ public final class ContentRecordingSession implements Parcelable { } /** + * Id of Task that is launched to be captured for a single app capture session. The value may be + * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture. + */ + @DataClass.Generated.Member + public @NonNull ContentRecordingSession setTaskId( int value) { + mTaskId = value; + return this; + } + + /** * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has * recorded content rendered to its surface. */ @@ -374,6 +404,7 @@ public final class ContentRecordingSession implements Parcelable { // String fieldNameToString() { ... } return "ContentRecordingSession { " + + "taskId = " + mTaskId + ", " + "virtualDisplayId = " + mVirtualDisplayId + ", " + "contentToRecord = " + recordContentToString(mContentToRecord) + ", " + "displayToRecord = " + mDisplayToRecord + ", " + @@ -396,6 +427,7 @@ public final class ContentRecordingSession implements Parcelable { ContentRecordingSession that = (ContentRecordingSession) o; //noinspection PointlessBooleanExpression return true + && mTaskId == that.mTaskId && mVirtualDisplayId == that.mVirtualDisplayId && mContentToRecord == that.mContentToRecord && mDisplayToRecord == that.mDisplayToRecord @@ -411,6 +443,7 @@ public final class ContentRecordingSession implements Parcelable { // int fieldNameHashCode() { ... } int _hash = 1; + _hash = 31 * _hash + mTaskId; _hash = 31 * _hash + mVirtualDisplayId; _hash = 31 * _hash + mContentToRecord; _hash = 31 * _hash + mDisplayToRecord; @@ -427,9 +460,10 @@ public final class ContentRecordingSession implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mWaitingForConsent) flg |= 0x10; - if (mTokenToRecord != null) flg |= 0x8; + if (mWaitingForConsent) flg |= 0x20; + if (mTokenToRecord != null) flg |= 0x10; dest.writeByte(flg); + dest.writeInt(mTaskId); dest.writeInt(mVirtualDisplayId); dest.writeInt(mContentToRecord); dest.writeInt(mDisplayToRecord); @@ -449,13 +483,15 @@ public final class ContentRecordingSession implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); - boolean waitingForConsent = (flg & 0x10) != 0; + boolean waitingForConsent = (flg & 0x20) != 0; + int taskId = in.readInt(); int virtualDisplayId = in.readInt(); int contentToRecord = in.readInt(); int displayToRecord = in.readInt(); - IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder(); + IBinder tokenToRecord = (flg & 0x10) == 0 ? null : (IBinder) in.readStrongBinder(); int targetUid = in.readInt(); + this.mTaskId = taskId; this.mVirtualDisplayId = virtualDisplayId; this.mContentToRecord = contentToRecord; @@ -496,6 +532,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public static final class Builder { + private int mTaskId; private int mVirtualDisplayId; private @RecordContent int mContentToRecord; private int mDisplayToRecord; @@ -509,13 +546,25 @@ public final class ContentRecordingSession implements Parcelable { } /** + * Id of Task that is launched to be captured for a single app capture session. The value may be + * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture. + */ + @DataClass.Generated.Member + public @NonNull Builder setTaskId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mTaskId = value; + return this; + } + + /** * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has * recorded content rendered to its surface. */ @DataClass.Generated.Member public @NonNull Builder setVirtualDisplayId(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x1; + mBuilderFieldsSet |= 0x2; mVirtualDisplayId = value; return this; } @@ -526,7 +575,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setContentToRecord(@RecordContent int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x2; + mBuilderFieldsSet |= 0x4; mContentToRecord = value; return this; } @@ -540,7 +589,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setDisplayToRecord(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x4; + mBuilderFieldsSet |= 0x8; mDisplayToRecord = value; return this; } @@ -554,7 +603,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setTokenToRecord(@NonNull IBinder value) { checkNotUsed(); - mBuilderFieldsSet |= 0x8; + mBuilderFieldsSet |= 0x10; mTokenToRecord = value; return this; } @@ -568,7 +617,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setWaitingForConsent(boolean value) { checkNotUsed(); - mBuilderFieldsSet |= 0x10; + mBuilderFieldsSet |= 0x20; mWaitingForConsent = value; return this; } @@ -579,7 +628,7 @@ public final class ContentRecordingSession implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setTargetUid(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; + mBuilderFieldsSet |= 0x40; mTargetUid = value; return this; } @@ -587,27 +636,31 @@ public final class ContentRecordingSession implements Parcelable { /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull ContentRecordingSession build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { - mVirtualDisplayId = INVALID_DISPLAY; + mTaskId = TASK_ID_UNKNOWN; } if ((mBuilderFieldsSet & 0x2) == 0) { - mContentToRecord = RECORD_CONTENT_DISPLAY; + mVirtualDisplayId = INVALID_DISPLAY; } if ((mBuilderFieldsSet & 0x4) == 0) { - mDisplayToRecord = INVALID_DISPLAY; + mContentToRecord = RECORD_CONTENT_DISPLAY; } if ((mBuilderFieldsSet & 0x8) == 0) { - mTokenToRecord = null; + mDisplayToRecord = INVALID_DISPLAY; } if ((mBuilderFieldsSet & 0x10) == 0) { - mWaitingForConsent = false; + mTokenToRecord = null; } if ((mBuilderFieldsSet & 0x20) == 0) { + mWaitingForConsent = false; + } + if ((mBuilderFieldsSet & 0x40) == 0) { mTargetUid = TARGET_UID_UNKNOWN; } ContentRecordingSession o = new ContentRecordingSession( + mTaskId, mVirtualDisplayId, mContentToRecord, mDisplayToRecord, @@ -618,7 +671,7 @@ public final class ContentRecordingSession implements Parcelable { } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -626,10 +679,10 @@ public final class ContentRecordingSession implements Parcelable { } @DataClass.Generated( - time = 1697456140720L, + time = 1716481148184L, 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\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)") + 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\npublic static final int TASK_ID_UNKNOWN\nprivate int mTaskId\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/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java index 17980ac5e735..f8800cb0fea0 100644 --- a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java +++ b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java @@ -18,6 +18,7 @@ package android.view; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; +import static android.view.ContentRecordingSession.TASK_ID_UNKNOWN; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -45,6 +46,7 @@ import org.junit.runner.RunWith; @Presubmit public class ContentRecordingSessionTest { private static final int DISPLAY_ID = 1; + private static final int TASK_ID = 123; private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken"); @Test @@ -65,6 +67,16 @@ public class ContentRecordingSessionTest { ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN); assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK); assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN); + assertThat(session.getTaskId()).isEqualTo(TASK_ID_UNKNOWN); + } + + @Test + public void testSecondaryTaskConstructor() { + ContentRecordingSession session = + ContentRecordingSession.createTaskSession(WINDOW_TOKEN, TASK_ID); + assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK); + assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN); + assertThat(session.getTaskId()).isEqualTo(TASK_ID); } @Test @@ -73,6 +85,7 @@ public class ContentRecordingSessionTest { DEFAULT_DISPLAY); assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY); assertThat(session.getTokenToRecord()).isNull(); + assertThat(session.getTaskId()).isEqualTo(TASK_ID_UNKNOWN); } @Test diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 2fb0af5557b5..7a1cf925bc6d 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -39,8 +39,8 @@ interface IMediaProjection { void unregisterCallback(IMediaProjectionCallback callback); /** - * Returns the {@link LaunchCookie} identifying the task to record, or {@code null} if - * there is none. + * Returns the {@link LaunchCookie} identifying the task to record. Will always be set + * regardless of starting a new task or recent task */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" @@ -48,8 +48,16 @@ interface IMediaProjection { LaunchCookie getLaunchCookie(); /** - * Updates the {@link LaunchCookie} identifying the task to record, or {@code null} if - * there is none. + * Returns the taskId identifying the task to record. Will only be set in the case of + * launching a recent task, otherwise set to -1. + */ + @EnforcePermission("MANAGE_MEDIA_PROJECTION") + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") + int getTaskId(); + + /** + * Updates the {@link LaunchCookie} identifying the task to record. */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" @@ -57,6 +65,15 @@ interface IMediaProjection { void setLaunchCookie(in LaunchCookie launchCookie); /** + * Updates the taskId identifying the task to record. + */ + @EnforcePermission("MANAGE_MEDIA_PROJECTION") + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") + void setTaskId(in int taskId); + + + /** * Returns {@code true} if this token is still valid. A token is valid as long as the token * hasn't timed out before it was used, and the token is only used once. * diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java index 0df36afddf25..6860c0bb2740 100644 --- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java +++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java @@ -28,6 +28,7 @@ import android.os.RemoteException; * outside the test it is implemented by the system server. */ public final class FakeIMediaProjection extends IMediaProjection.Stub { + int mTaskId = -1; boolean mIsStarted = false; LaunchCookie mLaunchCookie = null; IMediaProjectionCallback mIMediaProjectionCallback = null; @@ -87,6 +88,13 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) + public int getTaskId() throws RemoteException { + getTaskId_enforcePermission(); + return mTaskId; + } + + @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public void setLaunchCookie(LaunchCookie launchCookie) throws RemoteException { setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; @@ -94,6 +102,13 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) + public void setTaskId(int taskId) throws RemoteException { + setTaskId_enforcePermission(); + mTaskId = taskId; + } + + @Override + @EnforcePermission(MANAGE_MEDIA_PROJECTION) public boolean isValid() throws RemoteException { isValid_enforcePermission(); return true; diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt index a618490c1b53..de56c8493d98 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt @@ -21,15 +21,17 @@ import android.os.Parcel import android.os.Parcelable /** - * Class that represents an area that should be captured. Currently it has only a launch cookie that - * represents a task but we potentially could add more identifiers e.g. for a pair of tasks. + * Class that represents an area that should be captured. Currently it has only a launch cookie and + * id that represents a task but we potentially could add more identifiers e.g. for a pair of tasks. */ -data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?) : Parcelable { +data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?, val taskId: Int) : + Parcelable { - constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel)) + constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel), parcel.readInt()) override fun writeToParcel(dest: Parcel, flags: Int) { LaunchCookie.writeToParcel(launchCookie, dest) + dest.writeInt(taskId) } override fun describeContents(): Int = 0 diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt index 4685c5a0cb21..d6affd2f0250 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt @@ -174,7 +174,7 @@ class MediaProjectionAppSelectorActivity( // is created and ready to be captured. val activityStarted = activityLauncher.startActivityAsUser(intent, userHandle, activityOptions.toBundle()) { - returnSelectedApp(launchCookie) + returnSelectedApp(launchCookie, taskId = -1) } // Rely on the ActivityManager to pop up a dialog regarding app suspension @@ -232,7 +232,7 @@ class MediaProjectionAppSelectorActivity( } } - override fun returnSelectedApp(launchCookie: LaunchCookie) { + override fun returnSelectedApp(launchCookie: LaunchCookie, taskId: Int) { taskSelected = true if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) { // The client requested to return the result in the result receiver instead of @@ -242,7 +242,7 @@ class MediaProjectionAppSelectorActivity( EXTRA_CAPTURE_REGION_RESULT_RECEIVER, ResultReceiver::class.java ) as ResultReceiver - val captureRegion = MediaProjectionCaptureTarget(launchCookie) + val captureRegion = MediaProjectionCaptureTarget(launchCookie, taskId) val data = Bundle().apply { putParcelable(KEY_CAPTURE_TARGET, captureRegion) } resultReceiver.send(RESULT_OK, data) // TODO(b/279175710): Ensure consent result is always set here. Skipping this for now @@ -255,6 +255,7 @@ class MediaProjectionAppSelectorActivity( val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) projection.setLaunchCookie(launchCookie) + projection.setTaskId(taskId) val intent = Intent() intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt index f204b3e74f4b..6857000169e5 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt @@ -9,7 +9,10 @@ import android.app.ActivityOptions.LaunchCookie interface MediaProjectionAppSelectorResultHandler { /** * Return selected app to the original caller of the media projection app picker. - * @param launchCookie launch cookie of the launched activity of the target app + * @param launchCookie launch cookie of the launched activity of the target app, always set + * regardless of launching a new task or a recent task + * @param taskId id of the launched task of the target app, only set to a positive int when + * launching a recent task, otherwise set to -1 by default */ - fun returnSelectedApp(launchCookie: LaunchCookie) + fun returnSelectedApp(launchCookie: LaunchCookie, taskId: Int) } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt index 9549ab1cab3e..46aa0644035c 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt @@ -144,10 +144,9 @@ constructor( activityOptions.launchDisplayId = task.displayId activityOptions.setLaunchCookie(launchCookie) - val handleResult: () -> Unit = { resultHandler.returnSelectedApp(launchCookie)} - val taskId = task.taskId val splitBounds = task.splitBounds + val handleResult: () -> Unit = { resultHandler.returnSelectedApp(launchCookie, taskId)} if (pssAppSelectorRecentsSplitScreen() && task.isLaunchingInSplitScreen() && diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 5469a4e9da08..e024710ed3eb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -119,6 +119,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder()); if (mCaptureRegion != null) { projection.setLaunchCookie(mCaptureRegion.getLaunchCookie()); + projection.setTaskId(mCaptureRegion.getTaskId()); } mMediaProjection = new MediaProjection(mContext, projection); mMediaProjection.registerCallback(this, mHandler); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index deecc5bb5a03..0d7a9e4d2430 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -146,7 +146,8 @@ public class RecordingServiceTest extends SysuiTestCase { @Test public void testLogStartPartialRecording() { - MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new LaunchCookie()); + MediaProjectionCaptureTarget target = + new MediaProjectionCaptureTarget(new LaunchCookie(), 12345); Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, target); mRecordingService.onStartCommand(startIntent, 0, 0); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 27ea1cd2f65d..d4c0b0180242 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1745,6 +1745,7 @@ public final class DisplayManagerService extends SystemService { if (projection != null) { IBinder taskWindowContainerToken = projection.getLaunchCookie() == null ? null : projection.getLaunchCookie().binder; + int taskId = projection.getTaskId(); if (taskWindowContainerToken == null) { // Record a particular display. session = ContentRecordingSession.createDisplaySession( @@ -1752,7 +1753,7 @@ public final class DisplayManagerService extends SystemService { } else { // Record a single task indicated by the launch cookie. session = ContentRecordingSession.createTaskSession( - taskWindowContainerToken); + taskWindowContainerToken, taskId); } } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index bbb19e351b5d..e3d5c54f1d5e 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -553,7 +553,8 @@ public final class MediaProjectionManagerService extends SystemService mProjectionGrant.getLaunchCookie() == null ? null : mProjectionGrant.getLaunchCookie().binder; setReviewedConsentSessionLocked( - ContentRecordingSession.createTaskSession(taskWindowContainerToken)); + ContentRecordingSession.createTaskSession( + taskWindowContainerToken, mProjectionGrant.mTaskId)); break; } } @@ -977,6 +978,7 @@ public final class MediaProjectionManagerService extends SystemService private IBinder mToken; private IBinder.DeathRecipient mDeathEater; private boolean mRestoreSystemAlertWindow; + private int mTaskId = -1; private LaunchCookie mLaunchCookie = null; // Values for tracking token validity. @@ -1197,12 +1199,26 @@ public final class MediaProjectionManagerService extends SystemService @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call + public void setTaskId(int taskId) { + setTaskId_enforcePermission(); + mTaskId = taskId; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) + @Override // Binder call public LaunchCookie getLaunchCookie() { getLaunchCookie_enforcePermission(); return mLaunchCookie; } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) + @Override // Binder call + public int getTaskId() { + getTaskId_enforcePermission(); + return mTaskId; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override public boolean isValid() { isValid_enforcePermission(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b6035519fdba..0bf1c88d5b4f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8619,8 +8619,8 @@ public class WindowManagerService extends IWindowManager.Stub return true; } // For a task session, find the activity identified by the launch cookie. - final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie( - incomingSession.getTokenToRecord()); + final WindowContainerInfo wci = + getTaskWindowContainerInfoForRecordingSession(incomingSession); if (wci == null) { Slog.w(TAG, "Handling a new recording session; unable to find the " + "WindowContainerToken"); @@ -9082,35 +9082,58 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with - * the given launch cookie. + * Retrieve the {@link WindowContainerInfo} of the task that was launched for MediaProjection. * - * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an - * activity + * @param session the {@link ContentRecordingSession} containing the launch cookie and/or + * task id of the Task started for capture. * @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. */ @VisibleForTesting @Nullable - WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) { - // Find the activity identified by the launch cookie. - 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; + WindowContainerInfo getTaskWindowContainerInfoForRecordingSession( + @NonNull ContentRecordingSession session) { + WindowContainerToken taskWindowContainerToken = null; + ActivityRecord targetActivity = null; + Task targetTask = null; + + // First attempt to find the launched task by looking for the launched activity with the + // matching launch cookie. + if (session.getTokenToRecord() != null) { + IBinder launchCookie = session.getTokenToRecord(); + targetActivity = mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie); + if (targetActivity == null) { + Slog.w(TAG, "Unable to find the activity for this launch cookie"); + } else { + if (targetActivity.getTask() == null) { + Slog.w(TAG, "Unable to find the task for this launch cookie"); + } else { + targetTask = targetActivity.getTask(); + taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken(); + } + } } - if (targetActivity.getTask() == null) { - Slog.w(TAG, "Unable to find the task for this launch cookie"); - return null; + + // In the case we can't find an activity with a matching launch cookie, it could be due to + // the launched activity being closed, but the launched task is still open, so now attempt + // to look for the task directly. + if (taskWindowContainerToken == null && session.getTaskId() != -1) { + int targetTaskId = session.getTaskId(); + targetTask = mRoot.getTask(task -> task.isTaskId(targetTaskId)); + if (targetTask == null) { + Slog.w(TAG, "Unable to find the task for this projection"); + } else { + taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken(); + } } - WindowContainerToken taskWindowContainerToken = - targetActivity.getTask().mRemoteToken.toWindowContainerToken(); + + // If we were unable to find the launched task in either fashion, then something must have + // wrong (i.e. the task was closed before capture started). if (taskWindowContainerToken == null) { - Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName()); + Slog.w(TAG, "Unable to find the WindowContainerToken for ContentRecordingSession"); return null; } - return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken); + return new WindowContainerInfo(targetTask.effectiveUid, taskWindowContainerToken); } /** diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index abd3abee82fb..e64397d4223b 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -372,8 +372,8 @@ public class MediaProjectionManagerServiceTest { doReturn(true) .when(mWindowManagerInternal) .setContentRecordingSession(any(ContentRecordingSession.class)); - ContentRecordingSession taskSession = - createTaskSession(mock(IBinder.class), targetUid); + ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class)); + taskSession.setTargetUid(targetUid); service.setContentRecordingSession(taskSession); projection.stop(); @@ -708,8 +708,8 @@ public class MediaProjectionManagerServiceTest { mService = new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector); - ContentRecordingSession taskSession = - createTaskSession(mock(IBinder.class), targetUid); + ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class)); + taskSession.setTargetUid(targetUid); mService.setContentRecordingSession(taskSession); MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); @@ -915,8 +915,8 @@ public class MediaProjectionManagerServiceTest { .setContentRecordingSession(any(ContentRecordingSession.class)); int targetUid = 123455; - ContentRecordingSession taskSession = - createTaskSession(mock(IBinder.class), targetUid); + ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class)); + taskSession.setTargetUid(targetUid); service.setContentRecordingSession(taskSession); verify(mMediaProjectionMetricsLogger).logInProgress(projection.uid, targetUid); 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 7e6301fda872..24ebad60a191 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -795,39 +795,55 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() { - WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(null); - assertThat(wci).isNull(); - } - - @Test - public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() { + public void testGetTaskWindowContainerTokenForRecordingSession_invalidCookie() { Binder cookie = new Binder("test cookie"); - WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie); + WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession(cookie)); assertThat(wci).isNull(); final ActivityRecord testActivity = new ActivityBuilder(mAtm) .setCreateTask(true) .build(); - wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie); + wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession(cookie)); assertThat(wci).isNull(); } @Test - public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() { + public void testGetTaskWindowContainerTokenForRecordingSession_validCookie() { final Binder cookie = new Binder("ginger cookie"); final WindowContainerToken launchRootTask = mock(WindowContainerToken.class); final int uid = 123; setupActivityWithLaunchCookie(cookie, launchRootTask, uid); - WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie); + WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession(cookie)); + mExpect.that(wci.getToken()).isEqualTo(launchRootTask); + mExpect.that(wci.getUid()).isEqualTo(uid); + } + + @Test + public void testGetTaskWindowContainerTokenForRecordingSession_validTaskId() { + final WindowContainerToken launchRootTask = mock(WindowContainerToken.class); + final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class); + when(remoteToken.toWindowContainerToken()).thenReturn(launchRootTask); + + final int uid = 123; + final ActivityRecord testActivity = + new ActivityBuilder(mAtm).setCreateTask(true).setUid(uid).build(); + testActivity.mLaunchCookie = null; + testActivity.getTask().mRemoteToken = remoteToken; + + WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession( + new Binder("cookie"), testActivity.getTask().mTaskId)); mExpect.that(wci.getToken()).isEqualTo(launchRootTask); mExpect.that(wci.getUid()).isEqualTo(uid); } @Test - public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() { + public void testGetTaskWindowContainerTokenForRecordingSession_multipleCookies() { final Binder cookie1 = new Binder("ginger cookie"); final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class); final int uid1 = 123; @@ -839,13 +855,14 @@ public class WindowManagerServiceTests extends WindowTestsBase { setupActivityWithLaunchCookie(new Binder("peanut butter cookie"), mock(WindowContainerToken.class), /* uid= */ 789); - WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie1); + WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession(cookie1)); mExpect.that(wci.getToken()).isEqualTo(launchRootTask1); mExpect.that(wci.getUid()).isEqualTo(uid1); } @Test - public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() { + public void testGetTaskWindowContainerTokenForRecordingSession_multipleCookies_noneValid() { setupActivityWithLaunchCookie(new Binder("ginger cookie"), mock(WindowContainerToken.class), /* uid= */ 123); @@ -855,8 +872,8 @@ public class WindowManagerServiceTests extends WindowTestsBase { setupActivityWithLaunchCookie(new Binder("peanut butter cookie"), mock(WindowContainerToken.class), /* uid= */ 789); - WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie( - new Binder("some other cookie")); + WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession( + ContentRecordingSession.createTaskSession(new Binder("some other cookie"))); assertThat(wci).isNull(); } @@ -895,6 +912,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { public void setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue() { WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class); ActivityRecord activityRecord = createActivityRecord(createTask(mDefaultDisplay)); + activityRecord.mLaunchCookie = new Binder(); ContentRecordingSession session = ContentRecordingSession.createTaskSession( activityRecord.mLaunchCookie); @@ -908,6 +926,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class); Task task = createTask(mDefaultDisplay); ActivityRecord activityRecord = createActivityRecord(task); + activityRecord.mLaunchCookie = new Binder(); ContentRecordingSession session = ContentRecordingSession.createTaskSession(activityRecord.mLaunchCookie); @@ -915,7 +934,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { mExpect.that(session.getTokenToRecord()) .isEqualTo(task.mRemoteToken.toWindowContainerToken().asBinder()); - mExpect.that(session.getTargetUid()).isEqualTo(activityRecord.getUid()); + mExpect.that(session.getTargetUid()).isEqualTo(task.effectiveUid); } @Test |