# GPS2Audio v2.0.5 — Atmo Foreground-Service Hotfix ## Artifacts - Signed APK: `/home/user/workspace/GPS2Audio_v2_0_5_atmo_service_hotfix_signed.apk` - Source zip: `/home/user/workspace/GPS2Audio_v2_0_5_atmo_service_hotfix_source.zip` - This handoff: `/home/user/workspace/GPS2Audio_v2_0_5_atmo_service_hotfix_handoff.md` ## Build & Signing Verification | Field | Value | |----------------------|---------------------------------------------------| | applicationId | `de.waypointaudio` | | versionCode | `33` (up from `32` in v2.0.4) | | versionName | `2.0.5` | | minSdk / targetSdk | 26 / 35 | | compileSdk | 35 | | Build type | release, signed | | Signer DN | `CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DE` | | Signer cert SHA-256 | `7B:0E:A4:D5:52:86:05:17:3D:29:9D:7B:26:8E:7A:B8:76:8D:D5:04:EC:15:B6:6A:52:D4:7B:42:57:B3:BB:46` | | Signer cert SHA-1 | `EB:92:2A:83:39:09:ED:6B:2D:CB:62:52:F0:CF:42:5F:26:DF:25:AD` | Signature matches the GPS2Audio release keystore used since v1 → installs as an update over any existing release-channel install (no uninstall required). `uses-permission` set is unchanged vs. v2.0.4 (FOREGROUND_SERVICE_MEDIA_PLAYBACK, POST_NOTIFICATIONS, WAKE_LOCK, etc. all preserved). ## Bug Report Summary (what crashed) After pressing the **Atmo stream play** button the app crashed with: ``` android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord ... de.waypointaudio/.service.AtmoPlaybackService ``` This is the Android 12+ deadline crash: when an app calls `Context.startForegroundService(intent)`, the system gives the service a hard window (~5 s) to call `Service.startForeground(id, notification)`. Missing the deadline → the platform kills the app process. ## Root Cause In v2.0.4 (`app/src/main/kotlin/de/waypointaudio/service/AtmoPlaybackService.kt`) the deadline-relevant `startForeground(...)` call lived inside `onStartCommand`, gated by `intent?.action == ACTION_START`, and wrapped in `runCatching {}` that **silently swallowed** any throwable. Two concrete ways this could miss the deadline: 1. **Non-START intent on the same `startForegroundService()` cycle.** Both `start()` and `stop()` used `startForegroundService()`/`startService()`, but the `when (intent?.action)` branch for `ACTION_STOP` (or a `null` intent on re-delivery / `START_STICKY_COMPATIBILITY` scenarios) never reached `startForeground`. The system had been told "this is a FGS" but the service never confirmed it. Deadline missed → crash. 2. **`runCatching` swallowing a `startForeground` throwable.** If notification construction or `startForeground` itself threw (e.g. transient `RemoteException` from `NotificationManager`, or any backwards-compatibility edge from `setForegroundServiceType`), the failure was logged and the service kept running without ever calling `startForeground` again. Deadline missed → crash. The screenshot reported by the user is reproducible on Android 12+ devices where the first start path was for a stream and any of the above branches triggered. ## Fix (exact changes) File: `app/src/main/kotlin/de/waypointaudio/service/AtmoPlaybackService.kt` 1. **`startForeground()` is now the very first thing the service does**, in `onCreate()`, with a minimal-valid notification (no network/player work, no Intent parsing). This satisfies the system deadline unconditionally, *before* `onStartCommand` is even invoked. 2. **Re-arm in `onStartCommand`**: if the first `startForeground` attempt threw, we retry as the very first action of `onStartCommand` (`if (!foregroundStarted)`), before any branching on the action. 3. **Notification channel created before `startForeground`** on Android O+ (already true, kept; added a `SDK < O` early-return so the call is safe on older devices). 4. **Notification refresh on ACTION_START** uses `NotificationManager.notify` with the same ID — keeps the current foreground notification valid even if the rebuild with real title/source throws. 5. **`stop()` companion** now also uses `startForegroundService()` on Android O+ instead of `startService()`. This avoids `IllegalStateException` if the stop is requested while the app is in a state the platform considers "background", and it guarantees the service still calls `startForeground` immediately (in `onCreate`) before stopping itself — so the stop path can never be the cause of a missed-deadline crash either. 6. **`onStartCommand` now handles `null`/unknown actions** by stopping the service (after the mandatory `startForeground`) rather than leaving the service running without any work. 7. **`foregroundStarted` flag** prevents redundant `startForeground` calls on repeated starts and is reset in `stopForegroundCompat`. `foregroundServiceType=mediaPlayback` and the `FOREGROUND_SERVICE_MEDIA_PLAYBACK` permission are preserved. The POST_NOTIFICATIONS permission was already declared and is *not* hard-required for a foreground-service notification — denying it merely hides the notification, the FGS still runs. File: `app/build.gradle.kts` - `versionCode = 33`, `versionName = "2.0.5"`. No changes elsewhere. All v2.0.4 behaviors preserved: - Atmo continues playing in background while screen is off or app is minimized (FGS still active across pause/duck/fade, as before). - PTT and Waypoint-Audio pause/resume semantics unchanged (`BackgroundMusicPlayer.beforeWaypointAudio` / `afterWaypointAudio` untouched; FGS deliberately stays alive across pauses). - Manual stop / tour switch still triggers `BackgroundMusicPlayer.releasePlayer()` → `AtmoPlaybackService.stop()` → notification + service torn down. - All previously delivered features intact: drag-and-drop reorder, Wake Lock while recording / playing, multiple map providers, Track-Editor, Backup import/export, map search, audio library, OSM attribution, TabRow fix, About PayPal link + version notice dialog. ## Manual Test Plan These tests target the exact crash and the surrounding Atmo lifecycle. Use a device on Android 12+ (deadline-enforcing). 1. **Atmo stream play — no crash (primary repro).** - Open Tour-Settings → Begleitmusik (Atmo) → switch source to *Stream URL*, enter a valid stream (e.g. an Icecast `.mp3` URL). - Press the **Play** button. - **Expected:** audio starts within a few seconds; *no* crash; status bar shows an ongoing "Atmo / Stream — " notification. 2. **Background continuation.** - With Atmo playing, press Home; then power off the screen. - **Expected:** audio keeps playing for at least 60 s; FGS notification remains visible in the status bar. 3. **Stop / disable.** - Re-open app, press **Stop** on the Atmo controls (or disable Atmo in Tour-Settings). - **Expected:** audio stops; foreground notification disappears; no crash. Re-pressing Play starts a new stream cleanly. 4. **PTT interplay (do not regress).** - Start Atmo, then start a PTT session. - **Expected:** Atmo behavior follows configured *Wegpunkt-Verhalten* (pause/duck/fade/continue) for the PTT duration; FGS notification stays ongoing; Atmo resumes (or stays ducked) on PTT end exactly as in v2.0.4. 5. **Waypoint audio interplay.** - Place a waypoint with an audio file along a recorded track; trigger it while Atmo is playing. - **Expected:** Atmo behaves per configured behavior; FGS stays up; Atmo resumes after waypoint audio finishes, matching v2.0.4 behavior. 6. **Local playlist source.** - Switch Atmo source to *Local playlist*, pick 2–3 tracks, press Play. - **Expected:** plays; notification reads "Atmo / Playlist — N Titel"; no crash; track skip / shuffle behave as before. 7. **POST_NOTIFICATIONS denied (Android 13+).** - Long-press the Atmo notification (or use App Info → Notifications) and disable notifications for GPS2Audio, then start Atmo. - **Expected:** audio still plays in foreground (FGS keeps the process alive); no crash; notification simply suppressed by user choice. 8. **Repeated rapid Start/Stop.** - Toggle Atmo Play/Stop several times in quick succession. - **Expected:** no crash; notification appears/disappears cleanly; service ends after final Stop. 9. **Version check.** - Settings → About → version line reads `2.0.5 (33)`. ## Files Touched (delta vs. v2.0.4 baseline) ``` app/build.gradle.kts (versionCode 32→33, versionName 2.0.4→2.0.5) app/src/main/kotlin/de/waypointaudio/service/AtmoPlaybackService.kt (rewritten: always-on startForeground, null-intent handling, stop via startForegroundService) ``` Nothing else changed.