summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Naomi Musgrave <nmusgrave@google.com> 2023-03-27 11:57:14 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-03-27 11:57:14 +0000
commitf9e6314f1d39a0d0614b69a1b652396de9d90384 (patch)
treead2f2088029c700a08377c879e99509968e4a02a
parent6f965bea4a360accd96f4fbf6d196663b9d54ab8 (diff)
parentbb1da32fc8177b086145760ce10d70dc188b319d (diff)
Merge "[MediaProjection] Do not rely on WindowContext to indicate display" into udc-dev
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java2
-rw-r--r--core/java/android/hardware/display/DisplayManager.java13
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java8
-rw-r--r--core/java/android/hardware/display/VirtualDisplay.java13
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java38
-rw-r--r--core/java/android/view/ContentRecordingSession.java167
-rw-r--r--core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java8
-rw-r--r--core/tests/coretests/src/android/view/ContentRecordingSessionTest.java79
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--media/java/android/media/projection/MediaProjection.java60
-rw-r--r--media/tests/projection/src/android/media/projection/MediaProjectionTest.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java31
-rw-r--r--services/core/java/com/android/server/vr/Vr2dDisplay.java3
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java45
-rw-r--r--services/core/java/com/android/server/wm/ContentRecordingController.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java93
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java24
20 files changed, 399 insertions, 360 deletions
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 00e6408a8dd3..a7aba2108e61 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -583,7 +583,7 @@ public final class VirtualDeviceManager {
throw ex.rethrowFromSystemServer();
}
DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
- return displayManager.createVirtualDisplayWrapper(config, mContext, callbackWrapper,
+ return displayManager.createVirtualDisplayWrapper(config, callbackWrapper,
displayId);
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6ae71d2bb25e..b5281a5025b8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1096,8 +1096,7 @@ public final class DisplayManager {
@NonNull VirtualDisplayConfig config,
@Nullable Handler handler,
@Nullable VirtualDisplay.Callback callback) {
- return createVirtualDisplay(null /* projection */, config, callback, handler,
- null /* windowContext */);
+ return createVirtualDisplay(null /* projection */, config, callback, handler);
}
// TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService)
@@ -1122,15 +1121,13 @@ public final class DisplayManager {
if (surface != null) {
builder.setSurface(surface);
}
- return createVirtualDisplay(projection, builder.build(), callback, handler,
- null /* windowContext */);
+ return createVirtualDisplay(projection, builder.build(), callback, handler);
}
/** @hide */
public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
@NonNull VirtualDisplayConfig virtualDisplayConfig,
- @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
- @Nullable Context windowContext) {
+ @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
Executor executor = null;
// If callback is null, the executor will not be used. Avoid creating the handler and the
// handler executor.
@@ -1139,7 +1136,7 @@ public final class DisplayManager {
Handler.createAsync(handler != null ? handler.getLooper() : Looper.myLooper()));
}
return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
- executor, windowContext);
+ executor);
}
/**
@@ -1610,7 +1607,7 @@ public final class DisplayManager {
throw ex.rethrowFromSystemServer();
}
return DisplayManagerGlobal.getInstance().createVirtualDisplayWrapper(virtualDisplayConfig,
- null, callbackWrapper, displayId);
+ callbackWrapper, displayId);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f419ae4b8caf..3be82bc2b4f9 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -635,7 +635,7 @@ public final class DisplayManagerGlobal {
public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
@NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
- @Nullable Executor executor, @Nullable Context windowContext) {
+ @Nullable Executor executor) {
VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
@@ -645,7 +645,7 @@ public final class DisplayManagerGlobal {
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
- return createVirtualDisplayWrapper(virtualDisplayConfig, windowContext, callbackWrapper,
+ return createVirtualDisplayWrapper(virtualDisplayConfig, callbackWrapper,
displayId);
}
@@ -655,7 +655,7 @@ public final class DisplayManagerGlobal {
*/
@Nullable
public VirtualDisplay createVirtualDisplayWrapper(VirtualDisplayConfig virtualDisplayConfig,
- Context windowContext, IVirtualDisplayCallback callbackWrapper, int displayId) {
+ IVirtualDisplayCallback callbackWrapper, int displayId) {
if (displayId < 0) {
Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName());
return null;
@@ -672,7 +672,7 @@ public final class DisplayManagerGlobal {
return null;
}
return new VirtualDisplay(this, display, callbackWrapper,
- virtualDisplayConfig.getSurface(), windowContext);
+ virtualDisplayConfig.getSurface());
}
public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 02ab8be5fd1a..051ce636484c 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -15,8 +15,6 @@
*/
package android.hardware.display;
-import android.annotation.Nullable;
-import android.content.Context;
import android.view.Display;
import android.view.Surface;
@@ -38,19 +36,12 @@ public final class VirtualDisplay {
private final Display mDisplay;
private IVirtualDisplayCallback mToken;
private Surface mSurface;
- /**
- * Store the WindowContext in a field. If it is a local variable, and it is garbage collected
- * during a MediaProjection session, the WindowContainer listener no longer exists.
- */
- @Nullable private final Context mWindowContext;
-
- VirtualDisplay(DisplayManagerGlobal global, Display display,
- IVirtualDisplayCallback token, Surface surface, Context windowContext) {
+ VirtualDisplay(DisplayManagerGlobal global, Display display, IVirtualDisplayCallback token,
+ Surface surface) {
mGlobal = global;
mDisplay = display;
mToken = token;
mSurface = surface;
- mWindowContext = windowContext;
}
/**
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 03d6d91d1e78..a62d74ed9f16 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -28,7 +28,6 @@ import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
-import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.Surface;
@@ -54,8 +53,6 @@ public final class VirtualDisplayConfig implements Parcelable {
private final int mDisplayIdToMirror;
private final boolean mWindowManagerMirroringEnabled;
private ArraySet<String> mDisplayCategories = null;
- @Nullable
- private ContentRecordingSession mContentRecordingSession;
private final float mRequestedRefreshRate;
private VirtualDisplayConfig(
@@ -68,7 +65,6 @@ public final class VirtualDisplayConfig implements Parcelable {
@Nullable String uniqueId,
int displayIdToMirror,
boolean windowManagerMirroringEnabled,
- ContentRecordingSession session,
@NonNull ArraySet<String> displayCategories,
float requestedRefreshRate) {
mName = name;
@@ -80,7 +76,6 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId = uniqueId;
mDisplayIdToMirror = displayIdToMirror;
mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
- mContentRecordingSession = session;
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
}
@@ -161,16 +156,6 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
- * Returns the recording session associated with this {@link VirtualDisplay}. Only used for
- * recording via {@link MediaProjection}.
- * @hide
- */
- @Nullable
- public ContentRecordingSession getContentRecordingSession() {
- return mContentRecordingSession;
- }
-
- /**
* Returns the display categories.
*
* @see Builder#setDisplayCategories
@@ -201,7 +186,6 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeString8(mUniqueId);
dest.writeInt(mDisplayIdToMirror);
dest.writeBoolean(mWindowManagerMirroringEnabled);
- dest.writeTypedObject(mContentRecordingSession, flags);
dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
}
@@ -227,7 +211,6 @@ public final class VirtualDisplayConfig implements Parcelable {
&& Objects.equals(mUniqueId, that.mUniqueId)
&& mDisplayIdToMirror == that.mDisplayIdToMirror
&& mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
- && Objects.equals(mContentRecordingSession, that.mContentRecordingSession)
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
&& mRequestedRefreshRate == that.mRequestedRefreshRate;
}
@@ -236,8 +219,8 @@ public final class VirtualDisplayConfig implements Parcelable {
public int hashCode() {
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
- mDisplayIdToMirror, mWindowManagerMirroringEnabled, mContentRecordingSession,
- mDisplayCategories, mRequestedRefreshRate);
+ mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
+ mRequestedRefreshRate);
return hashCode;
}
@@ -254,7 +237,6 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mUniqueId=" + mUniqueId
+ " mDisplayIdToMirror=" + mDisplayIdToMirror
+ " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
- + " mContentRecordingSession=" + mContentRecordingSession
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ ")";
@@ -270,7 +252,6 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId = in.readString8();
mDisplayIdToMirror = in.readInt();
mWindowManagerMirroringEnabled = in.readBoolean();
- mContentRecordingSession = in.readTypedObject(ContentRecordingSession.CREATOR);
mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
}
@@ -302,8 +283,6 @@ public final class VirtualDisplayConfig implements Parcelable {
private String mUniqueId = null;
private int mDisplayIdToMirror = DEFAULT_DISPLAY;
private boolean mWindowManagerMirroringEnabled = false;
- @Nullable
- private ContentRecordingSession mContentRecordingSession;
private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
@@ -396,18 +375,6 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
- * Sets the recording session associated with this {@link VirtualDisplay}. Only used for
- * recording via {@link MediaProjection}.
- *
- * @hide
- */
- @NonNull
- public Builder setContentRecordingSession(@Nullable ContentRecordingSession session) {
- mContentRecordingSession = session;
- return this;
- }
-
- /**
* Sets the display categories.
*
* <p>The categories of the display indicate the type of activities allowed to run on that
@@ -468,7 +435,6 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId,
mDisplayIdToMirror,
mWindowManagerMirroringEnabled,
- mContentRecordingSession,
mDisplayCategories,
mRequestedRefreshRate);
}
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index c1c13171f83c..fdecb8bc61ab 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -56,7 +56,7 @@ public final class ContentRecordingSession implements Parcelable {
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
- private int mDisplayId = INVALID_DISPLAY;
+ private int mVirtualDisplayId = INVALID_DISPLAY;
/**
* The content to record.
@@ -65,10 +65,17 @@ public final class ContentRecordingSession implements Parcelable {
private int mContentToRecord = RECORD_CONTENT_DISPLAY;
/**
+ * Unique logical identifier of the {@link android.view.Display} to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_DISPLAY}, then is
+ * a valid display id.
+ */
+ private int mDisplayToRecord = INVALID_DISPLAY;
+
+ /**
* The token of the layer of the hierarchy to record.
- * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
- * represents the WindowToken corresponding to the DisplayContent to record.
- * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
* represents the {@link android.window.WindowContainerToken} of the Task to record.
*/
@Nullable
@@ -89,12 +96,11 @@ public final class ContentRecordingSession implements Parcelable {
}
/**
- * Returns an instance initialized for display recording.
+ * Returns an instance initialized for recording the indicated display.
*/
- public static ContentRecordingSession createDisplaySession(
- @NonNull IBinder displayContentWindowToken) {
- return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_DISPLAY)
- .setTokenToRecord(displayContentWindowToken);
+ public static ContentRecordingSession createDisplaySession(int displayToMirror) {
+ return new ContentRecordingSession().setDisplayToRecord(displayToMirror)
+ .setContentToRecord(RECORD_CONTENT_DISPLAY);
}
/**
@@ -108,10 +114,20 @@ public final class ContentRecordingSession implements Parcelable {
/**
* Returns {@code true} if this is a valid session.
+ *
+ * <p>A valid session has a non-null token for task recording, or a valid id for the display to
+ * record.
*/
public static boolean isValid(ContentRecordingSession session) {
- return session != null && (session.getDisplayId() > INVALID_DISPLAY
- && session.getTokenToRecord() != null);
+ if (session == null) {
+ return false;
+ }
+ final boolean isValidTaskSession = session.getContentToRecord() == RECORD_CONTENT_TASK
+ && session.getTokenToRecord() != null;
+ final boolean isValidDisplaySession = session.getContentToRecord() == RECORD_CONTENT_DISPLAY
+ && session.getDisplayToRecord() > INVALID_DISPLAY;
+ return session.getVirtualDisplayId() > INVALID_DISPLAY
+ && (isValidTaskSession || isValidDisplaySession);
}
/**
@@ -121,7 +137,7 @@ public final class ContentRecordingSession implements Parcelable {
public static boolean isProjectionOnSameDisplay(ContentRecordingSession session,
ContentRecordingSession incomingSession) {
return session != null && incomingSession != null
- && session.getDisplayId() == incomingSession.getDisplayId();
+ && session.getVirtualDisplayId() == incomingSession.getVirtualDisplayId();
}
@@ -161,11 +177,12 @@ public final class ContentRecordingSession implements Parcelable {
@DataClass.Generated.Member
/* package-private */ ContentRecordingSession(
- int displayId,
+ int virtualDisplayId,
@RecordContent int contentToRecord,
+ int displayToRecord,
@Nullable IBinder tokenToRecord,
boolean waitingToRecord) {
- this.mDisplayId = displayId;
+ this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
@@ -176,6 +193,7 @@ public final class ContentRecordingSession implements Parcelable {
+ "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
}
+ this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord;
@@ -187,8 +205,8 @@ public final class ContentRecordingSession implements Parcelable {
* recorded content rendered to its surface.
*/
@DataClass.Generated.Member
- public int getDisplayId() {
- return mDisplayId;
+ public int getVirtualDisplayId() {
+ return mVirtualDisplayId;
}
/**
@@ -200,10 +218,20 @@ public final class ContentRecordingSession implements Parcelable {
}
/**
- * {The token of the layer of the hierarchy to record.
- * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
- * represents the WindowToken corresponding to the DisplayContent to record.
- * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+ * Unique logical identifier of the {@link android.view.Display} to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_DISPLAY}, then is
+ * a valid display id.
+ */
+ @DataClass.Generated.Member
+ public int getDisplayToRecord() {
+ return mDisplayToRecord;
+ }
+
+ /**
+ * The token of the layer of the hierarchy to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
* represents the {@link android.window.WindowContainerToken} of the Task to record.
*/
@DataClass.Generated.Member
@@ -227,8 +255,8 @@ public final class ContentRecordingSession implements Parcelable {
* recorded content rendered to its surface.
*/
@DataClass.Generated.Member
- public @NonNull ContentRecordingSession setDisplayId( int value) {
- mDisplayId = value;
+ public @NonNull ContentRecordingSession setVirtualDisplayId( int value) {
+ mVirtualDisplayId = value;
return this;
}
@@ -251,10 +279,21 @@ public final class ContentRecordingSession implements Parcelable {
}
/**
- * {The token of the layer of the hierarchy to record.
- * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
- * represents the WindowToken corresponding to the DisplayContent to record.
- * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+ * Unique logical identifier of the {@link android.view.Display} to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_DISPLAY}, then is
+ * a valid display id.
+ */
+ @DataClass.Generated.Member
+ public @NonNull ContentRecordingSession setDisplayToRecord( int value) {
+ mDisplayToRecord = value;
+ return this;
+ }
+
+ /**
+ * The token of the layer of the hierarchy to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
* represents the {@link android.window.WindowContainerToken} of the Task to record.
*/
@DataClass.Generated.Member
@@ -282,8 +321,9 @@ public final class ContentRecordingSession implements Parcelable {
// String fieldNameToString() { ... }
return "ContentRecordingSession { " +
- "displayId = " + mDisplayId + ", " +
+ "virtualDisplayId = " + mVirtualDisplayId + ", " +
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
+ "displayToRecord = " + mDisplayToRecord + ", " +
"tokenToRecord = " + mTokenToRecord + ", " +
"waitingToRecord = " + mWaitingToRecord +
" }";
@@ -302,8 +342,9 @@ public final class ContentRecordingSession implements Parcelable {
ContentRecordingSession that = (ContentRecordingSession) o;
//noinspection PointlessBooleanExpression
return true
- && mDisplayId == that.mDisplayId
+ && mVirtualDisplayId == that.mVirtualDisplayId
&& mContentToRecord == that.mContentToRecord
+ && mDisplayToRecord == that.mDisplayToRecord
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
&& mWaitingToRecord == that.mWaitingToRecord;
}
@@ -315,8 +356,9 @@ public final class ContentRecordingSession implements Parcelable {
// int fieldNameHashCode() { ... }
int _hash = 1;
- _hash = 31 * _hash + mDisplayId;
+ _hash = 31 * _hash + mVirtualDisplayId;
_hash = 31 * _hash + mContentToRecord;
+ _hash = 31 * _hash + mDisplayToRecord;
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
_hash = 31 * _hash + Boolean.hashCode(mWaitingToRecord);
return _hash;
@@ -329,11 +371,12 @@ public final class ContentRecordingSession implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mWaitingToRecord) flg |= 0x8;
- if (mTokenToRecord != null) flg |= 0x4;
+ if (mWaitingToRecord) flg |= 0x10;
+ if (mTokenToRecord != null) flg |= 0x8;
dest.writeByte(flg);
- dest.writeInt(mDisplayId);
+ dest.writeInt(mVirtualDisplayId);
dest.writeInt(mContentToRecord);
+ dest.writeInt(mDisplayToRecord);
if (mTokenToRecord != null) dest.writeStrongBinder(mTokenToRecord);
}
@@ -349,12 +392,13 @@ public final class ContentRecordingSession implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean waitingToRecord = (flg & 0x8) != 0;
- int displayId = in.readInt();
+ boolean waitingToRecord = (flg & 0x10) != 0;
+ int virtualDisplayId = in.readInt();
int contentToRecord = in.readInt();
- IBinder tokenToRecord = (flg & 0x4) == 0 ? null : (IBinder) in.readStrongBinder();
+ int displayToRecord = in.readInt();
+ IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder();
- this.mDisplayId = displayId;
+ this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
@@ -365,6 +409,7 @@ public final class ContentRecordingSession implements Parcelable {
+ "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
}
+ this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord;
@@ -392,8 +437,9 @@ public final class ContentRecordingSession implements Parcelable {
@DataClass.Generated.Member
public static final class Builder {
- private int mDisplayId;
+ private int mVirtualDisplayId;
private @RecordContent int mContentToRecord;
+ private int mDisplayToRecord;
private @Nullable IBinder mTokenToRecord;
private boolean mWaitingToRecord;
@@ -407,10 +453,10 @@ public final class ContentRecordingSession implements Parcelable {
* recorded content rendered to its surface.
*/
@DataClass.Generated.Member
- public @NonNull Builder setDisplayId(int value) {
+ public @NonNull Builder setVirtualDisplayId(int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
- mDisplayId = value;
+ mVirtualDisplayId = value;
return this;
}
@@ -426,16 +472,29 @@ public final class ContentRecordingSession implements Parcelable {
}
/**
- * {The token of the layer of the hierarchy to record.
- * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then
- * represents the WindowToken corresponding to the DisplayContent to record.
- * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
+ * Unique logical identifier of the {@link android.view.Display} to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_DISPLAY}, then is
+ * a valid display id.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDisplayToRecord(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mDisplayToRecord = value;
+ return this;
+ }
+
+ /**
+ * The token of the layer of the hierarchy to record.
+ *
+ * <p>If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then
* represents the {@link android.window.WindowContainerToken} of the Task to record.
*/
@DataClass.Generated.Member
public @NonNull Builder setTokenToRecord(@NonNull IBinder value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
+ mBuilderFieldsSet |= 0x8;
mTokenToRecord = value;
return this;
}
@@ -449,7 +508,7 @@ public final class ContentRecordingSession implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setWaitingToRecord(boolean value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
+ mBuilderFieldsSet |= 0x10;
mWaitingToRecord = value;
return this;
}
@@ -457,30 +516,34 @@ public final class ContentRecordingSession implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull ContentRecordingSession build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
+ mBuilderFieldsSet |= 0x20; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
- mDisplayId = INVALID_DISPLAY;
+ mVirtualDisplayId = INVALID_DISPLAY;
}
if ((mBuilderFieldsSet & 0x2) == 0) {
mContentToRecord = RECORD_CONTENT_DISPLAY;
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mTokenToRecord = null;
+ mDisplayToRecord = INVALID_DISPLAY;
}
if ((mBuilderFieldsSet & 0x8) == 0) {
+ mTokenToRecord = null;
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
mWaitingToRecord = false;
}
ContentRecordingSession o = new ContentRecordingSession(
- mDisplayId,
+ mVirtualDisplayId,
mContentToRecord,
+ mDisplayToRecord,
mTokenToRecord,
mWaitingToRecord);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
+ if ((mBuilderFieldsSet & 0x20) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -488,10 +551,10 @@ public final class ContentRecordingSession implements Parcelable {
}
@DataClass.Generated(
- time = 1678817765846L,
+ time = 1679855157534L,
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 mDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingToRecord\npublic static android.view.ContentRecordingSession createDisplaySession(android.os.IBinder)\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\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingToRecord\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)")
@Deprecated
private void __metadata() {}
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java
index 4e5906427af4..51d73d57c480 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayConfigTest.java
@@ -19,11 +19,8 @@ package android.hardware.display;
import static com.google.common.truth.Truth.assertThat;
import android.graphics.SurfaceTexture;
-import android.os.Binder;
-import android.os.IBinder;
import android.os.Parcel;
import android.util.DisplayMetrics;
-import android.view.ContentRecordingSession;
import android.view.Surface;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -55,9 +52,6 @@ public class VirtualDisplayConfigTest {
// Values for hidden APIs.
private static final int DISPLAY_ID_TO_MIRROR = 10;
- private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken");
- private static final ContentRecordingSession CONTENT_RECORDING_SESSION =
- ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
private final Surface mSurface = new Surface(new SurfaceTexture(/*texName=*/1));
@@ -99,7 +93,6 @@ public class VirtualDisplayConfigTest {
.setRequestedRefreshRate(REQUESTED_REFRESH_RATE)
.setDisplayIdToMirror(DISPLAY_ID_TO_MIRROR)
.setWindowManagerMirroringEnabled(true)
- .setContentRecordingSession(CONTENT_RECORDING_SESSION)
.build();
}
@@ -113,6 +106,5 @@ public class VirtualDisplayConfigTest {
assertThat(config.getRequestedRefreshRate()).isEqualTo(REQUESTED_REFRESH_RATE);
assertThat(config.getDisplayIdToMirror()).isEqualTo(DISPLAY_ID_TO_MIRROR);
assertThat(config.isWindowManagerMirroringEnabled()).isTrue();
- assertThat(config.getContentRecordingSession()).isEqualTo(CONTENT_RECORDING_SESSION);
}
}
diff --git a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
index b3fe5c8addfd..17980ac5e735 100644
--- a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
+++ b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
@@ -50,7 +50,7 @@ public class ContentRecordingSessionTest {
@Test
public void testParcelable() {
ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
- session.setDisplayId(DISPLAY_ID);
+ session.setVirtualDisplayId(DISPLAY_ID);
Parcel parcel = Parcel.obtain();
session.writeToParcel(parcel, 0 /* flags */);
@@ -70,39 +70,84 @@ public class ContentRecordingSessionTest {
@Test
public void testDisplayConstructor() {
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
- WINDOW_TOKEN);
+ DEFAULT_DISPLAY);
assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
- assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN);
+ assertThat(session.getTokenToRecord()).isNull();
}
@Test
- public void testIsValid() {
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
+ public void testIsValid_displaySession() {
+ // Canonical display session.
+ ContentRecordingSession displaySession = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
+ displaySession.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(displaySession)).isTrue();
+
+ // Virtual display id values.
+ ContentRecordingSession displaySession0 = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(displaySession0)).isFalse();
+
+ ContentRecordingSession displaySession1 = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
+ displaySession1.setVirtualDisplayId(INVALID_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(displaySession1)).isFalse();
+
+ // Display id values.
+ ContentRecordingSession displaySession2 = ContentRecordingSession.createDisplaySession(
+ INVALID_DISPLAY);
+ displaySession2.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(displaySession2)).isFalse();
+
+ displaySession2.setDisplayToRecord(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(displaySession2)).isTrue();
+ }
+
+ @Test
+ public void testIsValid_taskSession() {
+ // Canonical task session.
+ ContentRecordingSession taskSession = ContentRecordingSession.createTaskSession(
WINDOW_TOKEN);
- assertThat(ContentRecordingSession.isValid(session)).isFalse();
+ taskSession.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(taskSession)).isTrue();
- session.setDisplayId(DEFAULT_DISPLAY);
- assertThat(ContentRecordingSession.isValid(session)).isTrue();
+ // Virtual display id values.
+ ContentRecordingSession taskSession0 = ContentRecordingSession.createTaskSession(
+ WINDOW_TOKEN);
+ assertThat(ContentRecordingSession.isValid(taskSession0)).isFalse();
- session.setDisplayId(INVALID_DISPLAY);
- assertThat(ContentRecordingSession.isValid(session)).isFalse();
+ ContentRecordingSession taskSession1 = ContentRecordingSession.createTaskSession(
+ WINDOW_TOKEN);
+ taskSession1.setVirtualDisplayId(INVALID_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(taskSession1)).isFalse();
+
+ // Window container values.
+ ContentRecordingSession taskSession3 = ContentRecordingSession.createTaskSession(null);
+ taskSession3.setVirtualDisplayId(DEFAULT_DISPLAY);
+ assertThat(ContentRecordingSession.isValid(taskSession3)).isFalse();
+
+ ContentRecordingSession taskSession4 = ContentRecordingSession.createTaskSession(
+ WINDOW_TOKEN);
+ taskSession4.setVirtualDisplayId(DEFAULT_DISPLAY);
+ taskSession4.setTokenToRecord(null);
+ assertThat(ContentRecordingSession.isValid(taskSession4)).isFalse();
}
@Test
public void testIsProjectionOnSameDisplay() {
assertThat(ContentRecordingSession.isProjectionOnSameDisplay(null, null)).isFalse();
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
- WINDOW_TOKEN);
- session.setDisplayId(DEFAULT_DISPLAY);
+ DEFAULT_DISPLAY);
+ session.setVirtualDisplayId(DEFAULT_DISPLAY);
assertThat(ContentRecordingSession.isProjectionOnSameDisplay(session, null)).isFalse();
ContentRecordingSession incomingSession = ContentRecordingSession.createDisplaySession(
- WINDOW_TOKEN);
- incomingSession.setDisplayId(DEFAULT_DISPLAY);
+ DEFAULT_DISPLAY);
+ incomingSession.setVirtualDisplayId(DEFAULT_DISPLAY);
assertThat(ContentRecordingSession.isProjectionOnSameDisplay(session,
incomingSession)).isTrue();
- incomingSession.setDisplayId(DEFAULT_DISPLAY + 1);
+ incomingSession.setVirtualDisplayId(DEFAULT_DISPLAY + 1);
assertThat(ContentRecordingSession.isProjectionOnSameDisplay(session,
incomingSession)).isFalse();
}
@@ -110,10 +155,10 @@ public class ContentRecordingSessionTest {
@Test
public void testEquals() {
ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
- session.setDisplayId(DISPLAY_ID);
+ session.setVirtualDisplayId(DISPLAY_ID);
ContentRecordingSession session2 = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
- session2.setDisplayId(DISPLAY_ID);
+ session2.setVirtualDisplayId(DISPLAY_ID);
assertThat(session).isEqualTo(session2);
}
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a73010ba0e41..2afd54b984a4 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -565,12 +565,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
- "-1549923951": {
- "message": "Content Recording: Unable to retrieve window container to start recording for display %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"-1545962566": {
"message": "View server did not start",
"level": "WARN",
@@ -1477,6 +1471,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-732715767": {
+ "message": "Unable to retrieve window container to start recording for display %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"-729530161": {
"message": "Moving to DESTROYED: %s (no app)",
"level": "VERBOSE",
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 6ed8f090608e..e040bf496723 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -16,8 +16,6 @@
package android.media.projection;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
@@ -29,13 +27,10 @@ import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.view.ContentRecordingSession;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
@@ -72,21 +67,17 @@ public final class MediaProjection {
private final IMediaProjection mImpl;
private final Context mContext;
private final DisplayManager mDisplayManager;
- private final IMediaProjectionManager mProjectionService;
@NonNull
private final Map<Callback, CallbackRecord> mCallbacks = new ArrayMap<>();
/** @hide */
public MediaProjection(Context context, IMediaProjection impl) {
- this(context, impl, IMediaProjectionManager.Stub.asInterface(
- ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE)),
- context.getSystemService(DisplayManager.class));
+ this(context, impl, context.getSystemService(DisplayManager.class));
}
/** @hide */
@VisibleForTesting
- public MediaProjection(Context context, IMediaProjection impl, IMediaProjectionManager service,
- DisplayManager displayManager) {
+ public MediaProjection(Context context, IMediaProjection impl, DisplayManager displayManager) {
mContext = context;
mImpl = impl;
try {
@@ -94,7 +85,6 @@ public final class MediaProjection {
} catch (RemoteException e) {
throw new RuntimeException("Failed to start media projection", e);
}
- mProjectionService = service;
mDisplayManager = displayManager;
}
@@ -223,38 +213,22 @@ public final class MediaProjection {
public VirtualDisplay createVirtualDisplay(
@NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
- try {
- final IBinder launchCookie = mImpl.getLaunchCookie();
- Context windowContext = null;
- ContentRecordingSession session;
- if (launchCookie == null) {
- windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
- TYPE_APPLICATION, null /* options */);
- session = ContentRecordingSession.createDisplaySession(
- windowContext.getWindowContextToken());
- } else {
- session = ContentRecordingSession.createTaskSession(launchCookie);
- }
- // Pass in the current session details, so they are guaranteed to only be set in
- // WindowManagerService AFTER a VirtualDisplay is constructed (assuming there are no
- // errors during set-up).
- virtualDisplayConfig.setContentRecordingSession(session);
- virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
- final VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(this,
- virtualDisplayConfig.build(), callback, handler, windowContext);
- if (virtualDisplay == null) {
- // Since WindowManager handling a new display and DisplayManager creating a new
- // VirtualDisplay is async, WindowManager may have tried to start task recording
- // and encountered an error that required stopping recording entirely. The
- // VirtualDisplay would then be null and the MediaProjection is no longer active.
- Slog.w(TAG, "Failed to create virtual display.");
- return null;
- }
- return virtualDisplay;
- } catch (RemoteException e) {
- // Can not capture if WMS is not accessible, so bail out.
- throw e.rethrowFromSystemServer();
+ // Pass in the current session details, so they are guaranteed to only be set in
+ // WindowManagerService AFTER a VirtualDisplay is constructed (assuming there are no
+ // errors during set-up).
+ virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
+ // Do not declare a display id to mirror; default to the default display.
+ final VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(this,
+ virtualDisplayConfig.build(), callback, handler);
+ if (virtualDisplay == null) {
+ // Since WindowManager handling a new display and DisplayManager creating a new
+ // VirtualDisplay is async, WindowManager may have tried to start task recording
+ // and encountered an error that required stopping recording entirely. The
+ // VirtualDisplay would then be null and the MediaProjection is no longer active.
+ Slog.w(TAG, "Failed to create virtual display.");
+ return null;
}
+ return virtualDisplay;
}
/**
diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java
index bf616d78cdb6..2a5674e4ded3 100644
--- a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java
+++ b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
import android.compat.testing.PlatformCompatChangeRule;
-import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
@@ -88,8 +87,6 @@ public class MediaProjectionTest {
@Mock
private MediaProjection.Callback mMediaProjectionCallback;
@Mock
- private IMediaProjectionManager mIMediaProjectionManager;
- @Mock
private Display mDisplay;
@Mock
private VirtualDisplay.Callback mVirtualDisplayCallback;
@@ -115,12 +112,10 @@ public class MediaProjectionTest {
.strictness(Strictness.LENIENT)
.startMocking();
- doReturn(mock(IBinder.class)).when(mIMediaProjectionManager).asBinder();
-
// Support the MediaProjection instance.
mFakeIMediaProjection.setLaunchCookie(mock(IBinder.class));
mMediaProjection = new MediaProjection(mTestableContext, mFakeIMediaProjection,
- mIMediaProjectionManager, mDisplayManager);
+ mDisplayManager);
// Support creation of the VirtualDisplay.
mTestableContext.addMockSystemService(DisplayManager.class, mDisplayManager);
@@ -128,8 +123,7 @@ public class MediaProjectionTest {
doReturn(DEFAULT_DISPLAY + 7).when(mDisplay).getDisplayId();
doReturn(mVirtualDisplay).when(mDisplayManager).createVirtualDisplay(
any(MediaProjection.class), any(VirtualDisplayConfig.class),
- nullable(VirtualDisplay.Callback.class), nullable(Handler.class),
- nullable(Context.class));
+ nullable(VirtualDisplay.Callback.class), nullable(Handler.class));
}
@After
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ea0a4ab56320..ad5ae5f4d0d9 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1514,20 +1514,37 @@ public final class DisplayManagerService extends SystemService {
}
}
- // When calling WindowManagerService#setContentRecordingSession, WindowManagerService
- // attempts to acquire a lock before executing its main body. Due to this, we need
- // to be sure that it isn't called while the DisplayManagerService is also holding
- // a lock, to avoid a deadlock scenario.
- final ContentRecordingSession session =
- virtualDisplayConfig.getContentRecordingSession();
+ // Build a session describing the MediaProjection instance, if there is one. A session
+ // for a VirtualDisplay or physical display mirroring is handled in DisplayContent.
+ ContentRecordingSession session = null;
+ try {
+ if (projection != null) {
+ IBinder launchCookie = projection.getLaunchCookie();
+ if (launchCookie == null) {
+ // Record a particular display.
+ session = ContentRecordingSession.createDisplaySession(
+ virtualDisplayConfig.getDisplayIdToMirror());
+ } else {
+ // Record a single task indicated by the launch cookie.
+ session = ContentRecordingSession.createTaskSession(launchCookie);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to retrieve the projection's launch cookie", e);
+ }
+
// Ensure session details are only set when mirroring (through VirtualDisplay flags or
// MediaProjection).
final boolean shouldMirror =
projection != null || (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0;
+ // When calling WindowManagerService#setContentRecordingSession, WindowManagerService
+ // attempts to acquire a lock before executing its main body. Due to this, we need
+ // to be sure that it isn't called while the DisplayManagerService is also holding
+ // a lock, to avoid a deadlock scenario.
if (shouldMirror && displayId != Display.INVALID_DISPLAY && session != null) {
// Only attempt to set content recording session if there are details to set and a
// VirtualDisplay has been successfully constructed.
- session.setDisplayId(displayId);
+ session.setVirtualDisplayId(displayId);
// We set the content recording session here on the server side instead of using
// a second AIDL call in MediaProjection. By ensuring that a virtual display has
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 51c5a89189c5..c26c1d456a65 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -302,8 +302,7 @@ class Vr2dDisplay {
builder.setUniqueId(UNIQUE_DISPLAY_ID);
builder.setFlags(flags);
mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
- builder.build(), null /* callback */, null /* handler */,
- null /* windowContext */);
+ builder.build(), null /* callback */, null /* handler */);
if (mVirtualDisplay != null) {
updateDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index d358eb5d38db..f1c5f91146ab 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -124,7 +124,7 @@ final class ContentRecorder implements WindowContainerListener {
*/
@VisibleForTesting void updateRecording() {
if (isCurrentlyRecording() && (mDisplayContent.getLastHasContent()
- || mDisplayContent.getDisplay().getState() == Display.STATE_OFF)) {
+ || mDisplayContent.getDisplayInfo().state == Display.STATE_OFF)) {
pauseRecording();
} else {
// Display no longer has content, or now has a surface to write to, so try to start
@@ -271,7 +271,7 @@ final class ContentRecorder implements WindowContainerListener {
// Only record if this display does not have its own content, is not recording already,
// and if this display is on (it has a surface to write output to).
if (mDisplayContent.getLastHasContent() || isCurrentlyRecording()
- || mDisplayContent.getDisplay().getState() == Display.STATE_OFF
+ || mDisplayContent.getDisplayInfo().state == Display.STATE_OFF
|| mContentRecordingSession == null) {
return;
}
@@ -294,7 +294,7 @@ final class ContentRecorder implements WindowContainerListener {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Display %d has no content and is on, so start recording for "
+ "state %d",
- mDisplayContent.getDisplayId(), mDisplayContent.getDisplay().getState());
+ mDisplayContent.getDisplayId(), mDisplayContent.getDisplayInfo().state);
// Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
mRecordedSurface = SurfaceControl.mirrorSurface(
@@ -324,7 +324,7 @@ final class ContentRecorder implements WindowContainerListener {
mRecordedWindowContainer.asTask().isVisibleRequested());
} else {
int currentDisplayState =
- mRecordedWindowContainer.asDisplayContent().getDisplay().getState();
+ mRecordedWindowContainer.asDisplayContent().getDisplayInfo().state;
mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
currentDisplayState != DISPLAY_STATE_OFF);
}
@@ -347,34 +347,25 @@ final class ContentRecorder implements WindowContainerListener {
@Nullable
private WindowContainer retrieveRecordedWindowContainer() {
final int contentToRecord = mContentRecordingSession.getContentToRecord();
- // Given the WindowToken of the region to record, retrieve the associated
- // SurfaceControl.
final IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord();
- if (tokenToRecord == null) {
- handleStartRecordingFailed();
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to start recording due to null token for display %d",
- mDisplayContent.getDisplayId());
- return null;
- }
switch (contentToRecord) {
case RECORD_CONTENT_DISPLAY:
- final WindowContainer wc =
- mDisplayContent.mWmService.mWindowContextListenerController.getContainer(
- tokenToRecord);
- if (wc == null) {
- // Fall back to mirroring using the data sent to DisplayManager
+ // Given the id of the display to record, retrieve the associated DisplayContent.
+ final DisplayContent dc =
+ mDisplayContent.mWmService.mRoot.getDisplayContent(
+ mContentRecordingSession.getDisplayToRecord());
+ if (dc == null) {
+ // Fall back to screenrecording using the data sent to DisplayManager
mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring(
mDisplayContent.getDisplayId(), false);
handleStartRecordingFailed();
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Unable to retrieve window container to start "
- + "recording for display %d",
- mDisplayContent.getDisplayId());
+ "Unable to retrieve window container to start recording for "
+ + "display %d", mDisplayContent.getDisplayId());
return null;
}
// TODO(206461622) Migrate to using the RootDisplayArea
- return wc.getDisplayContent();
+ return dc;
case RECORD_CONTENT_TASK:
if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_RECORD_TASK_FEATURE, false)) {
@@ -384,6 +375,16 @@ final class ContentRecorder implements WindowContainerListener {
mDisplayContent.getDisplayId());
return null;
}
+ // Given the WindowToken of the region to record, retrieve the associated
+ // SurfaceControl.
+ if (tokenToRecord == null) {
+ handleStartRecordingFailed();
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unable to start recording due to null token for "
+ + "display %d",
+ mDisplayContent.getDisplayId());
+ return null;
+ }
Task taskToRecord = WindowContainer.fromBinder(tokenToRecord).asTask();
if (taskToRecord == null) {
handleStartRecordingFailed();
diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java
index d60addc42831..a41dcc66ff52 100644
--- a/services/core/java/com/android/server/wm/ContentRecordingController.java
+++ b/services/core/java/com/android/server/wm/ContentRecordingController.java
@@ -86,7 +86,7 @@ final class ContentRecordingController {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Ignoring session on same display %d, with an existing "
+ "session %s",
- incomingSession.getDisplayId(), mSession.getDisplayId());
+ incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
return;
}
DisplayContent incomingDisplayContent = null;
@@ -94,10 +94,10 @@ final class ContentRecordingController {
if (incomingSession != null) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Handle incoming session on display %d, with a "
- + "pre-existing session %s", incomingSession.getDisplayId(),
- mSession == null ? null : mSession.getDisplayId());
+ + "pre-existing session %s", incomingSession.getVirtualDisplayId(),
+ mSession == null ? null : mSession.getVirtualDisplayId());
incomingDisplayContent = wmService.mRoot.getDisplayContentOrCreate(
- incomingSession.getDisplayId());
+ incomingSession.getVirtualDisplayId());
incomingDisplayContent.setContentRecordingSession(incomingSession);
// TODO(b/270118861) When user grants consent to re-use, explicitly ask ContentRecorder
// to update, since no config/display change arrives. Mark recording as black.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ef01cc8cd61a..37ee2e2a1187 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6545,7 +6545,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* mirror using MediaProjection. When done through MediaProjection API, the
* ContentRecordingSession will be created automatically.
*
- * This should only be called when there's no ContentRecordingSession already set for this
+ * <p>This should only be called when there's no ContentRecordingSession already set for this
* display. The code will ask DMS if this display should enable display mirroring and which
* displayId to mirror from.
*
@@ -6586,9 +6586,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mirrorDisplayId, mDisplayId);
}
+ // Create a session for mirroring the display content to this virtual display.
ContentRecordingSession session = ContentRecordingSession
- .createDisplaySession(mirrorDc.getDisplayUiContext().getWindowContextToken())
- .setDisplayId(mDisplayId);
+ .createDisplaySession(mirrorDc.getDisplayId())
+ .setVirtualDisplayId(mDisplayId);
setContentRecordingSession(session);
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Successfully created a ContentRecordingSession for "
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 6d2ce7fbe68d..b5237a5b34fd 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -21,6 +21,8 @@ import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
@@ -69,7 +71,6 @@ import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
@@ -111,6 +112,7 @@ import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -237,6 +239,8 @@ public class DisplayManagerServiceTest {
@Mock IBinder mMockDisplayToken;
@Mock SensorManagerInternal mMockSensorManagerInternal;
+ @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -1030,6 +1034,7 @@ public class DisplayManagerServiceTest {
@Test
public void testCreateVirtualDisplay_setContentRecordingSessionSuccess()
throws RemoteException {
+ final int displayToRecord = 50;
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mMockWindowManagerInternal
.setContentRecordingSession(any(ContentRecordingSession.class)))
@@ -1040,8 +1045,7 @@ public class DisplayManagerServiceTest {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
builder.setUniqueId("uniqueId --- setContentRecordingSession true");
- builder.setContentRecordingSession(
- ContentRecordingSession.createDisplaySession(new Binder("")));
+ builder.setDisplayIdToMirror(displayToRecord);
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -1052,6 +1056,12 @@ public class DisplayManagerServiceTest {
mMockAppToken /* callback */, projection, PACKAGE_NAME);
assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
+ mContentRecordingSessionCaptor.capture());
+ ContentRecordingSession session = mContentRecordingSessionCaptor.getValue();
+ assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
+ assertThat(session.getVirtualDisplayId()).isEqualTo(displayId);
+ assertThat(session.getDisplayToRecord()).isEqualTo(displayToRecord);
}
@Test
@@ -1066,8 +1076,6 @@ public class DisplayManagerServiceTest {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
builder.setUniqueId("uniqueId --- setContentRecordingSession false");
- builder.setContentRecordingSession(
- ContentRecordingSession.createDisplaySession(new Binder("")));
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -1081,6 +1089,41 @@ public class DisplayManagerServiceTest {
}
@Test
+ public void testCreateVirtualDisplay_setContentRecordingSession_taskSession()
+ throws RemoteException {
+ final int displayToRecord = 50;
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockWindowManagerInternal
+ .setContentRecordingSession(any(ContentRecordingSession.class)))
+ .thenReturn(true);
+ IMediaProjection projection = mock(IMediaProjection.class);
+ doReturn(mock(IBinder.class)).when(projection).getLaunchCookie();
+
+ doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection));
+
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setDisplayIdToMirror(displayToRecord);
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, projection, PACKAGE_NAME);
+
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ verify(mMockWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
+ mContentRecordingSessionCaptor.capture());
+ ContentRecordingSession session = mContentRecordingSessionCaptor.getValue();
+ assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK);
+ assertThat(session.getVirtualDisplayId()).isEqualTo(displayId);
+ assertThat(session.getTokenToRecord()).isNotNull();
+ }
+
+ @Test
public void testCreateVirtualDisplay_setContentRecordingSession_noProjection_noFlags() {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
@@ -1088,8 +1131,6 @@ public class DisplayManagerServiceTest {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
builder.setUniqueId("uniqueId --- setContentRecordingSession false");
- builder.setContentRecordingSession(
- ContentRecordingSession.createDisplaySession(new Binder("")));
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -1115,8 +1156,6 @@ public class DisplayManagerServiceTest {
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
builder.setUniqueId("uniqueId --- setContentRecordingSession false");
builder.setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
- builder.setContentRecordingSession(
- ContentRecordingSession.createDisplaySession(new Binder("")));
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
@@ -1147,8 +1186,6 @@ public class DisplayManagerServiceTest {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, 600, 800, 320);
builder.setUniqueId("uniqueId --- setContentRecordingSession false");
- builder.setContentRecordingSession(
- ContentRecordingSession.createDisplaySession(new Binder("")));
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 17f6d51a74f3..ad9f710fbbcc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -17,10 +17,12 @@
package com.android.server.wm;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.STATE_ON;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -28,6 +30,7 @@ import static com.android.server.wm.ContentRecorder.KEY_RECORD_TASK_FEATURE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,15 +43,12 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.display.VirtualDisplay;
-import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
-import android.util.DisplayMetrics;
import android.view.ContentRecordingSession;
+import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.Surface;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -75,11 +75,10 @@ import java.util.concurrent.CountDownLatch;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ContentRecorderTests extends WindowTestsBase {
- private static final IBinder TEST_TOKEN = new RecordingTestToken();
private static IBinder sTaskWindowContainerToken;
private Task mTask;
private final ContentRecordingSession mDisplaySession =
- ContentRecordingSession.createDisplaySession(TEST_TOKEN);
+ ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private ContentRecordingSession mTaskSession;
private static Point sSurfaceSize;
private ContentRecorder mContentRecorder;
@@ -89,8 +88,6 @@ public class ContentRecorderTests extends WindowTestsBase {
private ConfigListener mConfigListener;
private CountDownLatch mLatch;
- private VirtualDisplay mVirtualDisplay;
-
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -103,25 +100,25 @@ public class ContentRecorderTests extends WindowTestsBase {
doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
// GIVEN the VirtualDisplay associated with the session (so the display has state ON).
- mVirtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
- sSurfaceSize.x, sSurfaceSize.y,
- DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
- final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
- mWm.mRoot.onDisplayAdded(displayId);
- final DisplayContent virtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
+ DisplayInfo displayInfo = mDisplayInfo;
+ displayInfo.logicalWidth = sSurfaceSize.x;
+ displayInfo.logicalHeight = sSurfaceSize.y;
+ displayInfo.state = STATE_ON;
+ final DisplayContent virtualDisplayContent = createNewDisplay(displayInfo);
+ final int displayId = virtualDisplayContent.getDisplayId();
mContentRecorder = new ContentRecorder(virtualDisplayContent,
mMediaProjectionManagerWrapper);
spyOn(virtualDisplayContent);
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// record.
- setUpDefaultTaskDisplayAreaWindowToken();
- mDisplaySession.setDisplayId(displayId);
+ mDisplaySession.setVirtualDisplayId(displayId);
+ mDisplaySession.setDisplayToRecord(mDefaultDisplay.mDisplayId);
// GIVEN there is a window token associated with a task to record.
sTaskWindowContainerToken = setUpTaskWindowContainerToken(virtualDisplayContent);
mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
- mTaskSession.setDisplayId(displayId);
+ mTaskSession.setVirtualDisplayId(displayId);
mConfigListener = new ConfigListener();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
@@ -129,13 +126,15 @@ public class ContentRecorderTests extends WindowTestsBase {
mLatch = new CountDownLatch(1);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE,
"true", true);
+
+ // Skip unnecessary operations of relayout.
+ spyOn(mWm.mWindowPlacerLocked);
+ doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
}
@After
public void teardown() {
DeviceConfig.removeOnPropertiesChangedListener(mConfigListener);
- mVirtualDisplay.release();
- mWm.mRoot.onDisplayRemoved(mVirtualDisplay.getDisplay().getDisplayId());
}
@Test
@@ -154,19 +153,18 @@ public class ContentRecorderTests extends WindowTestsBase {
}
@Test
- public void testUpdateRecording_display_nullToken() {
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- session.setDisplayId(mDisplaySession.getDisplayId());
- session.setTokenToRecord(null);
+ public void testUpdateRecording_display_invalidDisplayIdToMirror() {
+ ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
+ INVALID_DISPLAY);
mContentRecorder.setContentRecordingSession(session);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
}
@Test
- public void testUpdateRecording_display_noWindowContainer() {
+ public void testUpdateRecording_display_noDisplayContentToMirror() {
doReturn(null).when(
- mWm.mWindowContextListenerController).getContainer(any());
+ mWm.mRoot).getDisplayContent(anyInt());
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
@@ -192,9 +190,8 @@ public class ContentRecorderTests extends WindowTestsBase {
@Test
public void testUpdateRecording_task_nullToken() {
- ContentRecordingSession session = ContentRecordingSession.createTaskSession(
- sTaskWindowContainerToken);
- session.setDisplayId(mDisplaySession.getDisplayId());
+ ContentRecordingSession session = mTaskSession;
+ session.setVirtualDisplayId(mDisplaySession.getVirtualDisplayId());
session.setTokenToRecord(null);
mContentRecorder.setContentRecordingSession(session);
mContentRecorder.updateRecording();
@@ -268,8 +265,8 @@ public class ContentRecorderTests extends WindowTestsBase {
final ActivityInfo info = new ActivityInfo();
info.windowLayout = new ActivityInfo.WindowLayout(-1 /* width */,
- -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
- Gravity.NO_GRAVITY, recordedWidth, recordedHeight);
+ -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */,
+ Gravity.NO_GRAVITY, recordedWidth, recordedHeight);
mTask.setMinDimensions(info);
// WHEN a recording is ongoing.
@@ -278,7 +275,11 @@ public class ContentRecorderTests extends WindowTestsBase {
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
// WHEN a configuration change arrives, and the recorded content is a different size.
- mTask.setBounds(new Rect(0, 0, recordedWidth, recordedHeight));
+ Configuration configuration = mTask.getConfiguration();
+ configuration.windowConfiguration.setBounds(new Rect(0, 0, recordedWidth, recordedHeight));
+ configuration.windowConfiguration.setAppBounds(
+ new Rect(0, 0, recordedWidth, recordedHeight));
+ mTask.onConfigurationChanged(configuration);
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
// THEN content in the captured DisplayArea is scaled to fit the surface size.
@@ -434,20 +435,6 @@ public class ContentRecorderTests extends WindowTestsBase {
displayAreaBounds.width(), displayAreaBounds.height());
}
- private static class RecordingTestToken extends Binder {
- }
-
- /**
- * Creates a WindowToken associated with the default task DisplayArea, in order for that
- * DisplayArea to be mirrored.
- */
- private void setUpDefaultTaskDisplayAreaWindowToken() {
- // GIVEN the default task display area is represented by the WindowToken.
- spyOn(mWm.mWindowContextListenerController);
- doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
- mWm.mWindowContextListenerController).getContainer(any());
- }
-
/**
* Creates a {@link android.window.WindowContainerToken} associated with a task, in order for
* that task to be recorded.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
index 342d68b16823..6cda0381d245 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
@@ -24,9 +24,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
@@ -47,15 +47,21 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ContentRecordingControllerTests extends WindowTestsBase {
- private static final IBinder TEST_TOKEN = new RecordingTestToken();
private final ContentRecordingSession mDefaultSession =
- ContentRecordingSession.createDisplaySession(
- TEST_TOKEN);
+ ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+
+ private int mVirtualDisplayId;
+ private DisplayContent mVirtualDisplayContent;
@Before
public void setup() {
- spyOn(mDisplayContent);
- mDefaultSession.setDisplayId(DEFAULT_DISPLAY);
+ // GIVEN the VirtualDisplay associated with the session (so the display has state ON).
+ mVirtualDisplayContent = new TestDisplayContent.Builder(mAtm, 500, 600).build();
+ mVirtualDisplayId = mVirtualDisplayContent.getDisplayId();
+ mWm.mRoot.onDisplayAdded(mVirtualDisplayId);
+ spyOn(mVirtualDisplayContent);
+
+ mDefaultSession.setVirtualDisplayId(mVirtualDisplayId);
}
@Test
@@ -76,20 +82,8 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
public void testSetContentRecordingSessionLocked_invalidDisplayId_notAccepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN an invalid display session (no display id is set).
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- // WHEN updating the session.
- controller.setContentRecordingSessionLocked(session, mWm);
- ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
- // THEN the invalid session was not accepted.
- assertThat(resultingSession).isNull();
- }
-
- @Test
- public void testSetContentRecordingSessionLocked_invalidToken_notAccepted() {
- ContentRecordingController controller = new ContentRecordingController();
- // GIVEN a session with a null token.
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(null);
- session.setDisplayId(DEFAULT_DISPLAY);
+ ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
// WHEN updating the session.
controller.setContentRecordingSessionLocked(session, mWm);
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
@@ -101,52 +95,46 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
public void testSetContentRecordingSessionLocked_newDisplaySession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session.
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- session.setDisplayId(DEFAULT_DISPLAY);
// WHEN updating the session.
- controller.setContentRecordingSessionLocked(session, mWm);
+ controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
// THEN the valid session was accepted.
- assertThat(resultingSession).isEqualTo(session);
- verify(mDisplayContent, atLeastOnce()).setContentRecordingSession(session);
+ assertThat(resultingSession).isEqualTo(mDefaultSession);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
}
@Test
public void testSetContentRecordingSessionLocked_updateCurrentDisplaySession_notAccepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- session.setDisplayId(DEFAULT_DISPLAY);
- controller.setContentRecordingSessionLocked(session, mWm);
- verify(mDisplayContent, atLeastOnce()).setContentRecordingSession(session);
+ controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
- // WHEN updating the session.
- ContentRecordingSession sessionUpdate = ContentRecordingSession.createDisplaySession(
- new RecordingTestToken());
- sessionUpdate.setDisplayId(DEFAULT_DISPLAY);
+ // WHEN updating the session on the same display.
+ ContentRecordingSession sessionUpdate =
+ ContentRecordingSession.createTaskSession(mock(IBinder.class));
+ sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
// THEN the session was not accepted.
- assertThat(resultingSession).isEqualTo(session);
- verify(mDisplayContent, never()).setContentRecordingSession(sessionUpdate);
+ assertThat(resultingSession).isEqualTo(mDefaultSession);
+ verify(mVirtualDisplayContent, never()).setContentRecordingSession(sessionUpdate);
}
@Test
public void testSetContentRecordingSessionLocked_disableCurrentDisplaySession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- session.setDisplayId(DEFAULT_DISPLAY);
- controller.setContentRecordingSessionLocked(session, mWm);
- verify(mDisplayContent, atLeastOnce()).setContentRecordingSession(session);
+ controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
// WHEN updating the session.
ContentRecordingSession sessionUpdate = null;
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
- ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
// THEN the valid session was accepted.
+ ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
assertThat(resultingSession).isEqualTo(sessionUpdate);
// Do not need to update the display content, since it will handle stopping the session
// via state change callbacks.
@@ -156,28 +144,23 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
public void testSetContentRecordingSessionLocked_takeOverCurrentDisplaySession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
- ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN);
- session.setDisplayId(DEFAULT_DISPLAY);
- controller.setContentRecordingSessionLocked(session, mWm);
- verify(mDisplayContent, atLeastOnce()).setContentRecordingSession(session);
+ controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
// WHEN updating the session.
- final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
- mDisplayInfo).build();
+ final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm, 500,
+ 600).build();
ContentRecordingSession sessionUpdate = ContentRecordingSession.createDisplaySession(
- TEST_TOKEN);
- sessionUpdate.setDisplayId(virtualDisplay.getDisplayId());
+ DEFAULT_DISPLAY);
+ assertThat(virtualDisplay.getDisplayId()).isNotEqualTo(mVirtualDisplayId);
+ sessionUpdate.setVirtualDisplayId(virtualDisplay.getDisplayId());
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
- ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
// THEN the valid session was accepted.
+ ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
assertThat(resultingSession).isEqualTo(sessionUpdate);
- verify(virtualDisplay).setContentRecordingSession(sessionUpdate);
+ verify(virtualDisplay, atLeastOnce()).setContentRecordingSession(sessionUpdate);
// THEN the recording was paused on the prior display.
- verify(mDisplayContent).pauseRecording();
-
- }
-
- private static class RecordingTestToken extends Binder {
+ verify(mVirtualDisplayContent).pauseRecording();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d071f1314f35..ba9f809e9a2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -116,7 +116,6 @@ import android.hardware.HardwareBuffer;
import android.hardware.display.VirtualDisplay;
import android.metrics.LogMaker;
import android.os.Binder;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -2630,7 +2629,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testVirtualDisplayContent_withoutSurface() {
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
- final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+ setUpDefaultTaskDisplayAreaWindowToken();
// GIVEN SurfaceControl does not mirror a null surface.
Point surfaceSize = new Point(
@@ -2645,8 +2644,8 @@ public class DisplayContentTests extends WindowTestsBase {
// WHEN getting the DisplayContent for the new virtual display.
DisplayContent actualDC = mWm.mRoot.getDisplayContent(displayId);
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
- tokenToMirror);
- session.setDisplayId(displayId);
+ DEFAULT_DISPLAY);
+ session.setVirtualDisplayId(displayId);
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
actualDC.updateRecording();
@@ -2660,7 +2659,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testVirtualDisplayContent_withSurface() {
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
- final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+ setUpDefaultTaskDisplayAreaWindowToken();
// GIVEN SurfaceControl can successfully mirror the provided surface.
Point surfaceSize = new Point(
@@ -2674,8 +2673,8 @@ public class DisplayContentTests extends WindowTestsBase {
// GIVEN a session for this display.
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
- tokenToMirror);
- session.setDisplayId(displayId);
+ DEFAULT_DISPLAY);
+ session.setVirtualDisplayId(displayId);
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
mWm.mRoot.onDisplayAdded(displayId);
@@ -2693,7 +2692,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testVirtualDisplayContent_displayMirroring() {
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
// mirror.
- final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+ setUpDefaultTaskDisplayAreaWindowToken();
// GIVEN SurfaceControl can successfully mirror the provided surface.
Point surfaceSize = new Point(
@@ -2726,22 +2725,15 @@ public class DisplayContentTests extends WindowTestsBase {
display.release();
}
- private static class MirroringTestToken extends Binder {
- }
-
/**
* Creates a WindowToken associated with the default task DisplayArea, in order for that
* DisplayArea to be mirrored.
*/
- private IBinder setUpDefaultTaskDisplayAreaWindowToken() {
- // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
- // mirror.
- final IBinder tokenToMirror = new MirroringTestToken();
+ private void setUpDefaultTaskDisplayAreaWindowToken() {
// GIVEN the default task display area is represented by the WindowToken.
spyOn(mWm.mWindowContextListenerController);
doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
mWm.mWindowContextListenerController).getContainer(any());
- return tokenToMirror;
}
/**