# GPS2Audio v2.0.6 — Atmo Background Playback Fix (WakeMode) ## Artifacts - Signed APK: `/home/user/workspace/GPS2Audio_v2_0_6_atmo_background_fix_signed.apk` - Source zip: `/home/user/workspace/GPS2Audio_v2_0_6_atmo_background_fix_source.zip` - This handoff: `/home/user/workspace/GPS2Audio_v2_0_6_atmo_background_fix_handoff.md` ## Build & Signing Verification | Field | Value | |----------------------|---------------------------------------------------| | applicationId | `de.waypointaudio` | | versionCode | `34` (up from `33` in v2.0.5) | | versionName | `2.0.6` | | 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` | | apksigner verify | PASS (v2 scheme) | | Source baseline | `GPS2Audio_v2_0_5_atmo_service_hotfix_source.zip` | The 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.5 (`FOREGROUND_SERVICE_MEDIA_PLAYBACK`, `POST_NOTIFICATIONS`, `WAKE_LOCK`, etc. all preserved). ## Bug Report Summary User report: After the v2.0.5 hotfix, pressing **Atmo stream Play** no longer crashes (foreground-service start deadline fix worked) – **but when the app is minimized / sent to background, the Atmo music still stops/pauses** within a short time. Expected behavior: Atmo continues to play in the background until the user (or one of the well-defined app rules) intentionally pauses or stops it. ## Root Cause In v2.0.4 we added `AtmoPlaybackService`, a `foregroundServiceType="mediaPlayback"` service that prevents Android from killing the app process while Atmo is alive (this is what the v2.0.5 hotfix made crash-safe). **However, a foreground service alone does NOT keep the device's CPU and network awake when the screen turns off.** The FGS guarantees the *process* stays alive, but the CPU enters low-power state and the network gets de-prioritised. For ExoPlayer this manifests as the audio pipeline stalling / pausing shortly after screen-off — exactly the user's observation. The fix is the one ExoPlayer documents for exactly this case: call `setWakeMode(...)` on the player. This makes ExoPlayer acquire a `PARTIAL_WAKE_LOCK` (CPU stays on while playing) and, for `WAKE_MODE_NETWORK`, additionally a `WifiLock` so the network connection survives screen-off / Doze. In v2.0.5's `BackgroundMusicPlayer.buildPlayer()` we did *not* call `setWakeMode(...)`. With no wake-mode set, ExoPlayer relies on the process holding the wake state externally — which, in this app, nobody did once `FLAG_KEEP_SCREEN_ON` (managed by `WakeLockController`) was cleared on the Activity going to `onPause`. That clearing is *correct* (only the screen flag should drop on minimise — see WakeLockController docstring), and was the explicit design since v2.0.0. The missing piece was the audio-pipeline wake lock owned by ExoPlayer itself. The Foreground Service notification IS being posted (v2.0.4 still works) — it's just that without a wake lock, the CPU sleeps and the player has nothing to decode with. ## Fix (exact changes) ### File: `app/src/main/kotlin/de/waypointaudio/service/BackgroundMusicPlayer.kt` Inside `buildPlayer(settings)`, immediately after `setHandleAudioBecomingNoisy(true)`, we now set the wake mode on the ExoPlayer instance: ```kotlin // Wake-Mode auf Network: hält PARTIAL_WAKE_LOCK + WifiLock während // der Wiedergabe – damit Stream UND lokale Playlist auch bei // ausgeschaltetem Display weiterspielen (v2.0.6 fix). // Der Foreground-Service alleine verhindert nur, dass der Prozess // gekillt wird – er hält aber nicht die CPU/das Netzwerk wach. // ExoPlayer benötigt diesen Modus auf vielen Geräten (Android 12+, // Doze), damit die Audio-Pipeline beim Bildschirm-Aus nicht // einschläft. Der Lock wird automatisch wieder freigegeben, // sobald `release()` oder `stop()` aufgerufen wird. if (isStream) { p.setWakeMode(C.WAKE_MODE_NETWORK) } else { p.setWakeMode(C.WAKE_MODE_LOCAL) } ``` - **Stream URLs** → `WAKE_MODE_NETWORK` (PARTIAL_WAKE_LOCK + WifiLock). - **Local playlist** → `WAKE_MODE_LOCAL` (PARTIAL_WAKE_LOCK only, no WifiLock, because no network access is needed once tracks are loaded from local files). ExoPlayer releases both locks automatically on `Player.stop()` / `Player.release()`, which is already wired into `BackgroundMusicPlayer.releasePlayer()`. Pause/resume keep the player allocated (the wake lock is held only while actually playing, ExoPlayer manages this internally). The existing `WAKE_LOCK` permission in `AndroidManifest.xml` already covers this — it was originally declared "for ExoPlayer", as the manifest comment states (`"Keep CPU alive while playing audio in service"`). It was just never being claimed because the player never asked for it. ### File: `app/build.gradle.kts` - `versionCode 33 → 34` - `versionName "2.0.5" → "2.0.6"` No other changes elsewhere. ## What was deliberately NOT changed - `AtmoPlaybackService` — kept exactly as the v2.0.5 hotfix. The service-start-deadline fix remains intact (still calls `startForeground` in `onCreate` as the very first action). - `MainActivity.onPause/onResume` — only `WakeLockController` is touched; it manipulates the **screen-on** flag (`FLAG_KEEP_SCREEN_ON`), never the audio wake lock. The two are deliberately separate concerns since v2.0.0. - `WakeLockController` — untouched. The screen flag still drops on background as designed. ExoPlayer now owns the CPU/wifi wake lock for audio, decoupled from screen state. - `BackgroundMusicManager` — untouched. Still an app-singleton owning the player; not released on Activity lifecycle (only on `release()`, which nothing currently calls). - `BackgroundMusicPlayer.beforeWaypointAudio / afterWaypointAudio` — untouched. PTT/waypoint interruption flows unchanged. - `AtmoResumeManager` — untouched. - `WaypointAudioInterruptionCoordinator` — untouched. - `ViewModel.onCleared()` — does NOT release the music manager; only the manual player. Confirmed (`viewmodel/WaypointViewModel.kt:1533-1539`). - No `LifecycleEventObserver` / `ProcessLifecycleOwner` / `DefaultLifecycleObserver` exists anywhere that would pause Atmo on background — grep-verified. - No `DisposableEffect` releases the Atmo player on screen disposal (the two existing `DisposableEffect` blocks dispose only the map view and draft-editor map view). - `AudioManager.requestAudioFocus` is not called on the Atmo path; ExoPlayer's audio-focus handling is left off (`handleAudioFocus = false`) so the app's own coordinators decide pause/resume — unchanged. ## How the fix satisfies the requirements 1. **Atmo stream and local playlist continue playing when minimized/backgrounded.** ✅ ExoPlayer now holds PARTIAL_WAKE_LOCK (+ WifiLock for streams) for the duration of playback, so CPU/network do not sleep when the screen turns off. 2. **Atmo still pauses/resumes for intentional rules** (PTT, waypoint, manual player, user pause/stop, disable, tour change). ✅ All those call paths go through `BackgroundMusicManager`/`AtmoResumeManager`/`beforeWaypointAudio` – none touched. The wake lock is released by ExoPlayer when `pause()` is called and re-acquired on `play()`, so pause flows are not impeded. 3. **Wake Lock release on background must not affect audio.** ✅ `WakeLockController` only touches `FLAG_KEEP_SCREEN_ON` on the Activity window. The audio wake lock is now an *independent* lock owned by ExoPlayer and unaffected by `onActivityPaused()`. 4. **Foreground service is alive for the whole Atmo playback duration.** ✅ Service start/stop is tied to `buildPlayer()` / `releasePlayer()` (i.e. to the *existence* of an ExoPlayer instance), not to Activity state. Pause / duck / fade keep the player allocated, so the FGS keeps running. ExoPlayer does not stop on Activity stop — the manager is an app-singleton, not a ViewModel-scoped object. 5. **Notification/service stops when Atmo is truly stopped/released by user or no content remains.** ✅ `releasePlayer()` calls `AtmoPlaybackService.stop(context)`; pause does not. 6. **Preserve all v2.0.5 features, no regressions.** ✅ Manifest unchanged, permissions unchanged, all PTT / waypoint / drag-reorder / Wake-Lock- settings / map-providers / Track-Editor + drafts / backup import-export / map-search / audio-library / OSM-attribution / TabRow fix / About-PayPal + version-notice code paths untouched. 7. **Version & signing.** ✅ versionCode 33 → 34, versionName 2.0.5 → 2.0.6. Signed with the existing GPS2Audio release keystore. `apksigner verify` passes. SHA-256 fingerprint matches all prior release builds. 8. **Artifacts produced at the requested paths.** ✅ See "Artifacts" above. ## Manual Test Plan ### Primary regression (the actual user bug) 1. Configure Atmo for a tour — either a local playlist OR a stream URL. 2. Open the app, start Atmo via the music dialog test button or via the start-tour flow. 3. Confirm the ongoing notification "Begleitmusik (Atmo)" / "Begleitmusik läuft" appears. 4. Press **Home** to minimize the app. **Audio must keep playing.** 5. Wait **at least 2 minutes** with the app in background. Audio must still be playing the whole time. 6. (If possible) **Lock the screen** (Power button) and wait another 2 minutes. Audio must still be playing. 7. **Reopen the app** — the mini-player / dialog must show the same track / stream still playing, no glitch / restart, no double-track, no error. ### PTT still pauses Atmo (v2.0.3 contract) 1. With Atmo playing in foreground, hold PTT. 2. Atmo pauses (or fades / ducks per settings). 3. Release PTT — Atmo resumes from where it left off. 4. Repeat with the app minimized / screen off — Atmo should still pause when PTT is engaged and resume when released. ### Waypoint audio still pauses Atmo (v2.0.3 contract) 1. Manually trigger a waypoint audio from the list, or walk into a radius. 2. Atmo follows the configured `WaypointMusicBehavior` (`PAUSE_RESUME` / `FADE_OUT_IN` / `DUCK_UNDERLAY` / `CONTINUE_UNDERLAY`). 3. After the waypoint audio finishes naturally, Atmo continues / resumes / auto-starts per `autoStartAfterWaypoint`. ### Manual player still pauses Atmo 1. Tap a waypoint to play its audio manually. 2. Atmo pauses for the duration, resumes after. ### User stop / pause / disable still works 1. Press **Stop** in the music dialog/mini-player → Atmo stops, **notification disappears** within ~1 s, no resume. 2. Press **Pause** → Atmo pauses, notification stays (player still allocated), pressing play resumes from same position. 3. Open Begleitmusik dialog → toggle "Aktiviert" off → save. Atmo stops, notification disappears. ### Tour change 1. Switch to a tour with `enabled=false` Atmo. Atmo stops on tour switch, notification disappears. 2. Switch to a tour with a different stream/playlist with `enabled=true` → new content takes over. ### Headphone unplug 1. With wired/Bluetooth headphones connected and Atmo playing, unplug (or disconnect Bluetooth). Atmo pauses (does **not** stop) — `setHandleAudioBecomingNoisy(true)` unchanged. 2. Press play in dialog/mini-player to resume. ### Wake Lock settings 1. Set Wake Lock mode to "Active tour only". Start the tour, screen stays on. 2. Press Power to lock the screen. The screen-on flag releases (expected), but Atmo keeps playing (audio wake lock is independent). 3. Unlock — Atmo is still playing, screen-on flag re-applies if mode dictates. ### Battery / Doze sanity - On a stock Android device with Battery Optimisation enabled for the app, Atmo should still play continuously in background — `PARTIAL_WAKE_LOCK` acquired by a foreground-service-backed app is an explicitly permitted use case on all Android versions. ## Regression Checklist (v2.0.x feature contract) - [x] PTT pauses waypoint audio (v2.0.3 feature) — unchanged code path. - [x] PTT pauses Atmo with resume — `AtmoResumeManager.notifyInterruptStart` still calls `pauseMusic()`, only releasing service on hard stop. - [x] Atmo plays in background — v2.0.4 FGS + v2.0.6 wake mode. - [x] No FGS-deadline crash on Atmo stream Play — v2.0.5 hotfix preserved. - [x] Drag-and-drop reorder of waypoints. - [x] Wake Lock settings (Never / App foreground / Active tour) — unchanged semantics, audio decoupled. - [x] Map providers — unchanged. - [x] Track-Editor + Drafts — unchanged. - [x] Backup import/export — unchanged. - [x] Map search — unchanged. - [x] Audio library — unchanged. - [x] OSM attribution badge on map — unchanged. - [x] TabRow fix — unchanged. - [x] About dialog PayPal link + Matrix QR — unchanged. - [x] Version-notice popup (once per new versionCode) — fires on first launch of 2.0.6 since versionCode bumped 33 → 34. ## Build Reproduction ``` unzip GPS2Audio_v2_0_6_atmo_background_fix_source.zip -d /tmp/v2.0.6 cd /tmp/v2.0.6/work cat > keystore.properties <