Summary
MediaProjectionController.stopInternal() can invoke onStopListener twice, and the listener (ScreenCaptureManager.releaseResources()) is unsynchronized — creating a race condition with in-progress captures that can crash the app.
Double invocation path
In stopInternal() (lines 73-88):
unregisterCallback() throws IllegalStateException (line 79, caught and swallowed) — callback remains registered
projection.stop() (line 83) triggers the callback's onStop() (line 30-33), which invokes onStopListener — first fire
- Line 87 invokes
onStopListener unconditionally — second fire
Spurious invocation
When projection is already null (e.g., during first initialize() call), line 87 still fires onStopListener. This calls releaseResources() with nothing to release.
Race condition
releaseResources() (ScreenCaptureManager.kt:178-188) is not synchronized. It can run from:
- The
onStop callback (main/binder thread)
stopInternal (any thread)
If a capture is in progress (holding the Mutex on Dispatchers.Default):
imageReader can be closed while awaitImage() is waiting on it
handlerThread can be quit while OnImageAvailableListener is dispatching
virtualDisplay can be released while ImageReader expects frames
This can produce IllegalStateException crashes.
Files
action-executor/src/main/java/com/scroller/agent/executor/MediaProjectionController.kt (lines 73-88, 29-33)
action-executor/src/main/java/com/scroller/agent/executor/ScreenCaptureManager.kt (lines 44-47, 178-188)
Summary
MediaProjectionController.stopInternal()can invokeonStopListenertwice, and the listener (ScreenCaptureManager.releaseResources()) is unsynchronized — creating a race condition with in-progress captures that can crash the app.Double invocation path
In
stopInternal()(lines 73-88):unregisterCallback()throwsIllegalStateException(line 79, caught and swallowed) — callback remains registeredprojection.stop()(line 83) triggers the callback'sonStop()(line 30-33), which invokesonStopListener— first fireonStopListenerunconditionally — second fireSpurious invocation
When
projectionis already null (e.g., during firstinitialize()call), line 87 still firesonStopListener. This callsreleaseResources()with nothing to release.Race condition
releaseResources()(ScreenCaptureManager.kt:178-188) is not synchronized. It can run from:onStopcallback (main/binder thread)stopInternal(any thread)If a capture is in progress (holding the
MutexonDispatchers.Default):imageReadercan be closed whileawaitImage()is waiting on ithandlerThreadcan be quit whileOnImageAvailableListeneris dispatchingvirtualDisplaycan be released while ImageReader expects framesThis can produce
IllegalStateExceptioncrashes.Files
action-executor/src/main/java/com/scroller/agent/executor/MediaProjectionController.kt(lines 73-88, 29-33)action-executor/src/main/java/com/scroller/agent/executor/ScreenCaptureManager.kt(lines 44-47, 178-188)