-
released this
2026-05-15 14:32:02 +02:00 | 7 commits to main since this releaseGPS2Audio 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.waypointaudioversionCode 34(up from33in v2.0.5)versionName 2.0.6minSdk / targetSdk 26 / 35 compileSdk 35 Build type release, signed Signer DN CN=GPS2Audio, OU=NesoHub, O=GPS2Audio, L=Germany, C=DESigner 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:46Signer cert SHA-1 EB:92:2A:83:39:09:ED:6B:2D:CB:62:52:F0:CF:42:5F:26:DF:25:ADapksigner verify PASS (v2 scheme) Source baseline GPS2Audio_v2_0_5_atmo_service_hotfix_source.zipThe signature matches the GPS2Audio release keystore used since v1 → installs
as an update over any existing release-channel install (no uninstall required).uses-permissionset 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, aforegroundServiceType="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, forWAKE_MODE_NETWORK,
additionally aWifiLockso 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 byWakeLockController) was cleared on the
Activity going toonPause. 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.ktInside
buildPlayer(settings), immediately aftersetHandleAudioBecomingNoisy(true),
we now set the wake mode on the ExoPlayer instance:// 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 intoBackgroundMusicPlayer.releasePlayer().
Pause/resume keep the player allocated (the wake lock is held only while
actually playing, ExoPlayer manages this internally).The existing
WAKE_LOCKpermission inAndroidManifest.xmlalready 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.ktsversionCode 33 → 34versionName "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 callsstartForegroundin
onCreateas the very first action).MainActivity.onPause/onResume— onlyWakeLockControlleris 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 onrelease(), 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
DisposableEffectreleases the Atmo player on screen disposal
(the two existingDisposableEffectblocks dispose only the map view and
draft-editor map view). AudioManager.requestAudioFocusis 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
- 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. - Atmo still pauses/resumes for intentional rules (PTT, waypoint, manual
player, user pause/stop, disable, tour change). ✅ All those call paths go
throughBackgroundMusicManager/AtmoResumeManager/beforeWaypointAudio
– none touched. The wake lock is released by ExoPlayer whenpause()is
called and re-acquired onplay(), so pause flows are not impeded. - Wake Lock release on background must not affect audio. ✅
WakeLockControlleronly touchesFLAG_KEEP_SCREEN_ONon the Activity
window. The audio wake lock is now an independent lock owned by
ExoPlayer and unaffected byonActivityPaused(). - Foreground service is alive for the whole Atmo playback duration. ✅
Service start/stop is tied tobuildPlayer()/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. - Notification/service stops when Atmo is truly stopped/released by user
or no content remains. ✅releasePlayer()calls
AtmoPlaybackService.stop(context); pause does not. - 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. - 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. - Artifacts produced at the requested paths. ✅ See "Artifacts" above.
Manual Test Plan
Primary regression (the actual user bug)
- Configure Atmo for a tour — either a local playlist OR a stream URL.
- Open the app, start Atmo via the music dialog test button or via the
start-tour flow. - Confirm the ongoing notification "Begleitmusik (Atmo)" / "Begleitmusik läuft"
appears. - Press Home to minimize the app. Audio must keep playing.
- Wait at least 2 minutes with the app in background.
Audio must still be playing the whole time. - (If possible) Lock the screen (Power button) and wait another 2 minutes.
Audio must still be playing. - 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)
- With Atmo playing in foreground, hold PTT.
- Atmo pauses (or fades / ducks per settings).
- Release PTT — Atmo resumes from where it left off.
- 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)
- Manually trigger a waypoint audio from the list, or walk into a radius.
- Atmo follows the configured
WaypointMusicBehavior
(PAUSE_RESUME/FADE_OUT_IN/DUCK_UNDERLAY/CONTINUE_UNDERLAY). - After the waypoint audio finishes naturally, Atmo continues / resumes /
auto-starts perautoStartAfterWaypoint.
Manual player still pauses Atmo
- Tap a waypoint to play its audio manually.
- Atmo pauses for the duration, resumes after.
User stop / pause / disable still works
- Press Stop in the music dialog/mini-player → Atmo stops,
notification disappears within ~1 s, no resume. - Press Pause → Atmo pauses, notification stays (player still allocated),
pressing play resumes from same position. - Open Begleitmusik dialog → toggle "Aktiviert" off → save. Atmo stops,
notification disappears.
Tour change
- Switch to a tour with
enabled=falseAtmo. Atmo stops on tour switch,
notification disappears. - Switch to a tour with a different stream/playlist with
enabled=true→
new content takes over.
Headphone unplug
- With wired/Bluetooth headphones connected and Atmo playing, unplug
(or disconnect Bluetooth). Atmo pauses (does not stop) —
setHandleAudioBecomingNoisy(true)unchanged. - Press play in dialog/mini-player to resume.
Wake Lock settings
- Set Wake Lock mode to "Active tour only". Start the tour, screen stays on.
- Press Power to lock the screen. The screen-on flag releases (expected),
but Atmo keeps playing (audio wake lock is independent). - 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)
- PTT pauses waypoint audio (v2.0.3 feature) — unchanged code path.
- PTT pauses Atmo with resume —
AtmoResumeManager.notifyInterruptStart
still callspauseMusic(), only releasing service on hard stop. - Atmo plays in background — v2.0.4 FGS + v2.0.6 wake mode.
- No FGS-deadline crash on Atmo stream Play — v2.0.5 hotfix preserved.
- Drag-and-drop reorder of waypoints.
- Wake Lock settings (Never / App foreground / Active tour) — unchanged
semantics, audio decoupled. - Map providers — unchanged.
- Track-Editor + Drafts — unchanged.
- Backup import/export — unchanged.
- Map search — unchanged.
- Audio library — unchanged.
- OSM attribution badge on map — unchanged.
- TabRow fix — unchanged.
- About dialog PayPal link + Matrix QR — unchanged.
- 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 <<EOF storeFile=/path/to/GPS2Audio_release_keystore.jks storePassword=... keyAlias=gps2audio_release keyPassword=... EOF export ANDROID_HOME=/path/to/android-sdk gradle :app:assembleRelease # Output: app/build/outputs/apk/release/app-release.apkTested toolchain: Gradle 8.9, JDK 21 (OpenJDK), Android SDK with platform-35
and build-tools 35.0.0.Downloads
- Signed APK: