Merge "(5/N)[MediaProjection] Send no frames until user reviews consent" into udc-dev

This commit is contained in:
Naomi Musgrave
2023-05-16 11:19:54 +00:00
committed by Android (Google) Code Review
11 changed files with 138 additions and 53 deletions

View File

@@ -87,7 +87,7 @@ public final class ContentRecordingSession implements Parcelable {
* *
* <p>Only set on the server side to sanitize any input from the client process. * <p>Only set on the server side to sanitize any input from the client process.
*/ */
private boolean mWaitingToRecord = false; private boolean mWaitingForConsent = false;
/** /**
* Default instance, with recording the display. * Default instance, with recording the display.
@@ -181,7 +181,7 @@ public final class ContentRecordingSession implements Parcelable {
@RecordContent int contentToRecord, @RecordContent int contentToRecord,
int displayToRecord, int displayToRecord,
@Nullable IBinder tokenToRecord, @Nullable IBinder tokenToRecord,
boolean waitingToRecord) { boolean waitingForConsent) {
this.mVirtualDisplayId = virtualDisplayId; this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord; this.mContentToRecord = contentToRecord;
@@ -195,7 +195,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord; this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord; this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord; this.mWaitingForConsent = waitingForConsent;
// onConstructed(); // You can define this method to get a callback // onConstructed(); // You can define this method to get a callback
} }
@@ -246,8 +246,8 @@ public final class ContentRecordingSession implements Parcelable {
* <p>Only set on the server side to sanitize any input from the client process. * <p>Only set on the server side to sanitize any input from the client process.
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
public boolean isWaitingToRecord() { public boolean isWaitingForConsent() {
return mWaitingToRecord; return mWaitingForConsent;
} }
/** /**
@@ -309,8 +309,8 @@ public final class ContentRecordingSession implements Parcelable {
* <p>Only set on the server side to sanitize any input from the client process. * <p>Only set on the server side to sanitize any input from the client process.
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
public @NonNull ContentRecordingSession setWaitingToRecord( boolean value) { public @NonNull ContentRecordingSession setWaitingForConsent( boolean value) {
mWaitingToRecord = value; mWaitingForConsent = value;
return this; return this;
} }
@@ -325,7 +325,7 @@ public final class ContentRecordingSession implements Parcelable {
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " + "contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
"displayToRecord = " + mDisplayToRecord + ", " + "displayToRecord = " + mDisplayToRecord + ", " +
"tokenToRecord = " + mTokenToRecord + ", " + "tokenToRecord = " + mTokenToRecord + ", " +
"waitingToRecord = " + mWaitingToRecord + "waitingForConsent = " + mWaitingForConsent +
" }"; " }";
} }
@@ -346,7 +346,7 @@ public final class ContentRecordingSession implements Parcelable {
&& mContentToRecord == that.mContentToRecord && mContentToRecord == that.mContentToRecord
&& mDisplayToRecord == that.mDisplayToRecord && mDisplayToRecord == that.mDisplayToRecord
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord) && java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
&& mWaitingToRecord == that.mWaitingToRecord; && mWaitingForConsent == that.mWaitingForConsent;
} }
@Override @Override
@@ -360,7 +360,7 @@ public final class ContentRecordingSession implements Parcelable {
_hash = 31 * _hash + mContentToRecord; _hash = 31 * _hash + mContentToRecord;
_hash = 31 * _hash + mDisplayToRecord; _hash = 31 * _hash + mDisplayToRecord;
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord); _hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
_hash = 31 * _hash + Boolean.hashCode(mWaitingToRecord); _hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
return _hash; return _hash;
} }
@@ -371,7 +371,7 @@ public final class ContentRecordingSession implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... } // void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0; byte flg = 0;
if (mWaitingToRecord) flg |= 0x10; if (mWaitingForConsent) flg |= 0x10;
if (mTokenToRecord != null) flg |= 0x8; if (mTokenToRecord != null) flg |= 0x8;
dest.writeByte(flg); dest.writeByte(flg);
dest.writeInt(mVirtualDisplayId); dest.writeInt(mVirtualDisplayId);
@@ -392,7 +392,7 @@ public final class ContentRecordingSession implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... } // static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte(); byte flg = in.readByte();
boolean waitingToRecord = (flg & 0x10) != 0; boolean waitingForConsent = (flg & 0x10) != 0;
int virtualDisplayId = in.readInt(); int virtualDisplayId = in.readInt();
int contentToRecord = in.readInt(); int contentToRecord = in.readInt();
int displayToRecord = in.readInt(); int displayToRecord = in.readInt();
@@ -411,7 +411,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord; this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord; this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord; this.mWaitingForConsent = waitingForConsent;
// onConstructed(); // You can define this method to get a callback // onConstructed(); // You can define this method to get a callback
} }
@@ -441,7 +441,7 @@ public final class ContentRecordingSession implements Parcelable {
private @RecordContent int mContentToRecord; private @RecordContent int mContentToRecord;
private int mDisplayToRecord; private int mDisplayToRecord;
private @Nullable IBinder mTokenToRecord; private @Nullable IBinder mTokenToRecord;
private boolean mWaitingToRecord; private boolean mWaitingForConsent;
private long mBuilderFieldsSet = 0L; private long mBuilderFieldsSet = 0L;
@@ -506,10 +506,10 @@ public final class ContentRecordingSession implements Parcelable {
* <p>Only set on the server side to sanitize any input from the client process. * <p>Only set on the server side to sanitize any input from the client process.
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
public @NonNull Builder setWaitingToRecord(boolean value) { public @NonNull Builder setWaitingForConsent(boolean value) {
checkNotUsed(); checkNotUsed();
mBuilderFieldsSet |= 0x10; mBuilderFieldsSet |= 0x10;
mWaitingToRecord = value; mWaitingForConsent = value;
return this; return this;
} }
@@ -531,14 +531,14 @@ public final class ContentRecordingSession implements Parcelable {
mTokenToRecord = null; mTokenToRecord = null;
} }
if ((mBuilderFieldsSet & 0x10) == 0) { if ((mBuilderFieldsSet & 0x10) == 0) {
mWaitingToRecord = false; mWaitingForConsent = false;
} }
ContentRecordingSession o = new ContentRecordingSession( ContentRecordingSession o = new ContentRecordingSession(
mVirtualDisplayId, mVirtualDisplayId,
mContentToRecord, mContentToRecord,
mDisplayToRecord, mDisplayToRecord,
mTokenToRecord, mTokenToRecord,
mWaitingToRecord); mWaitingForConsent);
return o; return o;
} }
@@ -551,10 +551,10 @@ public final class ContentRecordingSession implements Parcelable {
} }
@DataClass.Generated( @DataClass.Generated(
time = 1679855157534L, time = 1683628463074L,
codegenVersion = "1.0.23", codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java", 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 mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingToRecord\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)") inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated @Deprecated
private void __metadata() {} private void __metadata() {}

View File

@@ -1675,6 +1675,12 @@
"group": "WM_DEBUG_CONFIGURATION", "group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/am\/ActivityManagerService.java" "at": "com\/android\/server\/am\/ActivityManagerService.java"
}, },
"-584061725": {
"message": "Content Recording: Accept session updating same display %d with granted consent, with an existing session %s",
"level": "VERBOSE",
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecordingController.java"
},
"-583031528": { "-583031528": {
"message": "%s", "message": "%s",
"level": "INFO", "level": "INFO",
@@ -2167,6 +2173,12 @@
"group": "WM_DEBUG_BACK_PREVIEW", "group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/TaskFragment.java" "at": "com\/android\/server\/wm\/TaskFragment.java"
}, },
"-125383273": {
"message": "Content Recording: waiting to record, so do nothing",
"level": "VERBOSE",
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
"-124316973": { "-124316973": {
"message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s", "message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
"level": "VERBOSE", "level": "VERBOSE",

View File

@@ -1560,7 +1560,7 @@ public final class DisplayManagerService extends SystemService {
// VirtualDisplay has been successfully constructed. // VirtualDisplay has been successfully constructed.
session.setVirtualDisplayId(displayId); session.setVirtualDisplayId(displayId);
// Don't start mirroring until user re-grants consent. // Don't start mirroring until user re-grants consent.
session.setWaitingToRecord(waitForPermissionConsent); session.setWaitingForConsent(waitForPermissionConsent);
// We set the content recording session here on the server side instead of using // We set the content recording session here on the server side instead of using
// a second AIDL call in MediaProjection. By ensuring that a virtual display has // a second AIDL call in MediaProjection. By ensuring that a virtual display has

View File

@@ -406,7 +406,7 @@ public final class MediaProjectionManagerService extends SystemService
return; return;
} }
if (mProjectionGrant.mSession == null if (mProjectionGrant.mSession == null
|| !mProjectionGrant.mSession.isWaitingToRecord()) { || !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.w(TAG, "Reusing token: Ignore consent result " + consentResult Slog.w(TAG, "Reusing token: Ignore consent result " + consentResult
+ " if not waiting for the result."); + " if not waiting for the result.");
return; return;
@@ -445,7 +445,7 @@ public final class MediaProjectionManagerService extends SystemService
*/ */
private void setReviewedConsentSessionLocked(@Nullable ContentRecordingSession session) { private void setReviewedConsentSessionLocked(@Nullable ContentRecordingSession session) {
if (session != null) { if (session != null) {
session.setWaitingToRecord(false); session.setWaitingForConsent(false);
session.setVirtualDisplayId(mProjectionGrant.mVirtualDisplayId); session.setVirtualDisplayId(mProjectionGrant.mVirtualDisplayId);
} }
@@ -490,7 +490,7 @@ public final class MediaProjectionManagerService extends SystemService
// Supposedly the package has re-used the user's consent; confirm the provided details // Supposedly the package has re-used the user's consent; confirm the provided details
// against the current projection token before re-using the current projection. // against the current projection token before re-using the current projection.
if (mProjectionGrant == null || mProjectionGrant.mSession == null if (mProjectionGrant == null || mProjectionGrant.mSession == null
|| !mProjectionGrant.mSession.isWaitingToRecord()) { || !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.e(TAG, "Reusing token: Not possible to reuse the current projection " Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
+ "instance"); + "instance");
return null; return null;

View File

@@ -276,6 +276,12 @@ final class ContentRecorder implements WindowContainerListener {
return; return;
} }
if (mContentRecordingSession.isWaitingForConsent()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
+ "nothing");
return;
}
mRecordedWindowContainer = retrieveRecordedWindowContainer(); mRecordedWindowContainer = retrieveRecordedWindowContainer();
if (mRecordedWindowContainer == null) { if (mRecordedWindowContainer == null) {
// Either the token is missing, or the window associated with the token is missing. // Either the token is missing, or the window associated with the token is missing.

View File

@@ -56,8 +56,8 @@ final class ContentRecordingController {
* Updates the current recording session. * Updates the current recording session.
* <p>Handles the following scenarios: * <p>Handles the following scenarios:
* <ul> * <ul>
* <li>Invalid scenarios: The incoming session is malformed, or the incoming session is * <li>Invalid scenarios: The incoming session is malformed.</li>
* identical to the current session</li> * <li>Ignored scenario: the incoming session is identical to the current session.</li>
* <li>Start Scenario: Starting a new session. Recording begins immediately.</li> * <li>Start Scenario: Starting a new session. Recording begins immediately.</li>
* <li>Takeover Scenario: Occurs during a Start Scenario, if a pre-existing session was * <li>Takeover Scenario: Occurs during a Start Scenario, if a pre-existing session was
* in-progress. For example, recording on VirtualDisplay "app_foo" was ongoing. A * in-progress. For example, recording on VirtualDisplay "app_foo" was ongoing. A
@@ -66,6 +66,8 @@ final class ContentRecordingController {
* begin.</li> * begin.</li>
* <li>Stopping scenario: The incoming session is null and there is currently an ongoing * <li>Stopping scenario: The incoming session is null and there is currently an ongoing
* session. The controller stops recording.</li> * session. The controller stops recording.</li>
* <li>Updating scenario: There is an update for the same display, where recording
* was previously not taking place but is now permitted to go ahead.</li>
* </ul> * </ul>
* *
* @param incomingSession The incoming recording session (either an update to a current session * @param incomingSession The incoming recording session (either an update to a current session
@@ -78,20 +80,28 @@ final class ContentRecordingController {
if (incomingSession != null && !ContentRecordingSession.isValid(incomingSession)) { if (incomingSession != null && !ContentRecordingSession.isValid(incomingSession)) {
return; return;
} }
// Invalid scenario: ignore identical incoming session. final boolean hasSessionUpdatedWithConsent =
mSession != null && incomingSession != null && mSession.isWaitingForConsent()
&& !incomingSession.isWaitingForConsent();
if (ContentRecordingSession.isProjectionOnSameDisplay(mSession, incomingSession)) { if (ContentRecordingSession.isProjectionOnSameDisplay(mSession, incomingSession)) {
// TODO(242833866): if incoming session is no longer waiting to record, allow if (hasSessionUpdatedWithConsent) {
// the update through. // Updating scenario: accept an incoming session updating the current display.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Accept session updating same display %d with granted "
+ "consent, with an existing session %s",
incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
} else {
// Ignored scenario: ignore identical incoming session.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Ignoring session on same display %d, with an existing " "Content Recording: Ignoring session on same display %d, with an existing "
+ "session %s", + "session %s",
incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId()); incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
return; return;
} }
}
DisplayContent incomingDisplayContent = null; DisplayContent incomingDisplayContent = null;
// Start scenario: recording begins immediately.
if (incomingSession != null) { if (incomingSession != null) {
// Start scenario: recording begins immediately.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Handle incoming session on display %d, with a " "Content Recording: Handle incoming session on display %d, with a "
+ "pre-existing session %s", incomingSession.getVirtualDisplayId(), + "pre-existing session %s", incomingSession.getVirtualDisplayId(),
@@ -106,11 +116,14 @@ final class ContentRecordingController {
return; return;
} }
incomingDisplayContent.setContentRecordingSession(incomingSession); incomingDisplayContent.setContentRecordingSession(incomingSession);
// TODO(b/270118861): When user grants consent to re-use, explicitly ask ContentRecorder // Updating scenario: Explicitly ask ContentRecorder to update, since no config or
// to update, since no config/display change arrives. Mark recording as black. // display change will trigger an update from the DisplayContent.
if (hasSessionUpdatedWithConsent) {
incomingDisplayContent.updateRecording();
}
} }
// Takeover and stopping scenario: stop recording on the pre-existing session. // Takeover and stopping scenario: stop recording on the pre-existing session.
if (mSession != null) { if (mSession != null && !hasSessionUpdatedWithConsent) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Pause the recording session on display %s", "Content Recording: Pause the recording session on display %s",
mDisplayContent.getDisplayId()); mDisplayContent.getDisplayId());

View File

@@ -6693,9 +6693,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** /**
* Start recording if this DisplayContent no longer has content. Stop recording if it now * Start recording if this DisplayContent no longer has content. Stop recording if it now
* has content or the display is not on. * has content or the display is not on. Update recording if the content has changed (for
* example, the user has granted consent to token re-use, so we can now start mirroring).
*/ */
@VisibleForTesting void updateRecording() { void updateRecording() {
if (mContentRecorder == null || !mContentRecorder.isContentRecordingSessionSet()) { if (mContentRecorder == null || !mContentRecorder.isContentRecordingSessionSet()) {
if (!setDisplayMirroring()) { if (!setDisplayMirroring()) {
return; return;

View File

@@ -1079,7 +1079,7 @@ public class DisplayManagerServiceTest {
verify(mMockProjectionService, atLeastOnce()).setContentRecordingSession( verify(mMockProjectionService, atLeastOnce()).setContentRecordingSession(
mContentRecordingSessionCaptor.capture(), nullable(IMediaProjection.class)); mContentRecordingSessionCaptor.capture(), nullable(IMediaProjection.class));
ContentRecordingSession session = mContentRecordingSessionCaptor.getValue(); ContentRecordingSession session = mContentRecordingSessionCaptor.getValue();
assertThat(session.isWaitingToRecord()).isTrue(); assertThat(session.isWaitingForConsent()).isTrue();
} }
@Test @Test
@@ -1114,7 +1114,7 @@ public class DisplayManagerServiceTest {
assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY); assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
assertThat(session.getVirtualDisplayId()).isEqualTo(displayId); assertThat(session.getVirtualDisplayId()).isEqualTo(displayId);
assertThat(session.getDisplayToRecord()).isEqualTo(displayToRecord); assertThat(session.getDisplayToRecord()).isEqualTo(displayToRecord);
assertThat(session.isWaitingToRecord()).isFalse(); assertThat(session.isWaitingForConsent()).isFalse();
} }
@Test @Test

View File

@@ -139,7 +139,7 @@ public class MediaProjectionManagerServiceTest {
doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mPackageManager).when(mContext).getPackageManager();
mClock = new OffsettableClock.Stopped(); mClock = new OffsettableClock.Stopped();
mWaitingDisplaySession.setWaitingToRecord(true); mWaitingDisplaySession.setWaitingForConsent(true);
mWaitingDisplaySession.setVirtualDisplayId(5); mWaitingDisplaySession.setVirtualDisplayId(5);
mAppInfo.targetSdkVersion = 32; mAppInfo.targetSdkVersion = 32;
@@ -484,7 +484,7 @@ public class MediaProjectionManagerServiceTest {
mSessionCaptor.capture()); mSessionCaptor.capture());
// Examine latest value. // Examine latest value.
final ContentRecordingSession capturedSession = mSessionCaptor.getValue(); final ContentRecordingSession capturedSession = mSessionCaptor.getValue();
assertThat(capturedSession.isWaitingToRecord()).isFalse(); assertThat(capturedSession.isWaitingForConsent()).isFalse();
assertThat(capturedSession.getVirtualDisplayId()).isEqualTo(INVALID_DISPLAY); assertThat(capturedSession.getVirtualDisplayId()).isEqualTo(INVALID_DISPLAY);
} }

View File

@@ -79,6 +79,8 @@ public class ContentRecorderTests extends WindowTestsBase {
private Task mTask; private Task mTask;
private final ContentRecordingSession mDisplaySession = private final ContentRecordingSession mDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY); ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private ContentRecordingSession mTaskSession; private ContentRecordingSession mTaskSession;
private static Point sSurfaceSize; private static Point sSurfaceSize;
private ContentRecorder mContentRecorder; private ContentRecorder mContentRecorder;
@@ -120,6 +122,10 @@ public class ContentRecorderTests extends WindowTestsBase {
mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken); mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
mTaskSession.setVirtualDisplayId(displayId); mTaskSession.setVirtualDisplayId(displayId);
// GIVEN a session is waiting for the user to review consent.
mWaitingDisplaySession.setVirtualDisplayId(displayId);
mWaitingDisplaySession.setWaitingForConsent(true);
mConfigListener = new ConfigListener(); mConfigListener = new ConfigListener();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
mContext.getMainExecutor(), mConfigListener); mContext.getMainExecutor(), mConfigListener);
@@ -220,6 +226,18 @@ public class ContentRecorderTests extends WindowTestsBase {
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
} }
@Test
public void testUpdateRecording_waitingForConsent() {
mContentRecorder.setContentRecordingSession(mWaitingDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
}
@Test @Test
public void testOnConfigurationChanged_neverRecording() { public void testOnConfigurationChanged_neverRecording() {
mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT); mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);

View File

@@ -24,10 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession; import android.view.ContentRecordingSession;
@@ -36,6 +34,8 @@ import androidx.test.filters.SmallTest;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** /**
* Tests for the {@link ContentRecordingController} class. * Tests for the {@link ContentRecordingController} class.
@@ -49,12 +49,20 @@ import org.junit.runner.RunWith;
public class ContentRecordingControllerTests extends WindowTestsBase { public class ContentRecordingControllerTests extends WindowTestsBase {
private final ContentRecordingSession mDefaultSession = private final ContentRecordingSession mDefaultSession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY); ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private int mVirtualDisplayId; private int mVirtualDisplayId;
private DisplayContent mVirtualDisplayContent; private DisplayContent mVirtualDisplayContent;
private WindowContainer.RemoteToken mRootTaskToken;
@Mock
private WindowContainer mTaskWindowContainer;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this);
// GIVEN the VirtualDisplay associated with the session (so the display has state ON). // GIVEN the VirtualDisplay associated with the session (so the display has state ON).
mVirtualDisplayContent = new TestDisplayContent.Builder(mAtm, 500, 600).build(); mVirtualDisplayContent = new TestDisplayContent.Builder(mAtm, 500, 600).build();
mVirtualDisplayId = mVirtualDisplayContent.getDisplayId(); mVirtualDisplayId = mVirtualDisplayContent.getDisplayId();
@@ -62,6 +70,11 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
spyOn(mVirtualDisplayContent); spyOn(mVirtualDisplayContent);
mDefaultSession.setVirtualDisplayId(mVirtualDisplayId); mDefaultSession.setVirtualDisplayId(mVirtualDisplayId);
mWaitingDisplaySession.setVirtualDisplayId(mVirtualDisplayId);
mWaitingDisplaySession.setWaitingForConsent(true);
mRootTaskToken = new WindowContainer.RemoteToken(mTaskWindowContainer);
mTaskWindowContainer.mRemoteToken = mRootTaskToken;
} }
@Test @Test
@@ -92,7 +105,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
} }
@Test @Test
public void testSetContentRecordingSessionLocked_newDisplaySession_accepted() { public void testSetContentRecordingSessionLocked_newSession_accepted() {
ContentRecordingController controller = new ContentRecordingController(); ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session. // GIVEN a valid display session.
// WHEN updating the session. // WHEN updating the session.
@@ -104,15 +117,37 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
} }
@Test @Test
public void testSetContentRecordingSessionLocked_updateCurrentDisplaySession_notAccepted() { public void testSetContentRecordingSessionLocked_updateSession_noLongerWaiting_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mWaitingDisplaySession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(
mWaitingDisplaySession);
// WHEN updating the session on the same display, so no longer waiting to record.
ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
mRootTaskToken.toWindowContainerToken().asBinder());
sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
sessionUpdate.setWaitingForConsent(false);
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
// THEN the session was accepted.
assertThat(resultingSession).isEqualTo(sessionUpdate);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(sessionUpdate);
verify(mVirtualDisplayContent).updateRecording();
}
@Test
public void testSetContentRecordingSessionLocked_invalidUpdateSession_notWaiting_notAccepted() {
ContentRecordingController controller = new ContentRecordingController(); ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place. // GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm); controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession); verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
// WHEN updating the session on the same display. // WHEN updating the session on the same display.
ContentRecordingSession sessionUpdate = ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
ContentRecordingSession.createTaskSession(mock(IBinder.class)); mRootTaskToken.toWindowContainerToken().asBinder());
sessionUpdate.setVirtualDisplayId(mVirtualDisplayId); sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
controller.setContentRecordingSessionLocked(sessionUpdate, mWm); controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
@@ -123,7 +158,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
} }
@Test @Test
public void testSetContentRecordingSessionLocked_disableCurrentDisplaySession_accepted() { public void testSetContentRecordingSessionLocked_disableCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController(); ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place. // GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm); controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
@@ -141,7 +176,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
} }
@Test @Test
public void testSetContentRecordingSessionLocked_takeOverCurrentDisplaySession_accepted() { public void testSetContentRecordingSessionLocked_takeOverCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController(); ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place. // GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm); controller.setContentRecordingSessionLocked(mDefaultSession, mWm);