Skip to content

onStopListener double-fires and races with active captures #2

Description

@rogue-socket

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):

  1. unregisterCallback() throws IllegalStateException (line 79, caught and swallowed) — callback remains registered
  2. projection.stop() (line 83) triggers the callback's onStop() (line 30-33), which invokes onStopListenerfirst fire
  3. 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions