Loading data/etc/services.core.protolog.json +12 −0 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, "-1018968224": { "message": "Recorded task is removed, so stop recording on display %d", "level": "VERBOSE", "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, "-1016578046": { "message": "Moving to %s Relaunching %s callers=%s", "level": "INFO", Loading Loading @@ -2251,6 +2257,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, "96494268": { "message": "Stop MediaProjection on virtual display %d", "level": "VERBOSE", "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, "100936473": { "message": "Wallpaper animation!", "level": "VERBOSE", Loading services/core/java/com/android/server/display/DisplayManagerService.java +0 −2 Original line number Diff line number Diff line Loading @@ -70,8 +70,6 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +9 −4 Original line number Diff line number Diff line Loading @@ -335,7 +335,7 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public void stopActiveProjection() { if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " + "projection callbacks"); Loading Loading @@ -393,9 +393,14 @@ public final class MediaProjectionManagerService extends SystemService if (!isValidMediaProjection(projection)) { throw new SecurityException("Invalid media projection"); } LocalServices.getService( if (!LocalServices.getService( WindowManagerInternal.class).setContentRecordingSession( incomingSession); incomingSession)) { // Unable to start mirroring, so tear down this projection. if (mProjectionGrant != null) { mProjectionGrant.stop(); } } } } finally { Binder.restoreCallingIdentity(origId); Loading services/core/java/com/android/server/wm/ContentRecorder.java +81 −22 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; Loading @@ -26,6 +27,7 @@ import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.media.projection.MediaProjectionManager; import android.os.IBinder; import android.provider.DeviceConfig; import android.view.ContentRecordingSession; Loading @@ -38,7 +40,7 @@ import com.android.internal.protolog.common.ProtoLog; /** * Manages content recording for a particular {@link DisplayContent}. */ final class ContentRecorder { final class ContentRecorder implements WindowContainerListener { /** * The key for accessing the device config that controls if task recording is supported. Loading @@ -51,6 +53,8 @@ final class ContentRecorder { @NonNull private final DisplayContent mDisplayContent; @Nullable private final MediaProjectionManagerWrapper mMediaProjectionManager; /** * The session for content recording, or null if this DisplayContent is not being used for * recording. Loading @@ -73,8 +77,26 @@ final class ContentRecorder { */ @Nullable private Rect mLastRecordedBounds = null; /** * The last configuration orientation. */ private int mLastOrientation = ORIENTATION_UNDEFINED; ContentRecorder(@NonNull DisplayContent displayContent) { this(displayContent, () -> { MediaProjectionManager mpm = displayContent.mWmService.mContext.getSystemService( MediaProjectionManager.class); if (mpm != null) { mpm.stopActiveProjection(); } }); } @VisibleForTesting ContentRecorder(@NonNull DisplayContent displayContent, @NonNull MediaProjectionManagerWrapper mediaProjectionManager) { mDisplayContent = displayContent; mMediaProjectionManager = mediaProjectionManager; } /** Loading @@ -95,7 +117,7 @@ final class ContentRecorder { } /** * Start recording if this DisplayContent no longer has content. Stop recording if it now * Start recording if this DisplayContent no longer has content. Pause recording if it now * has content or the display is not on. */ @VisibleForTesting void updateRecording() { Loading Loading @@ -187,7 +209,7 @@ final class ContentRecorder { /** * Stops recording on this DisplayContent, and updates the session details. */ void remove() { void stopRecording() { if (mRecordedSurface != null) { // Do not wait for the mirrored surface to be garbage collected, but clean up // immediately. Loading @@ -195,7 +217,20 @@ final class ContentRecorder { mRecordedSurface = null; clearContentRecordingSession(); // Do not need to force remove the VirtualDisplay; this is handled by the media // projection service. // projection service when the display is removed. } } /** * Ensure recording does not fall back to the display stack; ensure the recording is stopped * and the client notified by tearing down the virtual display. */ void stopMediaProjection() { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Stop MediaProjection on virtual display %d", mDisplayContent.getDisplayId()); if (mMediaProjectionManager != null) { mMediaProjectionManager.stopActiveProjection(); } } Loading Loading @@ -326,6 +361,8 @@ final class ContentRecorder { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Unable to retrieve task to start recording for " + "display %d", mDisplayContent.getDisplayId()); } else { taskToRecord.registerWindowContainerListener(this); } return taskToRecord; default: Loading @@ -342,9 +379,9 @@ final class ContentRecorder { /** * Exit this recording session. * <p> * If this is a task session, tear down the recording entirely. Do not fall back * to recording the entire display on the display stack; this would surprise the user * given they selected task capture. * If this is a task session, stop the recording entirely, including the MediaProjection. * Do not fall back to recording the entire display on the display stack; this would surprise * the user given they selected task capture. * </p><p> * If this is a display session, just stop recording by layer mirroring. Fall back to recording * from the display stack. Loading @@ -353,25 +390,14 @@ final class ContentRecorder { private void handleStartRecordingFailed() { final boolean shouldExitTaskRecording = mContentRecordingSession != null && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK; if (shouldExitTaskRecording) { // Clean up the cached session first, since tearing down the display will generate // display // events which will trickle back to here. clearContentRecordingSession(); tearDownVirtualDisplay(); } else { clearContentRecordingSession(); if (shouldExitTaskRecording) { // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. stopMediaProjection(); } } /** * Ensure recording does not fall back to the display stack; ensure the recording is stopped * and the client notified by tearing down the virtual display. */ private void tearDownVirtualDisplay() { // TODO(b/219761722) Clean up the VirtualDisplay if task mirroring fails } /** * Apply transformations to the mirrored surface to ensure the captured contents are scaled to * fit and centred in the output surface. Loading Loading @@ -442,4 +468,37 @@ final class ContentRecorder { } return surfaceSize; } // WindowContainerListener @Override public void onRemoved() { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Recorded task is removed, so stop recording on display %d", mDisplayContent.getDisplayId()); Task recordedTask = mRecordedWindowContainer.asTask(); if (recordedTask == null || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) { return; } recordedTask.unregisterWindowContainerListener(this); // Stop mirroring and teardown. clearContentRecordingSession(); // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. stopMediaProjection(); } // WindowContainerListener @Override public void onMergedOverrideConfigurationChanged( Configuration mergedOverrideConfiguration) { WindowContainerListener.super.onMergedOverrideConfigurationChanged( mergedOverrideConfiguration); onConfigurationChanged(mLastOrientation); mLastOrientation = mergedOverrideConfiguration.orientation; } @VisibleForTesting interface MediaProjectionManagerWrapper { void stopActiveProjection(); } } services/core/java/com/android/server/wm/ContentRecordingController.java +4 −6 Original line number Diff line number Diff line Loading @@ -56,14 +56,13 @@ final class ContentRecordingController { * Updates the current recording session. If a new display is taking over recording, then * stops the prior display from recording. * * @param incomingSession the new recording session. Should either be {@code null}, to stop * the current session, or a session on a new/different display than the * current session. * @param incomingSession the new recording session. Should either have a {@code null} token, to * stop the current session, or a session on a new/different display * than the current session. * @param wmService the window manager service */ void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession, @NonNull WindowManagerService wmService) { // TODO(b/219761722) handle a null session arriving due to task setup failing if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession) || ContentRecordingSession.isSameDisplay(mSession, incomingSession))) { // Ignore an invalid session, or a session for the same display as currently recording. Loading @@ -82,8 +81,7 @@ final class ContentRecordingController { } if (mSession != null) { // Update the pre-existing display about the new session. ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Pause the recording session on display %s", ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Pause the recording session on display %s", mDisplayContent.getDisplayId()); mDisplayContent.pauseRecording(); mDisplayContent.setContentRecordingSession(null); Loading Loading
data/etc/services.core.protolog.json +12 −0 Original line number Diff line number Diff line Loading @@ -1105,6 +1105,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, "-1018968224": { "message": "Recorded task is removed, so stop recording on display %d", "level": "VERBOSE", "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, "-1016578046": { "message": "Moving to %s Relaunching %s callers=%s", "level": "INFO", Loading Loading @@ -2251,6 +2257,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, "96494268": { "message": "Stop MediaProjection on virtual display %d", "level": "VERBOSE", "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, "100936473": { "message": "Wallpaper animation!", "level": "VERBOSE", Loading
services/core/java/com/android/server/display/DisplayManagerService.java +0 −2 Original line number Diff line number Diff line Loading @@ -70,8 +70,6 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; Loading
services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +9 −4 Original line number Diff line number Diff line Loading @@ -335,7 +335,7 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public void stopActiveProjection() { if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add " + "projection callbacks"); Loading Loading @@ -393,9 +393,14 @@ public final class MediaProjectionManagerService extends SystemService if (!isValidMediaProjection(projection)) { throw new SecurityException("Invalid media projection"); } LocalServices.getService( if (!LocalServices.getService( WindowManagerInternal.class).setContentRecordingSession( incomingSession); incomingSession)) { // Unable to start mirroring, so tear down this projection. if (mProjectionGrant != null) { mProjectionGrant.stop(); } } } } finally { Binder.restoreCallingIdentity(origId); Loading
services/core/java/com/android/server/wm/ContentRecorder.java +81 −22 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; Loading @@ -26,6 +27,7 @@ import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.media.projection.MediaProjectionManager; import android.os.IBinder; import android.provider.DeviceConfig; import android.view.ContentRecordingSession; Loading @@ -38,7 +40,7 @@ import com.android.internal.protolog.common.ProtoLog; /** * Manages content recording for a particular {@link DisplayContent}. */ final class ContentRecorder { final class ContentRecorder implements WindowContainerListener { /** * The key for accessing the device config that controls if task recording is supported. Loading @@ -51,6 +53,8 @@ final class ContentRecorder { @NonNull private final DisplayContent mDisplayContent; @Nullable private final MediaProjectionManagerWrapper mMediaProjectionManager; /** * The session for content recording, or null if this DisplayContent is not being used for * recording. Loading @@ -73,8 +77,26 @@ final class ContentRecorder { */ @Nullable private Rect mLastRecordedBounds = null; /** * The last configuration orientation. */ private int mLastOrientation = ORIENTATION_UNDEFINED; ContentRecorder(@NonNull DisplayContent displayContent) { this(displayContent, () -> { MediaProjectionManager mpm = displayContent.mWmService.mContext.getSystemService( MediaProjectionManager.class); if (mpm != null) { mpm.stopActiveProjection(); } }); } @VisibleForTesting ContentRecorder(@NonNull DisplayContent displayContent, @NonNull MediaProjectionManagerWrapper mediaProjectionManager) { mDisplayContent = displayContent; mMediaProjectionManager = mediaProjectionManager; } /** Loading @@ -95,7 +117,7 @@ final class ContentRecorder { } /** * Start recording if this DisplayContent no longer has content. Stop recording if it now * Start recording if this DisplayContent no longer has content. Pause recording if it now * has content or the display is not on. */ @VisibleForTesting void updateRecording() { Loading Loading @@ -187,7 +209,7 @@ final class ContentRecorder { /** * Stops recording on this DisplayContent, and updates the session details. */ void remove() { void stopRecording() { if (mRecordedSurface != null) { // Do not wait for the mirrored surface to be garbage collected, but clean up // immediately. Loading @@ -195,7 +217,20 @@ final class ContentRecorder { mRecordedSurface = null; clearContentRecordingSession(); // Do not need to force remove the VirtualDisplay; this is handled by the media // projection service. // projection service when the display is removed. } } /** * Ensure recording does not fall back to the display stack; ensure the recording is stopped * and the client notified by tearing down the virtual display. */ void stopMediaProjection() { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Stop MediaProjection on virtual display %d", mDisplayContent.getDisplayId()); if (mMediaProjectionManager != null) { mMediaProjectionManager.stopActiveProjection(); } } Loading Loading @@ -326,6 +361,8 @@ final class ContentRecorder { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Unable to retrieve task to start recording for " + "display %d", mDisplayContent.getDisplayId()); } else { taskToRecord.registerWindowContainerListener(this); } return taskToRecord; default: Loading @@ -342,9 +379,9 @@ final class ContentRecorder { /** * Exit this recording session. * <p> * If this is a task session, tear down the recording entirely. Do not fall back * to recording the entire display on the display stack; this would surprise the user * given they selected task capture. * If this is a task session, stop the recording entirely, including the MediaProjection. * Do not fall back to recording the entire display on the display stack; this would surprise * the user given they selected task capture. * </p><p> * If this is a display session, just stop recording by layer mirroring. Fall back to recording * from the display stack. Loading @@ -353,25 +390,14 @@ final class ContentRecorder { private void handleStartRecordingFailed() { final boolean shouldExitTaskRecording = mContentRecordingSession != null && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK; if (shouldExitTaskRecording) { // Clean up the cached session first, since tearing down the display will generate // display // events which will trickle back to here. clearContentRecordingSession(); tearDownVirtualDisplay(); } else { clearContentRecordingSession(); if (shouldExitTaskRecording) { // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. stopMediaProjection(); } } /** * Ensure recording does not fall back to the display stack; ensure the recording is stopped * and the client notified by tearing down the virtual display. */ private void tearDownVirtualDisplay() { // TODO(b/219761722) Clean up the VirtualDisplay if task mirroring fails } /** * Apply transformations to the mirrored surface to ensure the captured contents are scaled to * fit and centred in the output surface. Loading Loading @@ -442,4 +468,37 @@ final class ContentRecorder { } return surfaceSize; } // WindowContainerListener @Override public void onRemoved() { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Recorded task is removed, so stop recording on display %d", mDisplayContent.getDisplayId()); Task recordedTask = mRecordedWindowContainer.asTask(); if (recordedTask == null || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) { return; } recordedTask.unregisterWindowContainerListener(this); // Stop mirroring and teardown. clearContentRecordingSession(); // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. stopMediaProjection(); } // WindowContainerListener @Override public void onMergedOverrideConfigurationChanged( Configuration mergedOverrideConfiguration) { WindowContainerListener.super.onMergedOverrideConfigurationChanged( mergedOverrideConfiguration); onConfigurationChanged(mLastOrientation); mLastOrientation = mergedOverrideConfiguration.orientation; } @VisibleForTesting interface MediaProjectionManagerWrapper { void stopActiveProjection(); } }
services/core/java/com/android/server/wm/ContentRecordingController.java +4 −6 Original line number Diff line number Diff line Loading @@ -56,14 +56,13 @@ final class ContentRecordingController { * Updates the current recording session. If a new display is taking over recording, then * stops the prior display from recording. * * @param incomingSession the new recording session. Should either be {@code null}, to stop * the current session, or a session on a new/different display than the * current session. * @param incomingSession the new recording session. Should either have a {@code null} token, to * stop the current session, or a session on a new/different display * than the current session. * @param wmService the window manager service */ void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession, @NonNull WindowManagerService wmService) { // TODO(b/219761722) handle a null session arriving due to task setup failing if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession) || ContentRecordingSession.isSameDisplay(mSession, incomingSession))) { // Ignore an invalid session, or a session for the same display as currently recording. Loading @@ -82,8 +81,7 @@ final class ContentRecordingController { } if (mSession != null) { // Update the pre-existing display about the new session. ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Pause the recording session on display %s", ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Pause the recording session on display %s", mDisplayContent.getDisplayId()); mDisplayContent.pauseRecording(); mDisplayContent.setContentRecordingSession(null); Loading