summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ContentRecordingSession.java97
-rw-r--r--core/tests/coretests/src/android/view/ContentRecordingSessionTest.java13
-rw-r--r--media/java/android/media/projection/IMediaProjection.aidl25
-rw-r--r--media/tests/projection/src/android/media/projection/FakeIMediaProjection.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java3
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java63
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java53
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