App crashes on startup when the control port is taken or a MediaSession still exists #11
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Two unguarded failure points during
onCreate(), both of which throw instead of degrading:1.
MediaSessionuses the default (empty) id.PlayerController.kt:26buildsMediaSession.Builder(context, player).build()withoutsetId(). Media3 keeps a staticSESSION_ID_TO_SESSION_MAPguarded by a static lock; the constructor throwsIllegalStateException("Session ID must be unique. ID=")if a session with that id is still registered in the process. If a previousPlayerControllerhas not been released when a newMainActivityis created, this crashes before anything else runs.2.
start(0, true)has no error handling.ControlServer.startServer()(ControlServer.kt:73-74) calls it directly fromonCreate(). A bind failure — port 8765 held by another process, or an OEM quirk on some TV — surfaces as an uncaughtIOExceptionand takes the whole app down at launch.Failure scenario
Neither is likely on the daily path (
configChangesavoids most recreations, and 8765 is usually free), but both fail hard rather than gracefully: the app dies at startup with a system crash dialog, and the viewer has no way to understand or recover from it. For an app whose whole point is being idiot-proof for family members, "port busy" should not equal "app won't start".Suggested fix
.setId("castarr"), and make surerelease()always runs before a new one is builtstart()in arunCatching; on failure keep the app fully usable without the phone remote and show a plain-language note in the settings ("Handy-Fernbedienung nicht verfügbar")Found by multi-agent code review; verified against media3 1.4.1 bytecode and current
PlayerController.kt/ControlServer.kt.Note: the same review flagged blocking socket writes on the main thread, cross-thread ExoPlayer reads and non-volatile state sharing — all of which are already fixed in this codebase (
sendExecutorfor all writes, payloads built on the main thread viapost {},StateFlowfor channel data).Fixed in
dd612fc, shipped in v0.9.0.MediaSession.Builder(context, player).setId("castarr").build(), so it no longer collides on the default empty id.startServer()wrapsstart()inrunCatchingand exposes arunningflag. A bind failure is logged and the app continues normally — everything except the phone remote keeps working.AppState.remoteAvailablecarries that flag into the UI: the Handy-Fernbedienung card then says "Fernbedienung nicht verfügbar — Port belegt. Neustart des Fernsehers hilft meistens." instead of the app dying at launch with a system crash dialog.stopServer()only callsstop()when the server actually started, so teardown after a failed bind is clean too.