Merge "MediaProjection sets content recording details in WM directly." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3302797586
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.util.IntArray;
|
||||
import android.util.Slog;
|
||||
@@ -351,24 +350,17 @@ public abstract class DisplayManagerInternal {
|
||||
public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
|
||||
|
||||
/**
|
||||
* Returns the window token of the level of the WindowManager hierarchy to mirror. Returns null
|
||||
* if layer mirroring by SurfaceFlinger should not be performed for the given displayId.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
public abstract IBinder getWindowTokenClientToMirror(int displayId);
|
||||
|
||||
/**
|
||||
* For the given displayId, updates the window token of the level of the WindowManager hierarchy
|
||||
* to mirror. If windowToken is null, then SurfaceFlinger performs no layer mirroring to the
|
||||
* For the given displayId, updates if WindowManager is responsible for mirroring on that
|
||||
* display. If {@code false}, then SurfaceFlinger performs no layer mirroring to the
|
||||
* given display.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
* Only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
public abstract void setWindowTokenClientToMirror(int displayId, IBinder windowToken);
|
||||
public abstract void setWindowManagerMirroring(int displayId, boolean isMirroring);
|
||||
|
||||
/**
|
||||
* Returns the default size of the surface associated with the display, or null if the surface
|
||||
* is not provided for layer mirroring by SurfaceFlinger.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
* Only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
public abstract Point getDisplaySurfaceDefaultSize(int displayId);
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.annotation.Nullable;
|
||||
import android.hardware.display.DisplayManager.VirtualDisplayFlag;
|
||||
import android.media.projection.MediaProjection;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.Surface;
|
||||
@@ -95,11 +94,10 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
private int mDisplayIdToMirror = DEFAULT_DISPLAY;
|
||||
|
||||
/**
|
||||
* The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
|
||||
* should not be performed.
|
||||
* Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
|
||||
* if DisplayManager should record contents instead.
|
||||
*/
|
||||
@Nullable
|
||||
private IBinder mWindowTokenClientToMirror = null;
|
||||
private boolean mWindowManagerMirroring = false;
|
||||
|
||||
|
||||
|
||||
@@ -126,7 +124,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
@Nullable Surface surface,
|
||||
@Nullable String uniqueId,
|
||||
int displayIdToMirror,
|
||||
@Nullable IBinder windowTokenClientToMirror) {
|
||||
boolean windowManagerMirroring) {
|
||||
this.mName = name;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mName);
|
||||
@@ -148,7 +146,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
this.mSurface = surface;
|
||||
this.mUniqueId = uniqueId;
|
||||
this.mDisplayIdToMirror = displayIdToMirror;
|
||||
this.mWindowTokenClientToMirror = windowTokenClientToMirror;
|
||||
this.mWindowManagerMirroring = windowManagerMirroring;
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
@@ -227,12 +225,12 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
|
||||
* should not be performed.
|
||||
* Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
|
||||
* if DisplayManager should record contents instead.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable IBinder getWindowTokenClientToMirror() {
|
||||
return mWindowTokenClientToMirror;
|
||||
public boolean isWindowManagerMirroring() {
|
||||
return mWindowManagerMirroring;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,9 +240,9 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
int flg = 0;
|
||||
if (mWindowManagerMirroring) flg |= 0x100;
|
||||
if (mSurface != null) flg |= 0x20;
|
||||
if (mUniqueId != null) flg |= 0x40;
|
||||
if (mWindowTokenClientToMirror != null) flg |= 0x100;
|
||||
dest.writeInt(flg);
|
||||
dest.writeString(mName);
|
||||
dest.writeInt(mWidth);
|
||||
@@ -254,7 +252,6 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
if (mSurface != null) dest.writeTypedObject(mSurface, flags);
|
||||
if (mUniqueId != null) dest.writeString(mUniqueId);
|
||||
dest.writeInt(mDisplayIdToMirror);
|
||||
if (mWindowTokenClientToMirror != null) dest.writeStrongBinder(mWindowTokenClientToMirror);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -269,6 +266,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
int flg = in.readInt();
|
||||
boolean windowManagerMirroring = (flg & 0x100) != 0;
|
||||
String name = in.readString();
|
||||
int width = in.readInt();
|
||||
int height = in.readInt();
|
||||
@@ -277,7 +275,6 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
|
||||
String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
|
||||
int displayIdToMirror = in.readInt();
|
||||
IBinder windowTokenClientToMirror = (flg & 0x100) == 0 ? null : (IBinder) in.readStrongBinder();
|
||||
|
||||
this.mName = name;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
@@ -300,7 +297,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
this.mSurface = surface;
|
||||
this.mUniqueId = uniqueId;
|
||||
this.mDisplayIdToMirror = displayIdToMirror;
|
||||
this.mWindowTokenClientToMirror = windowTokenClientToMirror;
|
||||
this.mWindowManagerMirroring = windowManagerMirroring;
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
@@ -334,7 +331,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
private @Nullable Surface mSurface;
|
||||
private @Nullable String mUniqueId;
|
||||
private int mDisplayIdToMirror;
|
||||
private @Nullable IBinder mWindowTokenClientToMirror;
|
||||
private boolean mWindowManagerMirroring;
|
||||
|
||||
private long mBuilderFieldsSet = 0L;
|
||||
|
||||
@@ -470,14 +467,14 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
|
||||
* should not be performed.
|
||||
* Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or
|
||||
* if DisplayManager should record contents instead.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull Builder setWindowTokenClientToMirror(@NonNull IBinder value) {
|
||||
public @NonNull Builder setWindowManagerMirroring(boolean value) {
|
||||
checkNotUsed();
|
||||
mBuilderFieldsSet |= 0x100;
|
||||
mWindowTokenClientToMirror = value;
|
||||
mWindowManagerMirroring = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -499,7 +496,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
mDisplayIdToMirror = DEFAULT_DISPLAY;
|
||||
}
|
||||
if ((mBuilderFieldsSet & 0x100) == 0) {
|
||||
mWindowTokenClientToMirror = null;
|
||||
mWindowManagerMirroring = false;
|
||||
}
|
||||
VirtualDisplayConfig o = new VirtualDisplayConfig(
|
||||
mName,
|
||||
@@ -510,7 +507,7 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
mSurface,
|
||||
mUniqueId,
|
||||
mDisplayIdToMirror,
|
||||
mWindowTokenClientToMirror);
|
||||
mWindowManagerMirroring);
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -523,10 +520,10 @@ public final class VirtualDisplayConfig implements Parcelable {
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1643938791506L,
|
||||
time = 1646227247934L,
|
||||
codegenVersion = "1.0.23",
|
||||
sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
|
||||
inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
|
||||
inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate boolean mWindowManagerMirroring\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
19
core/java/android/view/ContentRecordingSession.aidl
Normal file
19
core/java/android/view/ContentRecordingSession.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2022, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
parcelable ContentRecordingSession;
|
||||
447
core/java/android/view/ContentRecordingSession.java
Normal file
447
core/java/android/view/ContentRecordingSession.java
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Description of a content recording session.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@DataClass(
|
||||
genConstructor = false,
|
||||
genToString = true,
|
||||
genSetters = true,
|
||||
genEqualsHashCode = true
|
||||
)
|
||||
public final class ContentRecordingSession implements Parcelable {
|
||||
|
||||
/**
|
||||
* An entire DisplayContent is being recorded. Recording may also be paused.
|
||||
*/
|
||||
public static final int RECORD_CONTENT_DISPLAY = 0;
|
||||
/**
|
||||
* A single Task is being recorded. Recording may also be paused.
|
||||
*/
|
||||
public static final int RECORD_CONTENT_TASK = 1;
|
||||
|
||||
/**
|
||||
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
|
||||
* recorded content rendered to its surface.
|
||||
*/
|
||||
private int mDisplayId = INVALID_DISPLAY;
|
||||
|
||||
/**
|
||||
* The content to record.
|
||||
*/
|
||||
@RecordContent
|
||||
private int mContentToRecord = RECORD_CONTENT_DISPLAY;
|
||||
|
||||
/**
|
||||
* The window token of the layer of the hierarchy to record.
|
||||
* The display content if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_TASK}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
private IBinder mTokenToRecord = null;
|
||||
|
||||
/**
|
||||
* Default instance, with recording the display.
|
||||
*/
|
||||
private ContentRecordingSession() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance initialized for display recording.
|
||||
*/
|
||||
public static ContentRecordingSession createDisplaySession(
|
||||
@NonNull IBinder displayContentWindowToken) {
|
||||
return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_DISPLAY)
|
||||
.setTokenToRecord(displayContentWindowToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance initialized for task recording.
|
||||
*/
|
||||
public static ContentRecordingSession createTaskSession(
|
||||
@NonNull IBinder taskWindowContainerToken) {
|
||||
return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_TASK)
|
||||
.setTokenToRecord(taskWindowContainerToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this is a valid session.
|
||||
*/
|
||||
public static boolean isValid(ContentRecordingSession session) {
|
||||
return session != null && (session.getDisplayId() > INVALID_DISPLAY
|
||||
&& session.getTokenToRecord() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} when both sessions are for the same display.
|
||||
*/
|
||||
public static boolean isSameDisplay(ContentRecordingSession session,
|
||||
ContentRecordingSession incomingSession) {
|
||||
return session != null && incomingSession != null
|
||||
&& session.getDisplayId() == incomingSession.getDisplayId();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.23.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/ContentRecordingSession.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
@IntDef(prefix = "RECORD_CONTENT_", value = {
|
||||
RECORD_CONTENT_DISPLAY,
|
||||
RECORD_CONTENT_TASK
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@DataClass.Generated.Member
|
||||
public @interface RecordContent {}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static String recordContentToString(@RecordContent int value) {
|
||||
switch (value) {
|
||||
case RECORD_CONTENT_DISPLAY:
|
||||
return "RECORD_CONTENT_DISPLAY";
|
||||
case RECORD_CONTENT_TASK:
|
||||
return "RECORD_CONTENT_TASK";
|
||||
default: return Integer.toHexString(value);
|
||||
}
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
/* package-private */ ContentRecordingSession(
|
||||
int displayId,
|
||||
@RecordContent int contentToRecord,
|
||||
@VisibleForTesting @Nullable IBinder tokenToRecord) {
|
||||
this.mDisplayId = displayId;
|
||||
this.mContentToRecord = contentToRecord;
|
||||
|
||||
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
|
||||
&& !(mContentToRecord == RECORD_CONTENT_TASK)) {
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"contentToRecord was " + mContentToRecord + " but must be one of: "
|
||||
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
|
||||
+ "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
|
||||
}
|
||||
|
||||
this.mTokenToRecord = tokenToRecord;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
VisibleForTesting.class, null, mTokenToRecord);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
|
||||
* recorded content rendered to its surface.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public int getDisplayId() {
|
||||
return mDisplayId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content to record.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @RecordContent int getContentToRecord() {
|
||||
return mContentToRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* The window token of the layer of the hierarchy to record.
|
||||
* The display content if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_TASK}.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @VisibleForTesting @Nullable IBinder getTokenToRecord() {
|
||||
return mTokenToRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
|
||||
* recorded content rendered to its surface.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull ContentRecordingSession setDisplayId( int value) {
|
||||
mDisplayId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content to record.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull ContentRecordingSession setContentToRecord(@RecordContent int value) {
|
||||
mContentToRecord = value;
|
||||
|
||||
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
|
||||
&& !(mContentToRecord == RECORD_CONTENT_TASK)) {
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"contentToRecord was " + mContentToRecord + " but must be one of: "
|
||||
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
|
||||
+ "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The window token of the layer of the hierarchy to record.
|
||||
* The display content if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_TASK}.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull ContentRecordingSession setTokenToRecord(@VisibleForTesting @NonNull IBinder value) {
|
||||
mTokenToRecord = value;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
VisibleForTesting.class, null, mTokenToRecord);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public String toString() {
|
||||
// You can override field toString logic by defining methods like:
|
||||
// String fieldNameToString() { ... }
|
||||
|
||||
return "ContentRecordingSession { " +
|
||||
"displayId = " + mDisplayId + ", " +
|
||||
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
|
||||
"tokenToRecord = " + mTokenToRecord +
|
||||
" }";
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public boolean equals(@Nullable Object o) {
|
||||
// You can override field equality logic by defining either of the methods like:
|
||||
// boolean fieldNameEquals(ContentRecordingSession other) { ... }
|
||||
// boolean fieldNameEquals(FieldType otherValue) { ... }
|
||||
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@SuppressWarnings("unchecked")
|
||||
ContentRecordingSession that = (ContentRecordingSession) o;
|
||||
//noinspection PointlessBooleanExpression
|
||||
return true
|
||||
&& mDisplayId == that.mDisplayId
|
||||
&& mContentToRecord == that.mContentToRecord
|
||||
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int hashCode() {
|
||||
// You can override field hashCode logic by defining methods like:
|
||||
// int fieldNameHashCode() { ... }
|
||||
|
||||
int _hash = 1;
|
||||
_hash = 31 * _hash + mDisplayId;
|
||||
_hash = 31 * _hash + mContentToRecord;
|
||||
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
|
||||
return _hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
// You can override field parcelling by defining methods like:
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
byte flg = 0;
|
||||
if (mTokenToRecord != null) flg |= 0x4;
|
||||
dest.writeByte(flg);
|
||||
dest.writeInt(mDisplayId);
|
||||
dest.writeInt(mContentToRecord);
|
||||
if (mTokenToRecord != null) dest.writeStrongBinder(mTokenToRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int describeContents() { return 0; }
|
||||
|
||||
/** @hide */
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
@DataClass.Generated.Member
|
||||
/* package-private */ ContentRecordingSession(@NonNull Parcel in) {
|
||||
// You can override field unparcelling by defining methods like:
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
byte flg = in.readByte();
|
||||
int displayId = in.readInt();
|
||||
int contentToRecord = in.readInt();
|
||||
IBinder tokenToRecord = (flg & 0x4) == 0 ? null : (IBinder) in.readStrongBinder();
|
||||
|
||||
this.mDisplayId = displayId;
|
||||
this.mContentToRecord = contentToRecord;
|
||||
|
||||
if (!(mContentToRecord == RECORD_CONTENT_DISPLAY)
|
||||
&& !(mContentToRecord == RECORD_CONTENT_TASK)) {
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"contentToRecord was " + mContentToRecord + " but must be one of: "
|
||||
+ "RECORD_CONTENT_DISPLAY(" + RECORD_CONTENT_DISPLAY + "), "
|
||||
+ "RECORD_CONTENT_TASK(" + RECORD_CONTENT_TASK + ")");
|
||||
}
|
||||
|
||||
this.mTokenToRecord = tokenToRecord;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
VisibleForTesting.class, null, mTokenToRecord);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static final @NonNull Parcelable.Creator<ContentRecordingSession> CREATOR
|
||||
= new Parcelable.Creator<ContentRecordingSession>() {
|
||||
@Override
|
||||
public ContentRecordingSession[] newArray(int size) {
|
||||
return new ContentRecordingSession[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentRecordingSession createFromParcel(@NonNull Parcel in) {
|
||||
return new ContentRecordingSession(in);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A builder for {@link ContentRecordingSession}
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@DataClass.Generated.Member
|
||||
public static final class Builder {
|
||||
|
||||
private int mDisplayId;
|
||||
private @RecordContent int mContentToRecord;
|
||||
private @VisibleForTesting @Nullable IBinder mTokenToRecord;
|
||||
|
||||
private long mBuilderFieldsSet = 0L;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
|
||||
* recorded content rendered to its surface.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull Builder setDisplayId(int value) {
|
||||
checkNotUsed();
|
||||
mBuilderFieldsSet |= 0x1;
|
||||
mDisplayId = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content to record.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull Builder setContentToRecord(@RecordContent int value) {
|
||||
checkNotUsed();
|
||||
mBuilderFieldsSet |= 0x2;
|
||||
mContentToRecord = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The window token of the layer of the hierarchy to record.
|
||||
* The display content if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is
|
||||
* {@link RecordContent#RECORD_CONTENT_TASK}.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull Builder setTokenToRecord(@VisibleForTesting @NonNull IBinder value) {
|
||||
checkNotUsed();
|
||||
mBuilderFieldsSet |= 0x4;
|
||||
mTokenToRecord = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the instance. This builder should not be touched after calling this! */
|
||||
public @NonNull ContentRecordingSession build() {
|
||||
checkNotUsed();
|
||||
mBuilderFieldsSet |= 0x8; // Mark builder used
|
||||
|
||||
if ((mBuilderFieldsSet & 0x1) == 0) {
|
||||
mDisplayId = INVALID_DISPLAY;
|
||||
}
|
||||
if ((mBuilderFieldsSet & 0x2) == 0) {
|
||||
mContentToRecord = RECORD_CONTENT_DISPLAY;
|
||||
}
|
||||
if ((mBuilderFieldsSet & 0x4) == 0) {
|
||||
mTokenToRecord = null;
|
||||
}
|
||||
ContentRecordingSession o = new ContentRecordingSession(
|
||||
mDisplayId,
|
||||
mContentToRecord,
|
||||
mTokenToRecord);
|
||||
return o;
|
||||
}
|
||||
|
||||
private void checkNotUsed() {
|
||||
if ((mBuilderFieldsSet & 0x8) != 0) {
|
||||
throw new IllegalStateException(
|
||||
"This Builder should not be reused. Use a new Builder instance instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1644843382972L,
|
||||
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 @com.android.internal.annotations.VisibleForTesting @android.annotation.Nullable android.os.IBinder mTokenToRecord\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 isSameDisplay(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() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import android.graphics.Region;
|
||||
import android.os.Bundle;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.IAppTransitionAnimationSpecsFuture;
|
||||
@@ -878,6 +879,17 @@ interface IWindowManager
|
||||
*/
|
||||
void detachWindowContextFromWindowContainer(IBinder clientToken);
|
||||
|
||||
/**
|
||||
* Updates the content recording session. If a different session is already in progress, then
|
||||
* the pre-existing session is stopped, and the new incoming session takes over.
|
||||
*
|
||||
* The DisplayContent for the new session will begin recording when
|
||||
* {@link RootWindowContainer#onDisplayChanged} is invoked for the new {@link VirtualDisplay}.
|
||||
*
|
||||
* @param incomingSession the nullable incoming content recording session
|
||||
*/
|
||||
void setContentRecordingSession(in ContentRecordingSession incomingSession);
|
||||
|
||||
/**
|
||||
* Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
|
||||
*
|
||||
|
||||
@@ -75,6 +75,8 @@ per-file ContentInfo.java = file:/core/java/android/service/autofill/OWNERS
|
||||
per-file ContentInfo.java = file:/core/java/android/widget/OWNERS
|
||||
|
||||
# WindowManager
|
||||
per-file ContentRecordingSession.aidl = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file ContentRecordingSession.java = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file DisplayCutout.aidl = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file DisplayCutout.java = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file IDisplay*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
|
||||
@@ -82,7 +82,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
|
||||
Consts.TAG_WM),
|
||||
WM_DEBUG_WINDOW_INSETS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM),
|
||||
WM_DEBUG_LAYER_MIRRORING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
WM_DEBUG_CONTENT_RECORDING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
Consts.TAG_WM),
|
||||
WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
|
||||
WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
|
||||
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContentRecordingSession} class.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksCoreTests:ContentRecordingSessionTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class ContentRecordingSessionTest {
|
||||
private static final int DISPLAY_ID = 1;
|
||||
private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken");
|
||||
|
||||
@Test
|
||||
public void testParcelable() {
|
||||
ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
|
||||
session.setDisplayId(DISPLAY_ID);
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
session.writeToParcel(parcel, 0 /* flags */);
|
||||
parcel.setDataPosition(0);
|
||||
ContentRecordingSession session2 = ContentRecordingSession.CREATOR.createFromParcel(parcel);
|
||||
assertThat(session).isEqualTo(session2);
|
||||
parcel.recycle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskConstructor() {
|
||||
ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
|
||||
assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK);
|
||||
assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplayConstructor() {
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
WINDOW_TOKEN);
|
||||
assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
|
||||
assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValid() {
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
WINDOW_TOKEN);
|
||||
assertThat(ContentRecordingSession.isValid(session)).isFalse();
|
||||
|
||||
session.setDisplayId(DEFAULT_DISPLAY);
|
||||
assertThat(ContentRecordingSession.isValid(session)).isTrue();
|
||||
|
||||
session.setDisplayId(INVALID_DISPLAY);
|
||||
assertThat(ContentRecordingSession.isValid(session)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSameDisplay() {
|
||||
assertThat(ContentRecordingSession.isSameDisplay(null, null)).isFalse();
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
WINDOW_TOKEN);
|
||||
session.setDisplayId(DEFAULT_DISPLAY);
|
||||
assertThat(ContentRecordingSession.isSameDisplay(session, null)).isFalse();
|
||||
|
||||
ContentRecordingSession incomingSession = ContentRecordingSession.createDisplaySession(
|
||||
WINDOW_TOKEN);
|
||||
incomingSession.setDisplayId(DEFAULT_DISPLAY);
|
||||
assertThat(ContentRecordingSession.isSameDisplay(session, incomingSession)).isTrue();
|
||||
|
||||
incomingSession.setDisplayId(DEFAULT_DISPLAY + 1);
|
||||
assertThat(ContentRecordingSession.isSameDisplay(session, incomingSession)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
|
||||
session.setDisplayId(DISPLAY_ID);
|
||||
|
||||
ContentRecordingSession session2 = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
|
||||
session2.setDisplayId(DISPLAY_ID);
|
||||
assertThat(session).isEqualTo(session2);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file *Window* = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
per-file *ContentRecord* = file:/services/core/java/com/android/server/wm/OWNERS
|
||||
|
||||
# Scroll Capture
|
||||
per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
|
||||
|
||||
@@ -193,12 +193,6 @@
|
||||
"group": "WM_DEBUG_SYNC_ENGINE",
|
||||
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
|
||||
},
|
||||
"-1898316768": {
|
||||
"message": "Unable to retrieve window container to start layer mirroring for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-1895337367": {
|
||||
"message": "Delete root task display=%d winMode=%d",
|
||||
"level": "VERBOSE",
|
||||
@@ -349,6 +343,12 @@
|
||||
"group": "WM_DEBUG_STATES",
|
||||
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
|
||||
},
|
||||
"-1734445525": {
|
||||
"message": "Display %d has content (%b) so pause recording",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-1730156332": {
|
||||
"message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
|
||||
"level": "VERBOSE",
|
||||
@@ -511,6 +511,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-1542296596": {
|
||||
"message": "Going ahead with updating recording for display %d to new bounds %s and\/or orientation %d.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-1539974875": {
|
||||
"message": "removeAppToken: %s delayed=%b Callers=%s",
|
||||
"level": "VERBOSE",
|
||||
@@ -733,6 +739,12 @@
|
||||
"group": "WM_DEBUG_STARTING_WINDOW",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"-1323003327": {
|
||||
"message": "Unexpectedly null window container; unable to update recording for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-1311436264": {
|
||||
"message": "Unregister task fragment organizer=%s uid=%d pid=%d",
|
||||
"level": "VERBOSE",
|
||||
@@ -847,6 +859,12 @@
|
||||
"group": "WM_DEBUG_WINDOW_INSETS",
|
||||
"at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
|
||||
},
|
||||
"-1179559337": {
|
||||
"message": "Unable to start recording due to invalid region for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-1176488860": {
|
||||
"message": "SURFACE isSecure=%b: %s",
|
||||
"level": "INFO",
|
||||
@@ -1027,6 +1045,12 @@
|
||||
"group": "WM_DEBUG_DRAW",
|
||||
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
|
||||
},
|
||||
"-992111757": {
|
||||
"message": "Unable to retrieve window container to start recording for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-986746907": {
|
||||
"message": "Starting window removed %s",
|
||||
"level": "DEBUG",
|
||||
@@ -1087,6 +1111,12 @@
|
||||
"group": "WM_DEBUG_STATES",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"-922507769": {
|
||||
"message": "Display %d has no content and is on, so start recording for state %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-917215012": {
|
||||
"message": "%s: caller %d is using old GET_TASKS but privileged; allowing",
|
||||
"level": "WARN",
|
||||
@@ -1105,12 +1135,6 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-904499590": {
|
||||
"message": "Provided surface for layer mirroring on display %d is not present, so do not update the surface",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-883738232": {
|
||||
"message": "Adding more than one toast window for UID at a time.",
|
||||
"level": "WARN",
|
||||
@@ -1345,12 +1369,6 @@
|
||||
"group": "WM_DEBUG_ORIENTATION",
|
||||
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
|
||||
},
|
||||
"-663411559": {
|
||||
"message": "Going ahead with updating layer mirroring for display %d to new bounds %s and\/or orientation %d.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-655104359": {
|
||||
"message": "Frontmost changed immersion: %s",
|
||||
"level": "DEBUG",
|
||||
@@ -1531,6 +1549,18 @@
|
||||
"group": "WM_DEBUG_KEEP_SCREEN_ON",
|
||||
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
|
||||
},
|
||||
"-479990726": {
|
||||
"message": "Unable to start recording due to null token for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-473911359": {
|
||||
"message": "Display %d was already recording, so apply transformations if necessary",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-463348344": {
|
||||
"message": "Removing and adding activity %s to root task at top callers=%s",
|
||||
"level": "INFO",
|
||||
@@ -1609,10 +1639,10 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-384564722": {
|
||||
"message": "Unable to start layer mirroring for display %d since the surface is not available.",
|
||||
"-381522987": {
|
||||
"message": "Display %d state is now (%d), so update recording?",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-381475323": {
|
||||
@@ -1639,6 +1669,12 @@
|
||||
"group": "WM_DEBUG_TASKS",
|
||||
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
|
||||
},
|
||||
"-370641936": {
|
||||
"message": "Unable to update recording for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-360208282": {
|
||||
"message": "Animating wallpapers: old: %s hidden=%b new: %s hidden=%b",
|
||||
"level": "VERBOSE",
|
||||
@@ -1699,12 +1735,6 @@
|
||||
"group": "WM_DEBUG_STATES",
|
||||
"at": "com\/android\/server\/wm\/TaskFragment.java"
|
||||
},
|
||||
"-309399422": {
|
||||
"message": "Display %d state is now (%d), so update layer mirroring?",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-304728471": {
|
||||
"message": "New wallpaper: target=%s prev=%s",
|
||||
"level": "DEBUG",
|
||||
@@ -1753,6 +1783,12 @@
|
||||
"group": "WM_DEBUG_APP_TRANSITIONS",
|
||||
"at": "com\/android\/server\/wm\/AppTransitionController.java"
|
||||
},
|
||||
"-237664290": {
|
||||
"message": "Pause the recording session on display %s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/ContentRecordingController.java"
|
||||
},
|
||||
"-235225312": {
|
||||
"message": "Skipping config check for initializing activity: %s",
|
||||
"level": "VERBOSE",
|
||||
@@ -1789,12 +1825,6 @@
|
||||
"group": "WM_DEBUG_WINDOW_MOVEMENT",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-190034097": {
|
||||
"message": "Unable to retrieve window container to update layer mirroring for display %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-182877285": {
|
||||
"message": "Wallpaper layer changed: assigning layers + relayout",
|
||||
"level": "VERBOSE",
|
||||
@@ -1903,12 +1933,6 @@
|
||||
"group": "WM_DEBUG_STATES",
|
||||
"at": "com\/android\/server\/wm\/TaskFragment.java"
|
||||
},
|
||||
"-79877120": {
|
||||
"message": "Display %d has content (%b) so disable layer mirroring",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"-70719599": {
|
||||
"message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
|
||||
"level": "VERBOSE",
|
||||
@@ -2473,12 +2497,6 @@
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"504397469": {
|
||||
"message": "Unable to update layer mirroring for display %d to new bounds %s and\/or orientation %d, since the surface is not available.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"508887531": {
|
||||
"message": "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s",
|
||||
"level": "VERBOSE",
|
||||
@@ -3253,6 +3271,12 @@
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
|
||||
},
|
||||
"1401287081": {
|
||||
"message": "Handle incoming session on display %d, with a pre-existing session %s",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/ContentRecordingController.java"
|
||||
},
|
||||
"1401295262": {
|
||||
"message": "Mode default, asking user",
|
||||
"level": "WARN",
|
||||
@@ -3265,12 +3289,6 @@
|
||||
"group": "WM_DEBUG_SCREEN_ON",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"1407569006": {
|
||||
"message": "Display %d was already layer mirroring, so apply transformations if necessary",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"1422781269": {
|
||||
"message": "Resuming rotation after re-position",
|
||||
"level": "DEBUG",
|
||||
@@ -3517,12 +3535,6 @@
|
||||
"group": "WM_DEBUG_CONFIGURATION",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"1687376052": {
|
||||
"message": "Display %d has no content and is on, so start layer mirroring for state %d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_LAYER_MIRRORING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"1689989893": {
|
||||
"message": "SyncGroup %d: Set ready",
|
||||
"level": "VERBOSE",
|
||||
@@ -3535,6 +3547,12 @@
|
||||
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
|
||||
},
|
||||
"1707558369": {
|
||||
"message": "Unable to start recording for display %d since the surface is not available.",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"1720229827": {
|
||||
"message": "Creating animation bounds layer",
|
||||
"level": "INFO",
|
||||
@@ -3667,6 +3685,12 @@
|
||||
"group": "WM_DEBUG_STARTING_WINDOW",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"1854279309": {
|
||||
"message": "Provided surface for recording on display %d is not present, so do not update the surface",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_DEBUG_CONTENT_RECORDING",
|
||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||
},
|
||||
"1856211951": {
|
||||
"message": "moveFocusableActivityToTop: already on top, activity=%s",
|
||||
"level": "DEBUG",
|
||||
@@ -3942,6 +3966,9 @@
|
||||
"WM_DEBUG_CONTAINERS": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_DEBUG_CONTENT_RECORDING": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_DEBUG_DRAW": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
@@ -3960,9 +3987,6 @@
|
||||
"WM_DEBUG_KEEP_SCREEN_ON": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_DEBUG_LAYER_MIRRORING": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
"WM_DEBUG_LOCKTASK": {
|
||||
"tag": "WindowManager"
|
||||
},
|
||||
|
||||
@@ -29,7 +29,9 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -106,16 +108,12 @@ public final class MediaProjection {
|
||||
if (isSecure) {
|
||||
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
|
||||
}
|
||||
Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
|
||||
TYPE_APPLICATION, null /* options */);
|
||||
final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
|
||||
height, dpi, windowContext.getWindowContextToken());
|
||||
builder.setFlags(flags);
|
||||
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
|
||||
height, dpi).setFlags(flags);
|
||||
if (surface != null) {
|
||||
builder.setSurface(surface);
|
||||
}
|
||||
VirtualDisplay virtualDisplay = createVirtualDisplay(builder.build(), callback, handler,
|
||||
windowContext);
|
||||
VirtualDisplay virtualDisplay = createVirtualDisplay(builder, callback, handler);
|
||||
return virtualDisplay;
|
||||
}
|
||||
|
||||
@@ -145,37 +143,15 @@ public final class MediaProjection {
|
||||
public VirtualDisplay createVirtualDisplay(@NonNull String name,
|
||||
int width, int height, int dpi, int flags, @Nullable Surface surface,
|
||||
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
|
||||
Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
|
||||
TYPE_APPLICATION, null /* options */);
|
||||
final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
|
||||
height, dpi, windowContext.getWindowContextToken());
|
||||
builder.setFlags(flags);
|
||||
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
|
||||
height, dpi).setFlags(flags);
|
||||
if (surface != null) {
|
||||
builder.setSurface(surface);
|
||||
}
|
||||
VirtualDisplay virtualDisplay = createVirtualDisplay(builder.build(), callback, handler,
|
||||
windowContext);
|
||||
VirtualDisplay virtualDisplay = createVirtualDisplay(builder, callback, handler);
|
||||
return virtualDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link VirtualDisplayConfig.Builder}, which will mirror the contents of a
|
||||
* DisplayArea. The DisplayArea to mirror is from the DisplayArea the caller is launched on.
|
||||
*
|
||||
* @param name The name of the virtual display, must be non-empty.
|
||||
* @param width The width of the virtual display in pixels. Must be greater than 0.
|
||||
* @param height The height of the virtual display in pixels. Must be greater than 0.
|
||||
* @param dpi The density of the virtual display in dpi. Must be greater than 0.
|
||||
* @return a config representing a VirtualDisplay
|
||||
*/
|
||||
private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
|
||||
int width, int height, int dpi, IBinder windowContextToken) {
|
||||
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
|
||||
height, dpi);
|
||||
builder.setWindowTokenClientToMirror(windowContextToken);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link android.hardware.display.VirtualDisplay} to capture the
|
||||
* contents of the screen.
|
||||
@@ -186,18 +162,52 @@ public final class MediaProjection {
|
||||
* @param handler The {@link android.os.Handler} on which the callback should be invoked, or
|
||||
* null if the callback should be invoked on the calling thread's main
|
||||
* {@link android.os.Looper}.
|
||||
* @param windowContext the WindowContext associated with the caller.
|
||||
*
|
||||
* @see android.hardware.display.VirtualDisplay
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
|
||||
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
|
||||
Context windowContext) {
|
||||
DisplayManager dm = mContext.getSystemService(DisplayManager.class);
|
||||
return dm.createVirtualDisplay(this, virtualDisplayConfig, callback, handler,
|
||||
windowContext);
|
||||
public VirtualDisplay createVirtualDisplay(
|
||||
@NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
|
||||
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
|
||||
try {
|
||||
final Context windowContext = mContext.createWindowContext(
|
||||
mContext.getDisplayNoVerify(),
|
||||
TYPE_APPLICATION, null /* options */);
|
||||
final IBinder windowContextToken = windowContext.getWindowContextToken();
|
||||
virtualDisplayConfig.setWindowManagerMirroring(true);
|
||||
final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
|
||||
final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
|
||||
virtualDisplayConfig.build(),
|
||||
callback, handler, windowContext);
|
||||
setSession(windowContextToken, virtualDisplay);
|
||||
return virtualDisplay;
|
||||
} catch (RemoteException e) {
|
||||
// Can not capture if WMS is not accessible, so bail out.
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link ContentRecordingSession} describing the recording taking place on this
|
||||
* {@link VirtualDisplay}.
|
||||
*
|
||||
* @throws RemoteException if updating the session on the server failed.
|
||||
*/
|
||||
private void setSession(@NonNull IBinder windowContextToken,
|
||||
@Nullable VirtualDisplay virtualDisplay)
|
||||
throws RemoteException {
|
||||
if (virtualDisplay == null) {
|
||||
// Not able to set up a new VirtualDisplay.
|
||||
return;
|
||||
}
|
||||
// Identify the VirtualDisplay that will be hosting the recording.
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
windowContextToken);
|
||||
session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
|
||||
// TODO(b/216625226) handle task recording.
|
||||
// Successfully set up, so save the current session details.
|
||||
WindowManagerGlobal.getWindowManagerService().setContentRecordingSession(session);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
michaelwr@google.com
|
||||
santoscordon@google.com
|
||||
|
||||
@@ -109,27 +109,26 @@ abstract class DisplayDevice {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window token of the level of the WindowManager hierarchy to mirror, or null
|
||||
* if layer mirroring by SurfaceFlinger should not be performed.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
* Returns the if WindowManager is responsible for mirroring on this display. If {@code false},
|
||||
* then SurfaceFlinger performs no layer mirroring on this display.
|
||||
* Only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
@Nullable
|
||||
public IBinder getWindowTokenClientToMirrorLocked() {
|
||||
return null;
|
||||
public boolean isWindowManagerMirroringLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the window token of the level of the level of the WindowManager hierarchy to mirror.
|
||||
* If windowToken is null, then no layer mirroring by SurfaceFlinger to should be performed.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
* Updates if WindowManager is responsible for mirroring on this display. If {@code false}, then
|
||||
* SurfaceFlinger performs no layer mirroring to this display.
|
||||
* Only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
|
||||
public void setWindowManagerMirroringLocked(boolean isMirroring) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default size of the surface associated with the display, or null if the surface
|
||||
* is not provided for layer mirroring by SurfaceFlinger.
|
||||
* For now, only used for mirroring started from MediaProjection.
|
||||
* Only used for mirroring started from MediaProjection.
|
||||
*/
|
||||
@Nullable
|
||||
public Point getDisplaySurfaceDefaultSize() {
|
||||
|
||||
@@ -2265,13 +2265,12 @@ public final class DisplayManagerService extends SystemService {
|
||||
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
|
||||
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
|
||||
|
||||
// Mirror the part of WM hierarchy that corresponds to the provided window token.
|
||||
IBinder windowTokenClientToMirror = device.getWindowTokenClientToMirrorLocked();
|
||||
|
||||
// Find the logical display that the display device is showing.
|
||||
// Certain displays only ever show their own content.
|
||||
LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
|
||||
if (!ownContent && windowTokenClientToMirror == null) {
|
||||
// Proceed with display-managed mirroring only if window manager will not be handling it.
|
||||
if (!ownContent && !device.isWindowManagerMirroringLocked()) {
|
||||
// Only mirror the display if content recording is not taking place in WM.
|
||||
if (display != null && !display.hasContentLocked()) {
|
||||
// If the display does not have any content of its own, then
|
||||
// automatically mirror the requested logical display contents if possible.
|
||||
@@ -3864,23 +3863,11 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder getWindowTokenClientToMirror(int displayId) {
|
||||
final DisplayDevice device;
|
||||
synchronized (mSyncRoot) {
|
||||
device = getDeviceForDisplayLocked(displayId);
|
||||
if (device == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return device.getWindowTokenClientToMirrorLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowTokenClientToMirror(int displayId, IBinder windowToken) {
|
||||
public void setWindowManagerMirroring(int displayId, boolean isMirroring) {
|
||||
synchronized (mSyncRoot) {
|
||||
final DisplayDevice device = getDeviceForDisplayLocked(displayId);
|
||||
if (device != null) {
|
||||
device.setWindowTokenClientToMirrorLocked(windowToken);
|
||||
device.setWindowManagerMirroringLocked(isMirroring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import static com.android.server.display.DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED;
|
||||
import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
|
||||
import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.display.IVirtualDisplayCallback;
|
||||
@@ -235,7 +234,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
|
||||
private Display.Mode mMode;
|
||||
private boolean mIsDisplayOn;
|
||||
private int mDisplayIdToMirror;
|
||||
private IBinder mWindowTokenClientToMirror;
|
||||
private boolean mIsWindowManagerMirroring;
|
||||
|
||||
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
|
||||
int ownerUid, String ownerPackageName, Surface surface, int flags,
|
||||
@@ -258,7 +257,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
|
||||
mUniqueIndex = uniqueIndex;
|
||||
mIsDisplayOn = surface != null;
|
||||
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
|
||||
mWindowTokenClientToMirror = virtualDisplayConfig.getWindowTokenClientToMirror();
|
||||
mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroring();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,15 +288,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IBinder getWindowTokenClientToMirrorLocked() {
|
||||
return mWindowTokenClientToMirror;
|
||||
public boolean isWindowManagerMirroringLocked() {
|
||||
return mIsWindowManagerMirroring;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
|
||||
if (mWindowTokenClientToMirror != windowToken) {
|
||||
mWindowTokenClientToMirror = windowToken;
|
||||
public void setWindowManagerMirroringLocked(boolean mirroring) {
|
||||
if (mIsWindowManagerMirroring != mirroring) {
|
||||
mIsWindowManagerMirroring = mirroring;
|
||||
sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
|
||||
sendTraversalRequestLocked();
|
||||
}
|
||||
@@ -391,7 +389,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
|
||||
pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
|
||||
pw.println("mStopped=" + mStopped);
|
||||
pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
|
||||
pw.println("mWindowTokenClientToMirror=" + mWindowTokenClientToMirror);
|
||||
pw.println("mWindowManagerMirroring=" + mIsWindowManagerMirroring);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
michaelwr@google.com
|
||||
santoscordon@google.com
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTENT_RECORDING;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.view.ContentRecordingSession;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
|
||||
/**
|
||||
* Orchestrates the handoff between displays if the recording session changes, and keeps track of
|
||||
* the current recording session state. Only supports one content recording session on the device at
|
||||
* once.
|
||||
*/
|
||||
final class ContentRecordingController {
|
||||
|
||||
/**
|
||||
* The current recording session.
|
||||
*/
|
||||
@Nullable
|
||||
private ContentRecordingSession mSession = null;
|
||||
|
||||
@Nullable
|
||||
private DisplayContent mDisplayContent = null;
|
||||
|
||||
/**
|
||||
* Returns the current recording session. If returns {@code null}, then recording is not taking
|
||||
* place.
|
||||
*/
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
ContentRecordingSession getContentRecordingSessionLocked() {
|
||||
// Copy out the session, to allow it to be modified without updating this reference.
|
||||
return mSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current recording session. If a new display is taking over recording, then
|
||||
* stops the prior display from recording.
|
||||
*
|
||||
* @param incomingSession the new recording session. Should either be {@code null}, to stop
|
||||
* the current session, or a session on a new/different display than the
|
||||
* current session.
|
||||
* @param wmService the window manager service
|
||||
*/
|
||||
void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession,
|
||||
@NonNull WindowManagerService wmService) {
|
||||
if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession)
|
||||
|| ContentRecordingSession.isSameDisplay(mSession, incomingSession))) {
|
||||
// Ignore an invalid session, or a session for the same display as currently recording.
|
||||
return;
|
||||
}
|
||||
DisplayContent incomingDisplayContent = null;
|
||||
if (incomingSession != null) {
|
||||
// Recording will start on a new display, possibly taking over from a current session.
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Handle incoming session on display %d, with a pre-existing session %s",
|
||||
incomingSession.getDisplayId(),
|
||||
mSession == null ? null : mSession.getDisplayId());
|
||||
incomingDisplayContent = wmService.mRoot.getDisplayContentOrCreate(
|
||||
incomingSession.getDisplayId());
|
||||
incomingDisplayContent.setContentRecordingSession(incomingSession);
|
||||
}
|
||||
if (mSession != null) {
|
||||
// Update the pre-existing display about the new session.
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Pause the recording session on display %s",
|
||||
mDisplayContent.getDisplayId());
|
||||
mDisplayContent.pauseRecording();
|
||||
mDisplayContent.setContentRecordingSession(null);
|
||||
}
|
||||
// Update the cached states.
|
||||
mDisplayContent = incomingDisplayContent;
|
||||
mSession = incomingSession;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import static android.os.Process.SYSTEM_UID;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
|
||||
import static android.util.RotationUtils.deltaRotation;
|
||||
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
|
||||
import static android.view.Display.FLAG_PRIVATE;
|
||||
@@ -88,10 +89,10 @@ import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTENT_RECORDING;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LAYER_MIRRORING;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
|
||||
@@ -197,6 +198,7 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
@@ -304,22 +306,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
*/
|
||||
private SurfaceControl mWindowingLayer;
|
||||
|
||||
// TODO(216756854) move all recording fields to the controller
|
||||
/**
|
||||
* The window token of the layer of the hierarchy to mirror, or null if this DisplayContent
|
||||
* is not being used for layer mirroring.
|
||||
* The session for content recording, or null if this DisplayContent is not being used for
|
||||
* recording.
|
||||
*/
|
||||
@VisibleForTesting IBinder mTokenToMirror = null;
|
||||
@VisibleForTesting private ContentRecordingSession mContentRecordingSession = null;
|
||||
|
||||
/**
|
||||
* The surface for mirroring the contents of this hierarchy, or null if layer mirroring is
|
||||
* The WindowContainer for the level of the hierarchy to record.
|
||||
*/
|
||||
@Nullable private DisplayContent mRecordedWindowContainer = null;
|
||||
|
||||
/**
|
||||
* The surface for recording the contents of this hierarchy, or null if content recording is
|
||||
* temporarily disabled.
|
||||
*/
|
||||
private SurfaceControl mMirroredSurface = null;
|
||||
@Nullable private SurfaceControl mRecordedSurface = null;
|
||||
|
||||
/**
|
||||
* The last bounds of the DisplayArea to mirror.
|
||||
* The last bounds of the region to record.
|
||||
*/
|
||||
private Rect mLastMirroredDisplayAreaBounds = null;
|
||||
@Nullable private Rect mLastRecordedBounds = null;
|
||||
|
||||
/**
|
||||
* The default per Display minimal size of tasks. Calculated at construction.
|
||||
@@ -2535,44 +2543,41 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
// Update IME parent if needed.
|
||||
updateImeParent();
|
||||
|
||||
// Update mirroring surface for MediaProjection, if this DisplayContent is being used
|
||||
// for layer mirroring.
|
||||
if (isCurrentlyMirroring() && mLastMirroredDisplayAreaBounds != null) {
|
||||
// Mirroring has already begun, but update mirroring since the display is now on.
|
||||
final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
|
||||
mTokenToMirror);
|
||||
if (wc == null) {
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Unable to retrieve window container to update layer mirroring for "
|
||||
// Update surface for MediaProjection, if this DisplayContent is being used for recording.
|
||||
if (isCurrentlyRecording() && mLastRecordedBounds != null) {
|
||||
// Recording has already begun, but update recording since the display is now on.
|
||||
if (mRecordedWindowContainer == null) {
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unexpectedly null window container; unable to update recording for "
|
||||
+ "display %d",
|
||||
mDisplayId);
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Display %d was already layer mirroring, so apply transformations if necessary",
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Display %d was already recording, so apply transformations if necessary",
|
||||
mDisplayId);
|
||||
// Retrieve the size of the DisplayArea to mirror, and continue with the update
|
||||
// Retrieve the size of the region to record, and continue with the update
|
||||
// if the bounds or orientation has changed.
|
||||
final Rect displayAreaBounds = wc.getDisplayContent().getBounds();
|
||||
int displayAreaOrientation = wc.getDisplayContent().getOrientation();
|
||||
if (!mLastMirroredDisplayAreaBounds.equals(displayAreaBounds)
|
||||
|| lastOrientation != displayAreaOrientation) {
|
||||
final Rect recordedContentBounds = mRecordedWindowContainer.getBounds();
|
||||
int recordedContentOrientation = mRecordedWindowContainer.getOrientation();
|
||||
if (!mLastRecordedBounds.equals(recordedContentBounds)
|
||||
|| lastOrientation != recordedContentOrientation) {
|
||||
Point surfaceSize = fetchSurfaceSizeIfPresent();
|
||||
if (surfaceSize != null) {
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Going ahead with updating layer mirroring for display %d to new "
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Going ahead with updating recording for display %d to new "
|
||||
+ "bounds %s and/or orientation %d.",
|
||||
mDisplayId, displayAreaBounds, displayAreaOrientation);
|
||||
mDisplayId, recordedContentBounds, recordedContentOrientation);
|
||||
updateMirroredSurface(mWmService.mTransactionFactory.get(),
|
||||
displayAreaBounds, surfaceSize);
|
||||
recordedContentBounds, surfaceSize);
|
||||
} else {
|
||||
// If the surface removed, do nothing. We will handle this via onDisplayChanged
|
||||
// (the display will be off if the surface is removed).
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Unable to update layer mirroring for display %d to new bounds %s"
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unable to update recording for display %d to new bounds %s"
|
||||
+ " and/or orientation %d, since the surface is not available.",
|
||||
mDisplayId, displayAreaBounds, displayAreaOrientation);
|
||||
mDisplayId, recordedContentBounds, recordedContentOrientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4563,8 +4568,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
|
||||
true /* inTraversal, must call performTraversalInTrans... below */);
|
||||
}
|
||||
// If the display now has content, or no longer has content, update layer mirroring.
|
||||
updateMirroring();
|
||||
// If the display now has content, or no longer has content, update recording.
|
||||
updateRecording();
|
||||
|
||||
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
|
||||
if (wallpaperVisible != mLastWallpaperVisible) {
|
||||
@@ -5558,13 +5563,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
} else if (displayState == Display.STATE_ON) {
|
||||
mOffTokenAcquirer.release(mDisplayId);
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Display %d state is now (%d), so update layer mirroring?",
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Display %d state is now (%d), so update recording?",
|
||||
mDisplayId, displayState);
|
||||
if (lastDisplayState != displayState) {
|
||||
// If state is on due to surface being added, then start layer mirroring.
|
||||
// If state is off due to surface being removed, then stop layer mirroring.
|
||||
updateMirroring();
|
||||
// If state is on due to surface being added, then start recording.
|
||||
// If state is off due to surface being removed, then stop recording.
|
||||
updateRecording();
|
||||
}
|
||||
}
|
||||
// Dispatch pending Configuration to WindowContext if the associated display changes to
|
||||
@@ -5867,11 +5872,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
mRemoved = true;
|
||||
|
||||
if (mMirroredSurface != null) {
|
||||
if (mRecordedSurface != null) {
|
||||
// Do not wait for the mirrored surface to be garbage collected, but clean up
|
||||
// immediately.
|
||||
mWmService.mTransactionFactory.get().remove(mMirroredSurface).apply();
|
||||
mMirroredSurface = null;
|
||||
mWmService.mTransactionFactory.get().remove(mRecordedSurface).apply();
|
||||
mRecordedSurface = null;
|
||||
clearContentRecordingSession();
|
||||
}
|
||||
|
||||
// Only update focus/visibility for the last one because there may be many root tasks are
|
||||
@@ -6112,68 +6118,84 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
|
||||
/**
|
||||
* Start mirroring to this DisplayContent if it does not have its own content. Captures the
|
||||
* content of a WindowContainer indicated by a WindowToken. If unable to start mirroring, falls
|
||||
* Start recording to this DisplayContent if it does not have its own content. Captures the
|
||||
* content of a WindowContainer indicated by a WindowToken. If unable to start recording, falls
|
||||
* back to original MediaProjection approach.
|
||||
*/
|
||||
private void startMirrorIfNeeded() {
|
||||
// Only mirror if this display does not have its own content, is not mirroring already,
|
||||
private void startRecordingIfNeeded() {
|
||||
// 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 (mLastHasContent || isCurrentlyMirroring() || mDisplay.getState() == Display.STATE_OFF) {
|
||||
if (mLastHasContent || isCurrentlyRecording() || mDisplay.getState() == Display.STATE_OFF
|
||||
|| mContentRecordingSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Given the WindowToken of the DisplayArea to mirror, retrieve the associated
|
||||
final int contentToRecord = mContentRecordingSession.getContentToRecord();
|
||||
if (contentToRecord != RECORD_CONTENT_DISPLAY) {
|
||||
// TODO(b/216625226) handle task-based recording
|
||||
// Not a valid region, or recording is disabled, so fall back to prior MediaProjection
|
||||
// approach.
|
||||
clearContentRecordingSession();
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unable to start recording due to invalid region for display %d",
|
||||
mDisplayId);
|
||||
return;
|
||||
}
|
||||
// Given the WindowToken of the DisplayArea to record, retrieve the associated
|
||||
// SurfaceControl.
|
||||
IBinder tokenToMirror = mWmService.mDisplayManagerInternal.getWindowTokenClientToMirror(
|
||||
mDisplayId);
|
||||
if (tokenToMirror == null) {
|
||||
// This DisplayContent instance is not involved in layer mirroring. If the display
|
||||
// has been created for capturing, fall back to prior MediaProjection approach.
|
||||
IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord();
|
||||
if (tokenToRecord == null) {
|
||||
// Unexpectedly missing token. Fall back to prior MediaProjection approach.
|
||||
clearContentRecordingSession();
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unable to start recording due to null token for display %d", mDisplayId);
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
|
||||
tokenToMirror);
|
||||
tokenToRecord);
|
||||
if (wc == null) {
|
||||
// Un-set the window token to mirror for this VirtualDisplay, to fall back to the
|
||||
// Un-set the window token to record for this VirtualDisplay. Fall back to the
|
||||
// original MediaProjection approach.
|
||||
mWmService.mDisplayManagerInternal.setWindowTokenClientToMirror(mDisplayId, null);
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Unable to retrieve window container to start layer mirroring for display %d",
|
||||
mWmService.mDisplayManagerInternal.setWindowManagerMirroring(mDisplayId, false);
|
||||
clearContentRecordingSession();
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unable to retrieve window container to start recording for "
|
||||
+ "display %d",
|
||||
mDisplayId);
|
||||
return;
|
||||
}
|
||||
// TODO(206461622) Migrate to the RootDisplayArea
|
||||
mRecordedWindowContainer = wc.getDisplayContent();
|
||||
|
||||
Point surfaceSize = fetchSurfaceSizeIfPresent();
|
||||
final Point surfaceSize = fetchSurfaceSizeIfPresent();
|
||||
if (surfaceSize == null) {
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Unable to start layer mirroring for display %d since the surface is not "
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Unable to start recording for display %d since the surface is not "
|
||||
+ "available.",
|
||||
mDisplayId);
|
||||
return;
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Display %d has no content and is on, so start layer mirroring for state %d",
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Display %d has no content and is on, so start recording for state %d",
|
||||
mDisplayId, mDisplay.getState());
|
||||
|
||||
// Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
|
||||
SurfaceControl sc = wc.getDisplayContent().getSurfaceControl();
|
||||
mMirroredSurface = SurfaceControl.mirrorSurface(sc);
|
||||
mRecordedSurface = SurfaceControl.mirrorSurface(
|
||||
mRecordedWindowContainer.getSurfaceControl());
|
||||
SurfaceControl.Transaction transaction = mWmService.mTransactionFactory.get()
|
||||
// Set the mMirroredSurface's parent to the root SurfaceControl for this
|
||||
// DisplayContent. This brings the new mirrored hierarchy under this DisplayContent,
|
||||
// so SurfaceControl will write the layers of this hierarchy to the output surface
|
||||
// provided by the app.
|
||||
.reparent(mMirroredSurface, mSurfaceControl)
|
||||
.reparent(mRecordedSurface, mSurfaceControl)
|
||||
// Reparent the SurfaceControl of this DisplayContent to null, to prevent content
|
||||
// being added to it. This ensures that no app launched explicitly on the
|
||||
// VirtualDisplay will show up as part of the mirrored content.
|
||||
.reparent(mWindowingLayer, null)
|
||||
.reparent(mOverlayLayer, null);
|
||||
// Retrieve the size of the DisplayArea to mirror.
|
||||
updateMirroredSurface(transaction, wc.getDisplayContent().getBounds(), surfaceSize);
|
||||
mTokenToMirror = tokenToMirror;
|
||||
updateMirroredSurface(transaction, mRecordedWindowContainer.getBounds(), surfaceSize);
|
||||
|
||||
// No need to clean up. In SurfaceFlinger, parents hold references to their children. The
|
||||
// mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is
|
||||
@@ -6182,34 +6204,64 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
|
||||
/**
|
||||
* Start mirroring if this DisplayContent no longer has content. Stop mirroring if it now
|
||||
* Pause the recording session.
|
||||
*/
|
||||
public void pauseRecording() {
|
||||
if (mRecordedSurface == null) {
|
||||
return;
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Display %d has content (%b) so pause recording", mDisplayId,
|
||||
mLastHasContent);
|
||||
// If the display is not on and it is a virtual display, then it no longer has an
|
||||
// associated surface to write output to.
|
||||
// If the display now has content, stop mirroring to it.
|
||||
mWmService.mTransactionFactory.get()
|
||||
// Remove the reference to mMirroredSurface, to clean up associated memory.
|
||||
.remove(mRecordedSurface)
|
||||
// Reparent the SurfaceControl of this DisplayContent back to mSurfaceControl,
|
||||
// to allow content to be added to it. This allows this DisplayContent to stop
|
||||
// mirroring and show content normally.
|
||||
.reparent(mWindowingLayer, mSurfaceControl)
|
||||
.reparent(mOverlayLayer, mSurfaceControl)
|
||||
.apply();
|
||||
// Pause mirroring by destroying the reference to the mirrored layer.
|
||||
mRecordedSurface = null;
|
||||
// Do not un-set the token, in case content is removed and recording should begin again.
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the incoming recording session. Should only be used when starting to record on
|
||||
* this display; stopping recording is handled separately when the display is destroyed.
|
||||
* @param session the new session indicating recording will begin on this display.
|
||||
*/
|
||||
public void setContentRecordingSession(@Nullable ContentRecordingSession session) {
|
||||
mContentRecordingSession = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes both the local cache and WM Service view of the current session, to stop the session
|
||||
* on this display.
|
||||
*/
|
||||
private void clearContentRecordingSession() {
|
||||
// Update the cached session state first, since updating the service will result in always
|
||||
// returning to this instance to update recording state.
|
||||
mContentRecordingSession = null;
|
||||
mWmService.setContentRecordingSession(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording if this DisplayContent no longer has content. Stop recording if it now
|
||||
* has content or the display is not on.
|
||||
*/
|
||||
private void updateMirroring() {
|
||||
if (isCurrentlyMirroring() && (mLastHasContent
|
||||
@VisibleForTesting void updateRecording() {
|
||||
if (isCurrentlyRecording() && (mLastHasContent
|
||||
|| mDisplay.getState() == Display.STATE_OFF)) {
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Display %d has content (%b) so disable layer mirroring", mDisplayId,
|
||||
mLastHasContent);
|
||||
// If the display is not on and it is a virtual display, then it no longer has an
|
||||
// associated surface to write output to.
|
||||
// If the display now has content, stop mirroring to it.
|
||||
mWmService.mTransactionFactory.get()
|
||||
// Remove the reference to mMirroredSurface, to clean up associated memory.
|
||||
.remove(mMirroredSurface)
|
||||
// Reparent the SurfaceControl of this DisplayContent back to mSurfaceControl,
|
||||
// to allow content to be added to it. This allows this DisplayContent to stop
|
||||
// mirroring and show content normally.
|
||||
.reparent(mWindowingLayer, mSurfaceControl)
|
||||
.reparent(mOverlayLayer, mSurfaceControl)
|
||||
.apply();
|
||||
// Stop mirroring by destroying the reference to the mirrored layer.
|
||||
mMirroredSurface = null;
|
||||
// Do not un-set the token, in case content is removed and mirroring should begin again.
|
||||
pauseRecording();
|
||||
} else {
|
||||
// Display no longer has content, or now has a surface to write to, so try to start
|
||||
// mirroring to it.
|
||||
startMirrorIfNeeded();
|
||||
// recording.
|
||||
startRecordingIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6217,22 +6269,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
* Apply transformations to the mirrored surface to ensure the captured contents are scaled to
|
||||
* fit and centred in the output surface.
|
||||
*
|
||||
* @param transaction the transaction to include transformations of mMirroredSurface
|
||||
* to. Transaction is not applied before returning.
|
||||
* @param displayAreaBounds bounds of the DisplayArea to mirror to the surface provided by
|
||||
* the app.
|
||||
* @param surfaceSize the default size of the surface to write the display area content to
|
||||
* @param transaction the transaction to include transformations of mMirroredSurface
|
||||
* to. Transaction is not applied before returning.
|
||||
* @param recordedContentBounds bounds of the content to record to the surface provided by
|
||||
* the app.
|
||||
* @param surfaceSize the default size of the surface to write the display area
|
||||
* content to
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updateMirroredSurface(SurfaceControl.Transaction transaction,
|
||||
Rect displayAreaBounds, Point surfaceSize) {
|
||||
Rect recordedContentBounds, Point surfaceSize) {
|
||||
// Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
|
||||
// output surface.
|
||||
float scaleX = surfaceSize.x / (float) displayAreaBounds.width();
|
||||
float scaleY = surfaceSize.y / (float) displayAreaBounds.height();
|
||||
float scaleX = surfaceSize.x / (float) recordedContentBounds.width();
|
||||
float scaleY = surfaceSize.y / (float) recordedContentBounds.height();
|
||||
float scale = Math.min(scaleX, scaleY);
|
||||
int scaledWidth = Math.round(scale * (float) displayAreaBounds.width());
|
||||
int scaledHeight = Math.round(scale * (float) displayAreaBounds.height());
|
||||
int scaledWidth = Math.round(scale * (float) recordedContentBounds.width());
|
||||
int scaledHeight = Math.round(scale * (float) recordedContentBounds.height());
|
||||
|
||||
// Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored
|
||||
// contents in the output surface.
|
||||
@@ -6248,16 +6301,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
transaction
|
||||
// Crop the area to capture to exclude the 'extra' wallpaper that is used
|
||||
// for parallax (b/189930234).
|
||||
.setWindowCrop(mMirroredSurface, displayAreaBounds.width(),
|
||||
displayAreaBounds.height())
|
||||
.setWindowCrop(mRecordedSurface, recordedContentBounds.width(),
|
||||
recordedContentBounds.height())
|
||||
// Scale the root mirror SurfaceControl, based upon the size difference between the
|
||||
// source (DisplayArea to capture) and output (surface the app reads images from).
|
||||
.setMatrix(mMirroredSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
|
||||
.setMatrix(mRecordedSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
|
||||
// Position needs to be updated when the mirrored DisplayArea has changed, since
|
||||
// the content will no longer be centered in the output surface.
|
||||
.setPosition(mMirroredSurface, shiftedX /* x */, shiftedY /* y */)
|
||||
.setPosition(mRecordedSurface, shiftedX /* x */, shiftedY /* y */)
|
||||
.apply();
|
||||
mLastMirroredDisplayAreaBounds = new Rect(displayAreaBounds);
|
||||
mLastRecordedBounds = new Rect(recordedContentBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6274,8 +6327,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
// Layer mirroring started with a null surface, so do not apply any transformations yet.
|
||||
// State of virtual display will change to 'ON' when the surface is set.
|
||||
// will get event DISPLAY_DEVICE_EVENT_CHANGED
|
||||
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
|
||||
"Provided surface for layer mirroring on display %d is not present, so do not"
|
||||
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
|
||||
"Provided surface for recording on display %d is not present, so do not"
|
||||
+ " update the surface",
|
||||
mDisplayId);
|
||||
return null;
|
||||
@@ -6284,10 +6337,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this DisplayContent is currently layer mirroring.
|
||||
* Returns {@code true} if this DisplayContent is currently recording.
|
||||
*/
|
||||
boolean isCurrentlyMirroring() {
|
||||
return mTokenToMirror != null && mMirroredSurface != null;
|
||||
boolean isCurrentlyRecording() {
|
||||
return mContentRecordingSession != null && mRecordedSurface != null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable ContentRecordingSession getContentRecordingSession() {
|
||||
return mContentRecordingSession;
|
||||
}
|
||||
|
||||
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
|
||||
|
||||
@@ -229,6 +229,7 @@ import android.util.TimeUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.Choreographer;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Gravity;
|
||||
@@ -748,6 +749,9 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
private InputTarget mFocusedInputTarget;
|
||||
|
||||
@VisibleForTesting
|
||||
final ContentRecordingController mContentRecordingController = new ContentRecordingController();
|
||||
|
||||
@VisibleForTesting
|
||||
final class SettingsObserver extends ContentObserver {
|
||||
private final Uri mDisplayInversionEnabledUri =
|
||||
@@ -2894,6 +2898,16 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current content mirroring session.
|
||||
*/
|
||||
@Override
|
||||
public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
|
||||
synchronized (mGlobalLock) {
|
||||
mContentRecordingController.setContentRecordingSessionLocked(incomingSession, this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(multi-display): remove when no default display use case.
|
||||
void prepareAppTransitionNone() {
|
||||
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
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.never;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.ContentRecordingSession;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for the {@link ContentRecordingController} class.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:ContentRecordingControllerTests
|
||||
*/
|
||||
@SmallTest
|
||||
@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);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
spyOn(mDisplayContent);
|
||||
mDefaultSession.setDisplayId(DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetContentRecordingSessionLocked() {
|
||||
ContentRecordingController controller = new ContentRecordingController();
|
||||
assertThat(controller.getContentRecordingSessionLocked()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetContentRecordingSessionLocked_defaultSession() {
|
||||
ContentRecordingController controller = new ContentRecordingController();
|
||||
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
|
||||
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
|
||||
assertThat(resultingSession).isEqualTo(mDefaultSession);
|
||||
}
|
||||
|
||||
@Test
|
||||
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 an invalid display session (null token).
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(null);
|
||||
session.setDisplayId(DEFAULT_DISPLAY);
|
||||
// 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_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);
|
||||
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
|
||||
// THEN the valid session was accepted.
|
||||
assertThat(resultingSession).isEqualTo(session);
|
||||
verify(mDisplayContent, atLeastOnce()).setContentRecordingSession(session);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
// WHEN updating the session.
|
||||
ContentRecordingSession sessionUpdate = ContentRecordingSession.createDisplaySession(
|
||||
new RecordingTestToken());
|
||||
sessionUpdate.setDisplayId(DEFAULT_DISPLAY);
|
||||
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
|
||||
|
||||
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
|
||||
// THEN the session was not accepted.
|
||||
assertThat(resultingSession).isEqualTo(session);
|
||||
verify(mDisplayContent, 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);
|
||||
|
||||
// WHEN updating the session.
|
||||
ContentRecordingSession sessionUpdate = null;
|
||||
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
|
||||
|
||||
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
|
||||
// THEN the valid session was accepted.
|
||||
assertThat(resultingSession).isEqualTo(sessionUpdate);
|
||||
// Do not need to update the display content, since it will handle stopping the session
|
||||
// via state change callbacks.
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
|
||||
// WHEN updating the session.
|
||||
final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
|
||||
mDisplayInfo).build();
|
||||
ContentRecordingSession sessionUpdate = ContentRecordingSession.createDisplaySession(
|
||||
TEST_TOKEN);
|
||||
sessionUpdate.setDisplayId(virtualDisplay.getDisplayId());
|
||||
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
|
||||
|
||||
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
|
||||
// THEN the valid session was accepted.
|
||||
assertThat(resultingSession).isEqualTo(sessionUpdate);
|
||||
verify(virtualDisplay).setContentRecordingSession(sessionUpdate);
|
||||
// THEN the recording was paused on the prior display.
|
||||
verify(mDisplayContent).pauseRecording();
|
||||
|
||||
}
|
||||
|
||||
private static class RecordingTestToken extends Binder {
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,7 @@ import android.os.SystemClock;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ContentRecordingSession;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Gravity;
|
||||
@@ -2301,8 +2302,18 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
|
||||
mDisplayInfo).build();
|
||||
|
||||
// GIVEN a session is set up to capture a DisplayContent.
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
tokenToMirror);
|
||||
session.setDisplayId(virtualDisplay.getDisplayId());
|
||||
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
|
||||
|
||||
// WHEN attempting to mirror on the virtual display.
|
||||
virtualDisplay.updateRecording();
|
||||
|
||||
// THEN mirroring is initiated for the default display's DisplayArea.
|
||||
assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
|
||||
assertThat(virtualDisplay.isCurrentlyRecording()).isTrue();
|
||||
assertThat(virtualDisplay.getContentRecordingSession()).isEqualTo(session);
|
||||
|
||||
mockSession.finishMocking();
|
||||
}
|
||||
@@ -2329,9 +2340,14 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
|
||||
mDisplayInfo).build();
|
||||
|
||||
// THEN mirroring is initiated for the default display's DisplayArea.
|
||||
assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
|
||||
// GIVEN a session is set up to capture a DisplayContent.
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
tokenToMirror);
|
||||
session.setDisplayId(virtualDisplay.getDisplayId());
|
||||
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
|
||||
|
||||
// WHEN attempting to mirror on the virtual display, and the captured content is resized.
|
||||
virtualDisplay.updateRecording();
|
||||
float xScale = 0.7f;
|
||||
float yScale = 2f;
|
||||
Rect displayAreaBounds = new Rect(0, 0, Math.round(surfaceSize.x * xScale),
|
||||
@@ -2359,7 +2375,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
|
||||
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
|
||||
// mirror.
|
||||
setUpDefaultTaskDisplayAreaWindowToken();
|
||||
final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
|
||||
|
||||
// GIVEN SurfaceControl does not mirror a null surface.
|
||||
Point surfaceSize = new Point(
|
||||
@@ -2373,9 +2389,15 @@ 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);
|
||||
mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
|
||||
actualDC.updateRecording();
|
||||
|
||||
// THEN mirroring is not started, since a null surface indicates the VirtualDisplay is off.
|
||||
assertThat(actualDC.mTokenToMirror).isNull();
|
||||
assertThat(actualDC.isCurrentlyRecording()).isFalse();
|
||||
assertThat(actualDC.getContentRecordingSession()).isEqualTo(session);
|
||||
|
||||
display.release();
|
||||
mockSession.finishMocking();
|
||||
@@ -2402,44 +2424,27 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
// GIVEN a new VirtualDisplay with an associated surface.
|
||||
final VirtualDisplay display = createVirtualDisplay(surfaceSize, new Surface());
|
||||
final int displayId = display.getDisplay().getDisplayId();
|
||||
|
||||
// GIVEN a session for this display.
|
||||
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
|
||||
tokenToMirror);
|
||||
session.setDisplayId(displayId);
|
||||
mWm.setContentRecordingSession(session);
|
||||
mWm.mRoot.onDisplayAdded(displayId);
|
||||
|
||||
// WHEN getting the DisplayContent for the new virtual display.
|
||||
DisplayContent actualDC = mWm.mRoot.getDisplayContent(displayId);
|
||||
actualDC.updateRecording();
|
||||
|
||||
// THEN mirroring is initiated for the default display's DisplayArea.
|
||||
assertThat(actualDC.mTokenToMirror).isEqualTo(tokenToMirror);
|
||||
assertThat(actualDC.isCurrentlyRecording()).isTrue();
|
||||
assertThat(actualDC.getContentRecordingSession()).isEqualTo(session);
|
||||
|
||||
display.release();
|
||||
mockSession.finishMocking();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepClearAreasMultipleWindows() {
|
||||
final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
|
||||
final Rect rect1 = new Rect(0, 0, 10, 10);
|
||||
w1.setKeepClearAreas(Arrays.asList(rect1), Collections.emptyList());
|
||||
final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
|
||||
final Rect rect2 = new Rect(10, 10, 20, 20);
|
||||
w2.setKeepClearAreas(Arrays.asList(rect2), Collections.emptyList());
|
||||
|
||||
// No keep clear areas on display, because the windows are not visible
|
||||
assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
|
||||
|
||||
makeWindowVisible(w1);
|
||||
|
||||
// The returned keep-clear areas contain the areas just from the visible window
|
||||
assertEquals(new ArraySet(Arrays.asList(rect1)),
|
||||
new ArraySet(mDisplayContent.getKeepClearAreas()));
|
||||
|
||||
makeWindowVisible(w1, w2);
|
||||
|
||||
// The returned keep-clear areas contain the areas from all visible windows
|
||||
assertEquals(new ArraySet(Arrays.asList(rect1, rect2)),
|
||||
new ArraySet(mDisplayContent.getKeepClearAreas()));
|
||||
}
|
||||
|
||||
private class TestToken extends Binder {
|
||||
private static class MirroringTestToken extends Binder {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2449,10 +2454,7 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
private IBinder setUpDefaultTaskDisplayAreaWindowToken() {
|
||||
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
|
||||
// mirror.
|
||||
final IBinder tokenToMirror = new TestToken();
|
||||
doReturn(tokenToMirror).when(mWm.mDisplayManagerInternal).getWindowTokenClientToMirror(
|
||||
anyInt());
|
||||
|
||||
final IBinder tokenToMirror = new MirroringTestToken();
|
||||
// GIVEN the default task display area is represented by the WindowToken.
|
||||
spyOn(mWm.mWindowContextListenerController);
|
||||
doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
|
||||
@@ -2481,6 +2483,31 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
DisplayMetrics.DENSITY_140, surface, VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepClearAreasMultipleWindows() {
|
||||
final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
|
||||
final Rect rect1 = new Rect(0, 0, 10, 10);
|
||||
w1.setKeepClearAreas(Arrays.asList(rect1), Collections.emptyList());
|
||||
final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
|
||||
final Rect rect2 = new Rect(10, 10, 20, 20);
|
||||
w2.setKeepClearAreas(Arrays.asList(rect2), Collections.emptyList());
|
||||
|
||||
// No keep clear areas on display, because the windows are not visible
|
||||
assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
|
||||
|
||||
makeWindowVisible(w1);
|
||||
|
||||
// The returned keep-clear areas contain the areas just from the visible window
|
||||
assertEquals(new ArraySet(Arrays.asList(rect1)),
|
||||
new ArraySet(mDisplayContent.getKeepClearAreas()));
|
||||
|
||||
makeWindowVisible(w1, w2);
|
||||
|
||||
// The returned keep-clear areas contain the areas from all visible windows
|
||||
assertEquals(new ArraySet(Arrays.asList(rect1, rect2)),
|
||||
new ArraySet(mDisplayContent.getKeepClearAreas()));
|
||||
}
|
||||
|
||||
private void removeRootTaskTests(Runnable runnable) {
|
||||
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
|
||||
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
|
||||
|
||||
Reference in New Issue
Block a user