diff options
| author | 2023-03-27 11:57:14 +0000 | |
|---|---|---|
| committer | 2023-03-27 11:57:14 +0000 | |
| commit | f9e6314f1d39a0d0614b69a1b652396de9d90384 (patch) | |
| tree | ad2f2088029c700a08377c879e99509968e4a02a | |
| parent | 6f965bea4a360accd96f4fbf6d196663b9d54ab8 (diff) | |
| parent | bb1da32fc8177b086145760ce10d70dc188b319d (diff) | |
Merge "[MediaProjection] Do not rely on WindowContext to indicate display" into udc-dev
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; } /** |