(5/N)[MediaProjection] Send no frames until user reviews consent

When a host app re-uses a consent token, protect the user's data
by ensuring no frames are sent out until the user has reviewed
the permission dialog.

Test: atest WmTests:ContentRecordingControllerTests
Test: atest WmTests:ContentRecorderTests
Bug: 271323540
Change-Id: I0451ac709b64414fc75f3dfbca5a3dbe9244682d
This commit is contained in:
Naomi Musgrave
2023-05-04 14:38:38 +00:00
parent af4430a740
commit d8a8b3dad1
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.
*/
private boolean mWaitingToRecord = false;
private boolean mWaitingForConsent = false;
/**
* Default instance, with recording the display.
@@ -181,7 +181,7 @@ public final class ContentRecordingSession implements Parcelable {
@RecordContent int contentToRecord,
int displayToRecord,
@Nullable IBinder tokenToRecord,
boolean waitingToRecord) {
boolean waitingForConsent) {
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
@@ -195,7 +195,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord;
this.mWaitingForConsent = waitingForConsent;
// 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.
*/
@DataClass.Generated.Member
public boolean isWaitingToRecord() {
return mWaitingToRecord;
public boolean isWaitingForConsent() {
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.
*/
@DataClass.Generated.Member
public @NonNull ContentRecordingSession setWaitingToRecord( boolean value) {
mWaitingToRecord = value;
public @NonNull ContentRecordingSession setWaitingForConsent( boolean value) {
mWaitingForConsent = value;
return this;
}
@@ -325,7 +325,7 @@ public final class ContentRecordingSession implements Parcelable {
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
"displayToRecord = " + mDisplayToRecord + ", " +
"tokenToRecord = " + mTokenToRecord + ", " +
"waitingToRecord = " + mWaitingToRecord +
"waitingForConsent = " + mWaitingForConsent +
" }";
}
@@ -346,7 +346,7 @@ public final class ContentRecordingSession implements Parcelable {
&& mContentToRecord == that.mContentToRecord
&& mDisplayToRecord == that.mDisplayToRecord
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
&& mWaitingToRecord == that.mWaitingToRecord;
&& mWaitingForConsent == that.mWaitingForConsent;
}
@Override
@@ -360,7 +360,7 @@ public final class ContentRecordingSession implements Parcelable {
_hash = 31 * _hash + mContentToRecord;
_hash = 31 * _hash + mDisplayToRecord;
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
_hash = 31 * _hash + Boolean.hashCode(mWaitingToRecord);
_hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
return _hash;
}
@@ -371,7 +371,7 @@ public final class ContentRecordingSession implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
if (mWaitingToRecord) flg |= 0x10;
if (mWaitingForConsent) flg |= 0x10;
if (mTokenToRecord != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mVirtualDisplayId);
@@ -392,7 +392,7 @@ public final class ContentRecordingSession implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
boolean waitingToRecord = (flg & 0x10) != 0;
boolean waitingForConsent = (flg & 0x10) != 0;
int virtualDisplayId = in.readInt();
int contentToRecord = in.readInt();
int displayToRecord = in.readInt();
@@ -411,7 +411,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingToRecord = waitingToRecord;
this.mWaitingForConsent = waitingForConsent;
// 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 int mDisplayToRecord;
private @Nullable IBinder mTokenToRecord;
private boolean mWaitingToRecord;
private boolean mWaitingForConsent;
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.
*/
@DataClass.Generated.Member
public @NonNull Builder setWaitingToRecord(boolean value) {
public @NonNull Builder setWaitingForConsent(boolean value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mWaitingToRecord = value;
mWaitingForConsent = value;
return this;
}
@@ -531,14 +531,14 @@ public final class ContentRecordingSession implements Parcelable {
mTokenToRecord = null;
}
if ((mBuilderFieldsSet & 0x10) == 0) {
mWaitingToRecord = false;
mWaitingForConsent = false;
}
ContentRecordingSession o = new ContentRecordingSession(
mVirtualDisplayId,
mContentToRecord,
mDisplayToRecord,
mTokenToRecord,
mWaitingToRecord);
mWaitingForConsent);
return o;
}
@@ -551,10 +551,10 @@ public final class ContentRecordingSession implements Parcelable {
}
@DataClass.Generated(
time = 1679855157534L,
time = 1683628463074L,
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 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
private void __metadata() {}

View File

@@ -1675,6 +1675,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"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": {
"message": "%s",
"level": "INFO",
@@ -2167,6 +2173,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"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": {
"message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
"level": "VERBOSE",

View File

@@ -1560,7 +1560,7 @@ public final class DisplayManagerService extends SystemService {
// VirtualDisplay has been successfully constructed.
session.setVirtualDisplayId(displayId);
// 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
// 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;
}
if (mProjectionGrant.mSession == null
|| !mProjectionGrant.mSession.isWaitingToRecord()) {
|| !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.w(TAG, "Reusing token: Ignore consent result " + consentResult
+ " if not waiting for the result.");
return;
@@ -445,7 +445,7 @@ public final class MediaProjectionManagerService extends SystemService
*/
private void setReviewedConsentSessionLocked(@Nullable ContentRecordingSession session) {
if (session != null) {
session.setWaitingToRecord(false);
session.setWaitingForConsent(false);
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
// against the current projection token before re-using the current projection.
if (mProjectionGrant == null || mProjectionGrant.mSession == null
|| !mProjectionGrant.mSession.isWaitingToRecord()) {
|| !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
+ "instance");
return null;

View File

@@ -276,6 +276,12 @@ final class ContentRecorder implements WindowContainerListener {
return;
}
if (mContentRecordingSession.isWaitingForConsent()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
+ "nothing");
return;
}
mRecordedWindowContainer = retrieveRecordedWindowContainer();
if (mRecordedWindowContainer == null) {
// 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.
* <p>Handles the following scenarios:
* <ul>
* <li>Invalid scenarios: The incoming session is malformed, or the incoming session is
* identical to the current session</li>
* <li>Invalid scenarios: The incoming session is malformed.</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>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
@@ -66,6 +66,8 @@ final class ContentRecordingController {
* begin.</li>
* <li>Stopping scenario: The incoming session is null and there is currently an ongoing
* 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>
*
* @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)) {
return;
}
// Invalid scenario: ignore identical incoming session.
final boolean hasSessionUpdatedWithConsent =
mSession != null && incomingSession != null && mSession.isWaitingForConsent()
&& !incomingSession.isWaitingForConsent();
if (ContentRecordingSession.isProjectionOnSameDisplay(mSession, incomingSession)) {
// TODO(242833866): if incoming session is no longer waiting to record, allow
// the update through.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Ignoring session on same display %d, with an existing "
+ "session %s",
incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
return;
if (hasSessionUpdatedWithConsent) {
// 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,
"Content Recording: Ignoring session on same display %d, with an existing "
+ "session %s",
incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
return;
}
}
DisplayContent incomingDisplayContent = null;
// Start scenario: recording begins immediately.
if (incomingSession != null) {
// Start scenario: recording begins immediately.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Handle incoming session on display %d, with a "
+ "pre-existing session %s", incomingSession.getVirtualDisplayId(),
@@ -106,11 +116,14 @@ final class ContentRecordingController {
return;
}
incomingDisplayContent.setContentRecordingSession(incomingSession);
// TODO(b/270118861): When user grants consent to re-use, explicitly ask ContentRecorder
// to update, since no config/display change arrives. Mark recording as black.
// Updating scenario: Explicitly ask ContentRecorder to update, since no config or
// display change will trigger an update from the DisplayContent.
if (hasSessionUpdatedWithConsent) {
incomingDisplayContent.updateRecording();
}
}
// Takeover and stopping scenario: stop recording on the pre-existing session.
if (mSession != null) {
if (mSession != null && !hasSessionUpdatedWithConsent) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Pause the recording session on display %s",
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
* 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 (!setDisplayMirroring()) {
return;

View File

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

View File

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

View File

@@ -79,6 +79,8 @@ public class ContentRecorderTests extends WindowTestsBase {
private Task mTask;
private final ContentRecordingSession mDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private ContentRecordingSession mTaskSession;
private static Point sSurfaceSize;
private ContentRecorder mContentRecorder;
@@ -120,6 +122,10 @@ public class ContentRecorderTests extends WindowTestsBase {
mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
mTaskSession.setVirtualDisplayId(displayId);
// GIVEN a session is waiting for the user to review consent.
mWaitingDisplaySession.setVirtualDisplayId(displayId);
mWaitingDisplaySession.setWaitingForConsent(true);
mConfigListener = new ConfigListener();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
mContext.getMainExecutor(), mConfigListener);
@@ -220,6 +226,18 @@ public class ContentRecorderTests extends WindowTestsBase {
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
public void testOnConfigurationChanged_neverRecording() {
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 org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
@@ -36,6 +34,8 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Tests for the {@link ContentRecordingController} class.
@@ -49,12 +49,20 @@ import org.junit.runner.RunWith;
public class ContentRecordingControllerTests extends WindowTestsBase {
private final ContentRecordingSession mDefaultSession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private final ContentRecordingSession mWaitingDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private int mVirtualDisplayId;
private DisplayContent mVirtualDisplayContent;
private WindowContainer.RemoteToken mRootTaskToken;
@Mock
private WindowContainer mTaskWindowContainer;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
// GIVEN the VirtualDisplay associated with the session (so the display has state ON).
mVirtualDisplayContent = new TestDisplayContent.Builder(mAtm, 500, 600).build();
mVirtualDisplayId = mVirtualDisplayContent.getDisplayId();
@@ -62,6 +70,11 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
spyOn(mVirtualDisplayContent);
mDefaultSession.setVirtualDisplayId(mVirtualDisplayId);
mWaitingDisplaySession.setVirtualDisplayId(mVirtualDisplayId);
mWaitingDisplaySession.setWaitingForConsent(true);
mRootTaskToken = new WindowContainer.RemoteToken(mTaskWindowContainer);
mTaskWindowContainer.mRemoteToken = mRootTaskToken;
}
@Test
@@ -92,7 +105,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
}
@Test
public void testSetContentRecordingSessionLocked_newDisplaySession_accepted() {
public void testSetContentRecordingSessionLocked_newSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session.
// WHEN updating the session.
@@ -104,15 +117,37 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
}
@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();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
// WHEN updating the session on the same display.
ContentRecordingSession sessionUpdate =
ContentRecordingSession.createTaskSession(mock(IBinder.class));
ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
mRootTaskToken.toWindowContainerToken().asBinder());
sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
@@ -123,7 +158,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
}
@Test
public void testSetContentRecordingSessionLocked_disableCurrentDisplaySession_accepted() {
public void testSetContentRecordingSessionLocked_disableCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
@@ -141,7 +176,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
}
@Test
public void testSetContentRecordingSessionLocked_takeOverCurrentDisplaySession_accepted() {
public void testSetContentRecordingSessionLocked_takeOverCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);