goon/mobile/patches/expo-video+2.0.6.patch
goon-foss f33f37f117 fix(mobile): expo-video bez NoSuchElementException przy zamykaniu serwisu
toList() na values ConcurrentHashMap przy rozmiarze 1 robi iterator().next(); gdy
sesja zniknie miedzy size() i next(), leci NoSuchElementException (0.4.2, GOON-72).
ArrayList(values) idzie przez toArray(), ktore mapa robi bezpiecznie. Natywne,
czeka na nastepne APK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 12:00:11 +02:00

36 lines
2.1 KiB
Diff

diff --git a/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt b/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt
index cd798cf..8cb6dfb 100644
--- a/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt
+++ b/node_modules/expo-video/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt
@@ -26,7 +26,7 @@ class PlaybackServiceBinder(val service: ExpoVideoPlaybackService) : Binder()
@OptIn(UnstableApi::class)
class ExpoVideoPlaybackService : MediaSessionService() {
- private val mediaSessions = mutableMapOf<ExoPlayer, MediaSession>()
+ private val mediaSessions = java.util.concurrent.ConcurrentHashMap<ExoPlayer, MediaSession>()
private val binder = PlaybackServiceBinder(this)
private val commandSeekForward = SessionCommand(SEEK_FORWARD_COMMAND, Bundle.EMPTY)
@@ -136,10 +136,20 @@ class ExpoVideoPlaybackService : MediaSessionService() {
private fun cleanup() {
hideAllNotifications()
- mediaSessions.forEach { (_, session) ->
+ // Kopia przed iteracja: session.release() wola z powrotem unregisterPlayer, ktory
+ // usuwa wpis z mediaSessions w trakcie forEach -> ConcurrentModificationException
+ // przy zamykaniu apki z listy ostatnich (Goon, Sentry 147164135, 2026-09-15).
+ // Mapa jest ConcurrentHashMap (patrz deklaracja), bo sama kopia tez iteruje mape,
+ // a wpisy znikaja z innego watku przy zwalnianiu playera: na LinkedHashMap `toList()`
+ // wywalalo sie wtedy tak samo (Goon 0.4.1, Sentry GOON-6W, 2026-09-17).
+ // ArrayList(values), nie `toList()`: toList przy rozmiarze 1 robi iterator().next(),
+ // a wpis moze zniknac miedzy size() i next() -> NoSuchElementException (Goon 0.4.2,
+ // Sentry GOON-72). Konstruktor idzie przez toArray(), ktore mapa robi bezpiecznie.
+ val sessions = ArrayList(mediaSessions.values)
+ mediaSessions.clear()
+ sessions.forEach { session ->
session.release()
}
- mediaSessions.clear()
}
private fun hidePlayerNotification(player: ExoPlayer) {