I have an Android screen recording app. In my app the user can start a screen recording directly in the app, as well as via a button in a notification (bound to a foreground service).
To start a recording I retrieve the intent for creating media projections via the screen recording system dialog:
startActivityForResult(activity, mediaProjectionManager.createScreenCaptureIntent(), requestId)
This intent is later used to create a MediaProjection, which is again used to acquire a VirtualDisplay
for the screen recording.
In order to avoid having to start a screen recording system dialog every time the user starts a recording via the notification from my foreground service, I save the original Intent
for creating media projections in the service and then simply reuse that Intent
. This worked fine up to Android 14 (API 34).
During my testing with the Android 14 Preview I found out that this does not seem to be possible anymore. According to the exception thrown it is not allowed anymore to retrieve a MediaProjection
with the same Intent
OR create multiple virtual displays with the same MediaProjection
.
FATAL EXCEPTION: main
Process: com.recording.app.staging, PID: 19029
java.lang.SecurityException: Don't re-use the resultData to retrieve the same projection instance, and don't use a token that has timed out. Don't take multiple captures by invoking MediaProjection#createVirtualDisplay multiple times on the same instance.
at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
at android.os.Parcel.createException(Parcel.java:3041)
at android.os.Parcel.readException(Parcel.java:3024)
at android.os.Parcel.readException(Parcel.java:2966)
at android.hardware.display.IDisplayManager$Stub$Proxy.createVirtualDisplay(IDisplayManager.java:1425)
at android.hardware.display.DisplayManagerGlobal.createVirtualDisplay(DisplayManagerGlobal.java:643)
at android.hardware.display.DisplayManager.createVirtualDisplay(DisplayManager.java:1134)
at android.media.projection.MediaProjection.createVirtualDisplay(MediaProjection.java:247)
at android.media.projection.MediaProjection.createVirtualDisplay(MediaProjection.java:216)
at com.recording.app.model.service.recording.resource.MediaProjectionResource.createVirtualDisplay(MediaProjectionResource.kt:39)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.isValid(MediaProjectionManagerService.java:1081)
at com.android.server.display.DisplayManagerService.createVirtualDisplayInternal(DisplayManagerService.java:1447)
at com.android.server.display.DisplayManagerService.-$$Nest$mcreateVirtualDisplayInternal(DisplayManagerService.java:0)
at com.android.server.display.DisplayManagerService$BinderService.createVirtualDisplay(DisplayManagerService.java:3706)
at android.hardware.display.IDisplayManager$Stub.onTransact(IDisplayManager.java:730)com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.isValid(MediaProjectionManagerService.java:1081)
...
Is there still a way to allow my users to only grant the screen recording permission once and then to directly start a recording (as long as my service was not killed)?
Maybe it is possible to keep and reuse a single VirtualDisplay
, but that also does not seem like a good solution because the display would mirror the real display even if there was no recording going on, wasting resources.
If that does not work, my only remaining option is to always ask the user for permission. But I am also unsure whether that works when the user has opened a different app and only wants to start the recording via notification.
Did I miss something? Any insights are greatly appreciated!
Update
After some more testing:
- Reusing the same
Intent
to retrieve multipleMediaProjection
s is not possible - Reusing a single
MediaProjection
instance to get multipleVirtualDisplay
s also seems impossible - It is possible to reuse a single
VirtualDisplay
instance to do multiple recordings, but seems hacky
Outcome
It is possible to do multiple recordings with a single VirtualDisplay
by always updating the target surface using
virtualDispaly.surface = newRecordingSurface
However, this seems hacky and not intended because the system UI will show that the app is recording the screen as long as the VirtualDisplay
is active. So even if my app is not "really" capturing the screen (because no target surface is set for the VirtualDisplay
) the system UI still shows like it is.
This would look very concerning for a user and the user can also just stop the screen capture at which point the VirtualDisplay
seems to be broken.
Therefore, I decided to remove the functionality, to start a recording from my foreground service, until I find a better option.