Skip to content

Fix playback stopping when Android backs up the app - #6205

Merged
geekygecko merged 5 commits into
mainfrom
philip/backup-playback
Oct 22, 2026
Merged

geekygecko merged 5 commits into
mainfrom
philip/backup-playback

Conversation

@geekygecko

@geekygecko geekygecko commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Description

On Android 16 and newer, playback no longer stops when the phone runs its automatic backup of Pocket Casts in the background. I managed to repeat this issue using theadb backupnow command. This could be why alot of people's episodes stop playing.

A user reported playback stopping 10 to 20 minutes into a long sleep timer. Their bugreport showed Google's backup transport starting a full-data backup of the app mid-playback, then BackupManagerService logging "Killing agent host process" and SIGKILLing our process, foreground playback service and all. Android restarted the service, but nothing resumed playback, and the in-memory sleep timer was lost. The earlier incidents in the report have the same exit signature, as does PCDROID-555.

Android only kills the app after a backup when the backup ran in "restricted mode". Full backups used restricted mode by default, but Android 16 added the PROPERTY_USE_RESTRICTED_BACKUP_MODE application property. Setting it to false runs the backup inside our normal process and skips the kill on unbind (see BackupAgentConnectionManager.shouldKillAppOnUnbind in AOSP).

android:backupInForeground="false" is also set explicitly. It's already the default, and it only defers the system's scheduled full-backup job. Backups requested through requestBackup() (Google Play services, bmgr backupnow) skip that check, which is why the property is the actual fix.

Things reviewers should know:

  • Only the release build type enables backup, so this only changes release builds.
  • Android 15 and older ignore the property and can still be killed this way. Fixing those would mean moving to key/value backup.
  • Without restricted mode, a restore also runs our full Application startup while Android replaces the database and preferences files. That could overwrite restored data, so restore needs testing before release (step 6 below).

Fixes #6204
Linear: https://linear.app/a8c/issue/PCDROID-831/playback-ends-before-end-of-sleep-timer-sporadically

Testing Instructions

Use a release build on an Android 16+ device with backup enabled (adb shell bmgr enabled).

  1. Start playing an episode.
  2. Run adb shell bmgr backupnow au.com.shiftyjelly.pocketcasts.
  3. ✅ Playback keeps going.
  4. Optional: repeat on main to see playback stop and the process get killed.
  5. Signed out, subscribe to a few podcasts and change some settings, then run backupnow again.
  6. Uninstall, then reinstall the release build so Android restores automatically. ✅ Podcasts, settings and playlists come back, and the restore login prompt appears.

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

@geekygecko
geekygecko requested a review from a team as a code owner October 20, 2026 22:39
@geekygecko
geekygecko requested review from sztomek and removed request for a team October 20, 2026 22:39
@geekygecko geekygecko added the [Type] Bug Not functioning as intended. label Oct 20, 2026
@geekygecko geekygecko added this to the 8.24 milestone Oct 20, 2026
@geekygecko geekygecko added the [Area] Playback Episode playback issue label Oct 20, 2026
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @geekygecko's task in 47s —— View job


The manifest change should stop the post-backup SIGKILL on Android 16+. The catch is that the same property also turns off restricted mode for restore. Restore then runs alongside a fully initialised app that holds Room and SharedPreferences open, and nothing kills the process afterwards, so restored data can be overwritten. I'd hold the merge until restore is made safe, not just tested.

Blocking

  • app/src/release/AndroidManifest.xml:12 On Android 16+, restoring to a new phone can wipe out the restored podcasts and settings: the running app's stale prefs cache and open DB connection outlive the file swap. Add a minimal backupAgent with fullBackupOnly="true" that calls exitProcess(0) in onRestoreFinished(), then test restore with the app already running. Fix playback stopping when Android backs up the app #6205 (comment)

Comment on lines +12 to +14
<!-- Stops Android 16+ killing the app (and interrupting playback) after a full backup -->
<property
android:name="android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Android 16+, a user restoring to a new phone can end up with their restored podcasts and settings overwritten or reset, because restore now runs inside the full app process.

This property turns off restricted mode for restore as well as backup. That means PocketCastsApplication.onCreate runs first: Hilt init, Room opening and migrating pocketcasts, and Settings loading au.com.shiftyjelly.pocketcasts_preferences.xml into the SharedPreferences cache. Then the transport replaces those files underneath it. Because the process is no longer killed on unbind (the shouldKillAppOnUnbind path you cite), it keeps running with an open Room connection to the old file and a stale prefs cache. The next apply() writes the pre-restore prefs back over the restored file, and DB writes go to the replaced inode. Step 6 of the test plan probably won't catch this, because the restore usually runs before anything in the app touches prefs or the DB.

