• v2.0.5 b2aa1c167c

    marcel released this 2026-05-15 14:31:53 +02:00 | 7 commits to main since this release

    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.

    Downloads