Report MediaProjectionTargetChanged Atom
Update ContentRecorder & MediaProjectionManagerService to log a MediaProjectionTargetChanged atom upon recording starting and further updates to windowing mode.
Bug: 304728422
Test: atest WmTests:ContentRecorderTests
Test: atest FrameworksServicesTests:MediaProjectionManagerServiceTest
Test: atest FrameworksServicesTests:MediaProjectionMetricsLoggerTest
Change-Id: I5120ba2571fb2e6e084e72c4fd079767530ccdeb
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 87be13a..dc2b056 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2653,6 +2653,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
+ "261227010": {
+ "message": "Content Recording: Unable to tell log windowing mode change: %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"269576220": {
"message": "Resuming rotation after drag",
"level": "DEBUG",
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 24efbd1..a7ec6c6 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -212,4 +212,9 @@
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
oneway void notifyAppSelectorDisplayed(int hostUid);
+
+ @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
}
diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
index 5bad067..6c74cba 100644
--- a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
+++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
@@ -21,8 +21,8 @@
/** Wrapper around {@link FrameworkStatsLog} */
public class FrameworkStatsLogWrapper {
- /** Wrapper around {@link FrameworkStatsLog#write}. */
- public void write(
+ /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionStateChanged atom. */
+ public void writeStateChanged(
int code,
int sessionId,
int state,
@@ -41,4 +41,21 @@
timeSinceLastActive,
creationSource);
}
+
+ /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionTargetChanged atom. */
+ public void writeTargetChanged(
+ int code,
+ int sessionId,
+ int targetType,
+ int hostUid,
+ int targetUid,
+ int windowingMode) {
+ FrameworkStatsLog.write(
+ code,
+ sessionId,
+ targetType,
+ hostUid,
+ targetUid,
+ windowingMode);
+ }
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 893ed61..6deda46 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -479,6 +479,18 @@
mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid);
}
+ @VisibleForTesting
+ void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode) {
+ synchronized (mLock) {
+ if (mProjectionGrant == null) {
+ Slog.i(TAG, "Cannot log MediaProjectionTargetChanged atom due to null projection");
+ } else {
+ mMediaProjectionMetricsLogger.logChangedWindowingMode(
+ contentToRecord, mProjectionGrant.uid, targetUid, windowingMode);
+ }
+ }
+ }
+
/**
* Handles result of dialog shown from
* {@link BinderService#buildReviewGrantedConsentIntentLocked()}.
@@ -905,6 +917,20 @@
}
@Override // Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void notifyWindowingModeChanged(
+ int contentToRecord, int targetUid, int windowingMode) {
+ notifyWindowingModeChanged_enforcePermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.notifyWindowingModeChanged(
+ contentToRecord, targetUid, windowingMode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index d7fefeb..be2a25a 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -16,16 +16,32 @@
package com.android.server.media.projection;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.util.Log;
+import android.view.ContentRecordingSession.RecordContent;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import java.time.Duration;
@@ -91,7 +107,7 @@
durationSinceLastActiveSession == null
? TIME_SINCE_LAST_ACTIVE_UNKNOWN
: (int) durationSinceLastActiveSession.toSeconds();
- write(
+ writeStateChanged(
mSessionIdGenerator.createAndGetNewSessionId(),
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
hostUid,
@@ -102,13 +118,13 @@
/**
* Logs that the user entered the setup flow and permission dialog is displayed. This state is
- * not sent when the permission is already granted and we skipped showing the permission dialog.
+ * not sent when the permission is already granted, and we skipped showing the permission dialog.
*
* @param hostUid UID of the package that initiates MediaProjection.
*/
public void logPermissionRequestDisplayed(int hostUid) {
Log.d(TAG, "logPermissionRequestDisplayed");
- write(
+ writeStateChanged(
mSessionIdGenerator.getCurrentSessionId(),
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED,
hostUid,
@@ -123,7 +139,7 @@
* @param hostUid UID of the package that initiates MediaProjection.
*/
public void logProjectionPermissionRequestCancelled(int hostUid) {
- write(
+ writeStateChanged(
mSessionIdGenerator.getCurrentSessionId(),
FrameworkStatsLog
.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
@@ -141,7 +157,7 @@
*/
public void logAppSelectorDisplayed(int hostUid) {
Log.d(TAG, "logAppSelectorDisplayed");
- write(
+ writeStateChanged(
mSessionIdGenerator.getCurrentSessionId(),
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED,
hostUid,
@@ -158,7 +174,7 @@
*/
public void logInProgress(int hostUid, int targetUid) {
Log.d(TAG, "logInProgress");
- write(
+ writeStateChanged(
mSessionIdGenerator.getCurrentSessionId(),
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS,
hostUid,
@@ -168,6 +184,54 @@
}
/**
+ * Logs that the windowing mode of a projection has changed.
+ *
+ * @param contentToRecord ContentRecordingSession.RecordContent indicating whether it is a
+ * task capture or display capture - gets converted to the corresponding
+ * TargetType before being logged.
+ * @param hostUid UID of the package that initiates MediaProjection.
+ * @param targetUid UID of the package that is captured if selected.
+ * @param windowingMode Updated WindowConfiguration.WindowingMode of the captured region - gets
+ * converted to the corresponding TargetWindowingMode before being logged.
+ */
+ public void logChangedWindowingMode(
+ int contentToRecord, int hostUid, int targetUid, int windowingMode) {
+ Log.d(TAG, "logChangedWindowingMode");
+ writeTargetChanged(
+ mSessionIdGenerator.getCurrentSessionId(),
+ contentToRecordToTargetType(contentToRecord),
+ hostUid,
+ targetUid,
+ windowingModeToTargetWindowingMode(windowingMode));
+
+ }
+
+ @VisibleForTesting
+ public int contentToRecordToTargetType(@RecordContent int recordContentType) {
+ return switch (recordContentType) {
+ case RECORD_CONTENT_DISPLAY ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+ case RECORD_CONTENT_TASK ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+ default -> MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+ };
+ }
+
+ @VisibleForTesting
+ public int windowingModeToTargetWindowingMode(@WindowingMode int windowingMode) {
+ return switch (windowingMode) {
+ case WINDOWING_MODE_FULLSCREEN ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+ case WINDOWING_MODE_FREEFORM ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+ case WINDOWING_MODE_MULTI_WINDOW ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+ default ->
+ MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
+ };
+ }
+
+ /**
* Logs that the capturing stopped, either normally or because of error.
*
* @param hostUid UID of the package that initiates MediaProjection.
@@ -178,7 +242,7 @@
mPreviousState
== MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress);
- write(
+ writeStateChanged(
mSessionIdGenerator.getCurrentSessionId(),
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED,
hostUid,
@@ -191,14 +255,31 @@
}
}
- private void write(
+ public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
+ writeStateChanged(hostUid, state, sessionCreationSource);
+ }
+
+ private void writeStateChanged(int hostUid, int state, int sessionCreationSource) {
+ mFrameworkStatsLogWrapper.writeStateChanged(
+ /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+ /* session_id */ 123,
+ /* state */ state,
+ /* previous_state */ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
+ /* host_uid */ hostUid,
+ /* target_uid */ -1,
+ /* time_since_last_active */ 0,
+ /* creation_source */ sessionCreationSource);
+ }
+
+ private void writeStateChanged(
int sessionId,
int state,
int hostUid,
int targetUid,
int timeSinceLastActive,
int creationSource) {
- mFrameworkStatsLogWrapper.write(
+ mFrameworkStatsLogWrapper.writeStateChanged(
/* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
sessionId,
state,
@@ -209,4 +290,19 @@
creationSource);
mPreviousState = state;
}
+
+ private void writeTargetChanged(
+ int sessionId,
+ int targetType,
+ int hostUid,
+ int targetUid,
+ int targetWindowingMode) {
+ mFrameworkStatsLogWrapper.writeTargetChanged(
+ /* code */ FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED,
+ sessionId,
+ targetType,
+ hostUid,
+ targetUid,
+ targetWindowingMode);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 022ef61..8717098 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.MEDIA_PROJECTION_SERVICE;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
@@ -100,6 +101,8 @@
@Configuration.Orientation
private int mLastOrientation = ORIENTATION_UNDEFINED;
+ private int mLastWindowingMode = WINDOWING_MODE_UNDEFINED;
+
private final boolean mCorrectForAnisotropicPixels;
ContentRecorder(@NonNull DisplayContent displayContent) {
@@ -156,7 +159,8 @@
* Handle a configuration change on the display content, and resize recording if needed.
* @param lastOrientation the prior orientation of the configuration
*/
- void onConfigurationChanged(@Configuration.Orientation int lastOrientation) {
+ void onConfigurationChanged(
+ @Configuration.Orientation int lastOrientation, int lastWindowingMode) {
// Update surface for MediaProjection, if this DisplayContent is being used for recording.
if (!isCurrentlyRecording() || mLastRecordedBounds == null) {
return;
@@ -185,6 +189,16 @@
}
}
+ // Record updated windowing mode, if necessary.
+ int recordedContentWindowingMode = mRecordedWindowContainer.getWindowingMode();
+ if (lastWindowingMode != recordedContentWindowingMode) {
+ mMediaProjectionManager.notifyWindowingModeChanged(
+ mContentRecordingSession.getContentToRecord(),
+ mContentRecordingSession.getTargetUid(),
+ recordedContentWindowingMode
+ );
+ }
+
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Display %d was already recording, so apply "
+ "transformations if necessary",
@@ -327,8 +341,10 @@
return;
}
+ final int contentToRecord = mContentRecordingSession.getContentToRecord();
+
// TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are inaccurate.
- if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+ if (contentToRecord == RECORD_CONTENT_TASK) {
if (mRecordedWindowContainer.asTask().inPinnedWindowingMode()) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Display %d should start recording, but "
@@ -375,7 +391,7 @@
// Notify the client about the visibility of the mirrored region, now that we have begun
// capture.
- if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+ if (contentToRecord == RECORD_CONTENT_TASK) {
mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
mRecordedWindowContainer.asTask().isVisibleRequested());
} else {
@@ -385,6 +401,11 @@
currentDisplayState != DISPLAY_STATE_OFF);
}
+ // Record initial windowing mode after recording starts.
+ mMediaProjectionManager.notifyWindowingModeChanged(
+ contentToRecord, mContentRecordingSession.getTargetUid(),
+ mRecordedWindowContainer.getWindowConfiguration().getWindowingMode());
+
// No need to clean up. In SurfaceFlinger, parents hold references to their children. The
// mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is
// holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up
@@ -617,8 +638,9 @@
Configuration mergedOverrideConfiguration) {
WindowContainerListener.super.onMergedOverrideConfigurationChanged(
mergedOverrideConfiguration);
- onConfigurationChanged(mLastOrientation);
+ onConfigurationChanged(mLastOrientation, mLastWindowingMode);
mLastOrientation = mergedOverrideConfiguration.orientation;
+ mLastWindowingMode = mergedOverrideConfiguration.windowConfiguration.getWindowingMode();
}
// WindowContainerListener
@@ -635,6 +657,7 @@
void stopActiveProjection();
void notifyActiveProjectionCapturedContentResized(int width, int height);
void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible);
+ void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
}
private static final class RemoteMediaProjectionManagerWrapper implements
@@ -700,6 +723,22 @@
}
}
+ @Override
+ public void notifyWindowingModeChanged(int contentToRecord, int targetUid,
+ int windowingMode) {
+ fetchMediaProjectionManager();
+ if (mIMediaProjectionManager == null) {
+ return;
+ }
+ try {
+ mIMediaProjectionManager.notifyWindowingModeChanged(
+ contentToRecord, targetUid, windowingMode);
+ } catch (RemoteException e) {
+ ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Unable to tell log windowing mode change: %s", e);
+ }
+ }
+
private void fetchMediaProjectionManager() {
if (mIMediaProjectionManager != null) {
return;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 576e8a4..c716879 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2757,6 +2757,7 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int lastOrientation = getConfiguration().orientation;
+ final int lastWindowingMode = getWindowingMode();
super.onConfigurationChanged(newParentConfig);
if (mDisplayPolicy != null) {
mDisplayPolicy.onConfigurationChanged();
@@ -2768,7 +2769,7 @@
// Update surface for MediaProjection, if this DisplayContent is being used for recording.
if (mContentRecorder != null) {
- mContentRecorder.onConfigurationChanged(lastOrientation);
+ mContentRecorder.onConfigurationChanged(lastOrientation, lastWindowingMode);
}
if (lastOrientation != getConfiguration().orientation) {
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index ece3dfe..097cc51 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -17,12 +17,17 @@
package com.android.server.media.projection;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.media.projection.MediaProjectionManager.TYPE_MIRRORING;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
+import static android.view.ContentRecordingSession.TARGET_UID_FULL_SCREEN;
+import static android.view.ContentRecordingSession.TARGET_UID_UNKNOWN;
+import static android.view.ContentRecordingSession.createDisplaySession;
+import static android.view.ContentRecordingSession.createTaskSession;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -62,6 +67,7 @@
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
+import android.view.ContentRecordingSession.RecordContent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
@@ -99,7 +105,7 @@
private final ApplicationInfo mAppInfo = new ApplicationInfo();
private final TestLooper mTestLooper = new TestLooper();
private static final ContentRecordingSession DISPLAY_SESSION =
- ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+ createDisplaySession(DEFAULT_DISPLAY);
// Callback registered by an app on a MediaProjection instance.
private final FakeIMediaProjectionCallback mIMediaProjectionCallback =
new FakeIMediaProjectionCallback();
@@ -142,7 +148,7 @@
private MediaProjectionManagerService mService;
private OffsettableClock mClock;
private ContentRecordingSession mWaitingDisplaySession =
- ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+ createDisplaySession(DEFAULT_DISPLAY);
@Mock
private ActivityManagerInternal mAmInternal;
@@ -333,7 +339,7 @@
projection.stop();
verify(mMediaProjectionMetricsLogger)
- .logStopped(UID, ContentRecordingSession.TARGET_UID_UNKNOWN);
+ .logStopped(UID, TARGET_UID_UNKNOWN);
}
@Test
@@ -351,7 +357,7 @@
projection.stop();
verify(mMediaProjectionMetricsLogger)
- .logStopped(UID, ContentRecordingSession.TARGET_UID_FULL_SCREEN);
+ .logStopped(UID, TARGET_UID_FULL_SCREEN);
}
@Test
@@ -366,7 +372,7 @@
.when(mWindowManagerInternal)
.setContentRecordingSession(any(ContentRecordingSession.class));
ContentRecordingSession taskSession =
- ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid);
+ createTaskSession(mock(IBinder.class), targetUid);
service.setContentRecordingSession(taskSession);
projection.stop();
@@ -695,6 +701,26 @@
verify(mMediaProjectionMetricsLogger).logAppSelectorDisplayed(hostUid);
}
+ @Test
+ public void notifyWindowingModeChanged_forwardsToLogger() throws Exception {
+ int targetUid = 123;
+ mService =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+ ContentRecordingSession taskSession =
+ createTaskSession(mock(IBinder.class), targetUid);
+ mService.setContentRecordingSession(taskSession);
+
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ mService.notifyWindowingModeChanged(
+ RECORD_CONTENT_TASK, targetUid, WINDOWING_MODE_MULTI_WINDOW);
+
+ verify(mMediaProjectionMetricsLogger).logChangedWindowingMode(RECORD_CONTENT_TASK,
+ projection.uid, targetUid, WINDOWING_MODE_MULTI_WINDOW);
+ }
+
/**
* Executes and validates scenario where the consent result indicates the projection ends.
*/
@@ -755,7 +781,7 @@
*/
private void testSetUserReviewGrantedConsentResult_startedSession(
@ReviewGrantedConsentResult int consentResult,
- @ContentRecordingSession.RecordContent int recordedContent)
+ @RecordContent int recordedContent)
throws NameNotFoundException {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
projection.setLaunchCookie(mock(IBinder.class));
@@ -777,7 +803,7 @@
*/
private void testSetUserReviewGrantedConsentResult_failedToStartSession(
@ReviewGrantedConsentResult int consentResult,
- @ContentRecordingSession.RecordContent int recordedContent)
+ @RecordContent int recordedContent)
throws NameNotFoundException {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
projection.start(mIMediaProjectionCallback);
@@ -889,7 +915,7 @@
int targetUid = 123455;
ContentRecordingSession taskSession =
- ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid);
+ createTaskSession(mock(IBinder.class), targetUid);
service.setContentRecordingSession(taskSession);
verify(mMediaProjectionMetricsLogger).logInProgress(projection.uid, targetUid);
@@ -970,7 +996,7 @@
verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
}
- private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) {
+ private void verifySetSessionWithContent(@RecordContent int content) {
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
mSessionCaptor.capture());
assertThat(mSessionCaptor.getValue()).isNotNull();
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index ad1cd6e..72ce9fe 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -16,6 +16,14 @@
package com.android.server.media.projection;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
@@ -24,6 +32,13 @@
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -38,10 +53,14 @@
import com.android.internal.util.FrameworkStatsLog;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.time.Duration;
@@ -60,12 +79,18 @@
private static final int TEST_TARGET_UID = 456;
private static final int TEST_CREATION_SOURCE = 789;
+ private static final int TEST_WINDOWING_MODE = 987;
+ private static final int TEST_CONTENT_TO_RECORD = 654;
+
@Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper;
@Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator;
@Mock private MediaProjectionTimestampStore mTimestampStore;
private MediaProjectionMetricsLogger mLogger;
+ @Rule
+ public Expect mExpect = Expect.create();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -93,7 +118,7 @@
public void logInitiated_logsHostUid() {
mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
@@ -107,7 +132,7 @@
public void logInitiated_logsUnknownTargetUid() {
mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
- verifyTargetUidLogged(-2);
+ verifyStageChangedTargetUidLogged(-2);
}
@Test
@@ -178,14 +203,14 @@
public void logStopped_logsHostUid() {
mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
public void logStopped_logsTargetUid() {
mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
- verifyTargetUidLogged(TEST_TARGET_UID);
+ verifyStageChangedTargetUidLogged(TEST_TARGET_UID);
}
@Test
@@ -263,14 +288,14 @@
public void logInProgress_logsHostUid() {
mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
public void logInProgress_logsTargetUid() {
mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
- verifyTargetUidLogged(TEST_TARGET_UID);
+ verifyStageChangedTargetUidLogged(TEST_TARGET_UID);
}
@Test
@@ -336,14 +361,14 @@
public void logPermissionRequestDisplayed_logsHostUid() {
mLogger.logPermissionRequestDisplayed(TEST_HOST_UID);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
public void logPermissionRequestDisplayed_logsUnknownTargetUid() {
mLogger.logPermissionRequestDisplayed(TEST_HOST_UID);
- verifyTargetUidLogged(-2);
+ verifyStageChangedTargetUidLogged(-2);
}
@Test
@@ -409,14 +434,14 @@
public void logAppSelectorDisplayed_logsHostUid() {
mLogger.logAppSelectorDisplayed(TEST_HOST_UID);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
public void logAppSelectorDisplayed_logsUnknownTargetUid() {
mLogger.logAppSelectorDisplayed(TEST_HOST_UID);
- verifyTargetUidLogged(-2);
+ verifyStageChangedTargetUidLogged(-2);
}
@Test
@@ -492,14 +517,14 @@
public void logProjectionPermissionRequestCancelled_logsHostUid() {
mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
- verifyHostUidLogged(TEST_HOST_UID);
+ verifyStateChangedHostUidLogged(TEST_HOST_UID);
}
@Test
public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() {
mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
- verifyTargetUidLogged(-2);
+ verifyStageChangedTargetUidLogged(-2);
}
@Test
@@ -510,9 +535,88 @@
MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
}
+ @Test
+ public void logWindowingModeChanged_logsTargetChangedAtomId() {
+ mLogger.logChangedWindowingMode(
+ TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+
+ verifyTargetChangedAtomIdLogged();
+ }
+
+ @Test
+ public void logWindowingModeChanged_logsTargetType() {
+ MediaProjectionMetricsLogger logger = Mockito.spy(mLogger);
+ final int testTargetType = 111;
+ when(logger.contentToRecordToTargetType(TEST_CONTENT_TO_RECORD)).thenReturn(testTargetType);
+ logger.logChangedWindowingMode(
+ TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+ verifyTargetTypeLogged(testTargetType);
+ }
+
+ @Test
+ public void logWindowingModeChanged_logsHostUid() {
+ mLogger.logChangedWindowingMode(
+ TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+ verifyTargetChangedHostUidLogged(TEST_HOST_UID);
+ }
+
+ @Test
+ public void logWindowingModeChanged_logsTargetUid() {
+ mLogger.logChangedWindowingMode(
+ TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+ verifyTargetChangedTargetUidLogged(TEST_TARGET_UID);
+ }
+
+ @Test
+ public void logWindowingModeChanged_logsTargetWindowingMode() {
+ MediaProjectionMetricsLogger logger = Mockito.spy(mLogger);
+ final int testTargetWindowingMode = 222;
+ when(logger.windowingModeToTargetWindowingMode(TEST_WINDOWING_MODE))
+ .thenReturn(testTargetWindowingMode);
+ logger.logChangedWindowingMode(
+ TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+ verifyWindowingModeLogged(testTargetWindowingMode);
+ }
+
+ @Test
+ public void testContentToRecordToTargetType() {
+ mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_DISPLAY))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY);
+
+ mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_TASK))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK);
+
+ mExpect.that(mLogger.contentToRecordToTargetType(2))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+
+ mExpect.that(mLogger.contentToRecordToTargetType(-1))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+
+ mExpect.that(mLogger.contentToRecordToTargetType(100))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+ }
+
+ @Test
+ public void testWindowingModeToTargetWindowingMode() {
+ mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FULLSCREEN))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN);
+
+ mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_MULTI_WINDOW))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN);
+
+ mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FREEFORM))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM);
+
+ mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_PINNED))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN);
+
+ mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_UNDEFINED))
+ .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN);
+ }
+
private void verifyStateChangedAtomIdLogged() {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ eq(FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -525,7 +629,7 @@
private void verifyStateLogged(int state) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
eq(state),
@@ -536,9 +640,9 @@
/* creationSource= */ anyInt());
}
- private void verifyHostUidLogged(int hostUid) {
+ private void verifyStateChangedHostUidLogged(int hostUid) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -551,7 +655,7 @@
private void verifyCreationSourceLogged(int creationSource) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -562,9 +666,9 @@
eq(creationSource));
}
- private void verifyTargetUidLogged(int targetUid) {
+ private void verifyStageChangedTargetUidLogged(int targetUid) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -577,7 +681,7 @@
private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -590,7 +694,7 @@
private void verifySessionIdLogged(int newSessionId) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ eq(newSessionId),
/* state= */ anyInt(),
@@ -603,7 +707,7 @@
private void verifyPreviousStateLogged(int previousState) {
verify(mFrameworkStatsLogWrapper)
- .write(
+ .writeStateChanged(
/* code= */ anyInt(),
/* sessionId= */ anyInt(),
/* state= */ anyInt(),
@@ -613,4 +717,59 @@
/* timeSinceLastActive= */ anyInt(),
/* creationSource= */ anyInt());
}
+
+ private void verifyTargetChangedAtomIdLogged() {
+ verify(mFrameworkStatsLogWrapper)
+ .writeTargetChanged(
+ eq(FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED),
+ /* sessionId= */ anyInt(),
+ /* targetType= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* targetWindowingMode= */ anyInt());
+ }
+
+ private void verifyTargetTypeLogged(int targetType) {
+ verify(mFrameworkStatsLogWrapper)
+ .writeTargetChanged(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ eq(targetType),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ /* targetWindowingMode= */ anyInt());
+ }
+
+ private void verifyTargetChangedHostUidLogged(int hostUid) {
+ verify(mFrameworkStatsLogWrapper)
+ .writeTargetChanged(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* targetType= */ anyInt(),
+ eq(hostUid),
+ /* targetUid= */ anyInt(),
+ /* targetWindowingMode= */ anyInt());
+ }
+
+ private void verifyTargetChangedTargetUidLogged(int targetUid) {
+ verify(mFrameworkStatsLogWrapper)
+ .writeTargetChanged(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* targetType= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ eq(targetUid),
+ /* targetWindowingMode= */ anyInt());
+ }
+
+ private void verifyWindowingModeLogged(int targetWindowingMode) {
+ verify(mFrameworkStatsLogWrapper)
+ .writeTargetChanged(
+ /* code= */ anyInt(),
+ /* sessionId= */ anyInt(),
+ /* targetType= */ anyInt(),
+ /* hostUid= */ anyInt(),
+ /* targetUid= */ anyInt(),
+ eq(targetWindowingMode));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 78566fb..887e5ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -42,6 +42,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
@@ -232,7 +233,7 @@
@Test
public void testOnConfigurationChanged_neverRecording() {
defaultInit();
- mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
+ mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
verify(mTransaction, never()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
@@ -248,7 +249,7 @@
@Configuration.Orientation final int lastOrientation =
mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT
? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
- mContentRecorder.onConfigurationChanged(lastOrientation);
+ mContentRecorder.onConfigurationChanged(lastOrientation, WINDOWING_MODE_FULLSCREEN);
verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
anyFloat());
@@ -266,7 +267,8 @@
// The user rotates the device, so the host app resizes the virtual display for the capture.
resizeDisplay(mDisplayContent, newWidth, mSurfaceSize.y);
resizeDisplay(mVirtualDisplayContent, newWidth, mSurfaceSize.y);
- mContentRecorder.onConfigurationChanged(mDisplayContent.getConfiguration().orientation);
+ mContentRecorder.onConfigurationChanged(
+ mDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
anyFloat());
@@ -283,7 +285,7 @@
// Change a value that we shouldn't rely upon; it has the wrong type.
mVirtualDisplayContent.setOverrideOrientation(SCREEN_ORIENTATION_FULL_SENSOR);
mContentRecorder.onConfigurationChanged(
- mVirtualDisplayContent.getConfiguration().orientation);
+ mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
// No resize is issued, only the initial transformations when we started recording.
verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -307,7 +309,7 @@
doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
anyInt());
mContentRecorder.onConfigurationChanged(
- mVirtualDisplayContent.getConfiguration().orientation);
+ mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
// No resize is issued, only the initial transformations when we started recording.
verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -379,6 +381,55 @@
}
@Test
+ public void testTaskWindowingModeChanged_changeWindowMode_notifyWindowModeChanged() {
+ defaultInit();
+ // WHEN a recording is ongoing.
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ // THEN the windowing mode change callback is notified.
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+ mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN);
+
+ // WHEN a configuration change arrives, and the task is now multi-window mode.
+ mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ Configuration configuration = mTask.getConfiguration();
+ mTask.onConfigurationChanged(configuration);
+
+ // THEN windowing mode change callback is notified again.
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+ mTaskSession.getTargetUid(), WINDOWING_MODE_MULTI_WINDOW);
+ }
+
+ @Test
+ public void testTaskWindowingModeChanged_sameWindowMode_notifyWindowModeChanged() {
+ defaultInit();
+ // WHEN a recording is ongoing.
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+ // THEN the windowing mode change callback is notified.
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+ mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN);
+
+ // WHEN a configuration change arrives, and the task is STILL fullscreen.
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ Configuration configuration = mTask.getConfiguration();
+ mTask.onConfigurationChanged(configuration);
+
+ // THEN the windowing mode change callback is NOT called notified again.
+ verify(mMediaProjectionManagerWrapper, times(1))
+ .notifyWindowingModeChanged(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
public void testTaskWindowingModeChanged_pip_stopsRecording() {
defaultInit();
// WHEN a recording is ongoing.
@@ -421,9 +472,12 @@
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
- // THEN the visibility change callback is notified.
+ // THEN the visibility change & windowing mode change callbacks are notified.
verify(mMediaProjectionManagerWrapper)
.notifyActiveProjectionCapturedContentVisibilityChanged(true);
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+ mTaskSession.getTargetUid(), mRootWindowContainer.getWindowingMode());
}
@Test
@@ -434,9 +488,12 @@
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
- // THEN the visibility change callback is notified.
+ // THEN the visibility change & windowing mode change callbacks are notified.
verify(mMediaProjectionManagerWrapper)
.notifyActiveProjectionCapturedContentVisibilityChanged(true);
+ verify(mMediaProjectionManagerWrapper)
+ .notifyWindowingModeChanged(mDisplaySession.getContentToRecord(),
+ mDisplaySession.getTargetUid(), mRootWindowContainer.getWindowingMode());
}
@Test