Fix: keep the backup benefit but make restore safe. Add a minimal android:backupAgent with android:fullBackupOnly="true" (it stays a full backup with the same rules) and end the process in onRestoreFinished() (exitProcess(0)) so the next launch reads the restored files. Then test restore with the app already running, e.g. bmgr restore <token> au.com.shiftyjelly.pocketcasts while it's in the foreground.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completed the restore successfully in my testing, and everything worked as expected.

@wpmobilebot

wpmobilebot commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Project manifest changes for app

The following changes in the app's merged AndroidManifest.xml file were detected (build variant: release):

--- ./build/reports/diff_manifest/app/release/base_manifest.txt	2026-10-22 01:56:27.893594891 +0000
+++ ./build/reports/diff_manifest/app/release/head_manifest.txt	2026-10-22 01:56:59.132372442 +0000
@@ -100,6 +100,12 @@
         android:supportsRtl="false"
         android:theme="@style/ThemeDark"
         android:usesCleartextTraffic="true" >
+
+        <!-- Stops Android 16+ killing the app (and interrupting playback) after a full backup -->
+        <property
+            android:name="android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE"
+            android:value="false" />
+
         <meta-data
             android:name="com.google.android.gms.car.application"
             android:resource="@xml/automotive_app_desc" />

Go to https://buildkite.com/automattic/pocket-casts-android/builds/20313/canvas?sid=01a16150-2e85-4af9-8df3-23dc798f75e0, click on the Artifacts tab and audit the files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @geekygecko's task in 17s —— View job


The only commit since the last review adds the CHANGELOG entry, so there's nothing new to review. The restore concern is still open. Step 6 runs a restore on a fresh install, before the app has opened Room or loaded SharedPreferences. That case can pass even when a restore with the app already running loses data.

Blocking

  • app/src/release/AndroidManifest.xml:14 On Android 16+, the app's open DB connection and cached prefs can overwrite restored data, because nothing restarts the process after a non-restricted restore. To fix it, add a backupAgent with fullBackupOnly="true" that calls exitProcess(0) in onRestoreFinished(). Alternatively, show that a restore works with the app open (bmgr restore <token> au.com.shiftyjelly.pocketcasts while it's in the foreground). Fix playback stopping when Android backs up the app #6205 (comment)

@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @geekygecko's task in 14s —— View job


The only new commit (4b39ccb) rewords the CHANGELOG entry, so there's no new code to review. The restore concern is still open. The restore you tested passed, but a restore onto a fresh install runs before the app has opened Room or loaded prefs. The risky case is a restore while the app is already running, and that hasn't been shown yet.

Blocking

  • app/src/release/AndroidManifest.xml:14 On Android 16+, data restored while the app is running can be overwritten, because without restricted mode nothing kills the process after the restore. To close this, either add a backupAgent with fullBackupOnly="true" that calls exitProcess(0) in onRestoreFinished(), or run bmgr restore <token> au.com.shiftyjelly.pocketcasts with the app in the foreground and confirm the restored data is still there after relaunch. Fix playback stopping when Android backs up the app #6205 (comment)

@claude

claude Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 14s —— View job


The only new commit (fdba244) merges main and brings in nothing new for this PR's files, so there's nothing new to review. The restore concern is still open: a restore while the app is already running hasn't been shown to keep the restored data.

Blocking

  • app/src/release/AndroidManifest.xml:14 On Android 16+, data restored while the app is running can be overwritten, because nothing kills the process after a non-restricted restore. Add a backupAgent with fullBackupOnly="true" that calls exitProcess(0) in onRestoreFinished(), or confirm that bmgr restore with the app in the foreground keeps the restored data. Fix playback stopping when Android backs up the app #6205 (comment)

Comment thread app/src/release/AndroidManifest.xml Outdated
@claude

claude Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @geekygecko's task in 13s —— View job


The only new commit (ee288aa) removes android:backupInForeground="false" and changes nothing else, so there's no new problem to raise. The restore concern from earlier is still open: no one has shown that a restore keeps its data when the app is already running.

Blocking

  • app/src/release/AndroidManifest.xml:12 On Android 16+, a restore that happens while the app is running can have its data overwritten, because nothing kills the process after a non-restricted restore. Either add a backupAgent with fullBackupOnly="true" that calls exitProcess(0) in onRestoreFinished(), or run bmgr restore with the app in the foreground and confirm the restored data is still there. Fix playback stopping when Android backs up the app #6205 (comment)

@geekygecko
geekygecko merged commit ba6e06d into main Oct 22, 2026
22 of 23 checks passed
@geekygecko
geekygecko deleted the philip/backup-playback branch October 22, 2026 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] Playback Episode playback issue [Type] Bug Not functioning as intended.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Playback ends before end of sleep timer sporadically

3 participants