Skip to content

CMM-2174: Move background-start work off the main thread - #23285

Open
nbradbury wants to merge 9 commits into
trunkfrom
cmm-2174-background-anr
Open

CMM-2174: Move background-start work off the main thread#23285
nbradbury wants to merge 9 commits into
trunkfrom
cmm-2174-background-anr

Conversation

@nbradbury

@nbradbury nbradbury commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

TL;DR

We've had a long-standing issue with various ANRs at start-up caused by initializing processes on the main thread. This PR attempts to resolve most of the ANRs by moving these processes off the main thread:

  • WorkManager job
  • BOOT_COMPLETED
  • FCM
  • Legacy DB open
  • Gravatar's keystore initializer
  • Analytics DB reads

Description

Fixes CMM-2174 (Sentry JETPACK-ANDROID-PB9, "Background ANR").

That Sentry issue is an ANRv2 bucket built from the main thread's idle-looper stack, so it can't point at a cause. Play Console can: for 3 Aug–1 Sep the named main-thread frames on background process starts (WorkManager SystemJobService, BOOT_COMPLETED, FCM) were WordPressDB.<init>, AppInitializer.initWorkManager, the Gravatar Quick Editor's androidx.startup initializer (AndroidKeyStore + Tink self-test inside a ContentProvider, before Application.onCreate), and the pending-drafts receiver.

  • Gravatar Quick Editor: its startup initializer is removed from the manifest and initialized on demand from MeFragment. The class is Kotlin-internal, hence the lookup by name; startup-runtime's consumer rules keep all Initializers under R8.
  • WordPressDB: WordPress.wpDB is now a lazy synchronized getter (same delete-and-recreate fallback), warmed on Dispatchers.IO. The dead isWpDBInitialized check in WPLaunchActivity and its fatal_db_error string are removed.
  • WorkManager: initialized on demand via Configuration.Provider on WordPress; the periodic upload enqueue runs off main. InitializationRule joins that job so UploadWorkerTest can't race the test WorkManager swap.
  • NotificationsPendingDraftsReceiver: goAsync() + background thread.
  • Analytics: refreshMetadata, install/upgrade tracks and the notification-state check run in one ordered IO coroutine (Tracks attributes events from state the refresh sets). The media-sanitize site lookup moves into its worker thread.

Every new appScope.launch is try/caught because that scope has a plain Job.

Success metric is the Play ANR buckets above, not the Sentry issue, which will reopen regardless. The SystemJobService bucket should shrink, not vanish: WorkManager still initializes on main inside its own service on job-triggered starts.

Not addressed here: Sentry init, fluxc AccountStore constructor DB read, fluxc Volley queue creation, EncryptedLogging construction.

Testing instructions

Gravatar editor:

  1. Me → tap the avatar, then the profile fields
  • Editor opens both times; sign-in state persists between opens
    Note: If you get an "Oops" message here, it's unrelated to this PR - it's a known issue that has been reported to the Gravatar team.

Legacy DB:

  1. Cold-start the app, open Notifications and a site's Settings
  • Both load normally

Boot receiver:

  1. Create a local draft, reboot the emulator
  • No ANR/crash; adb logcat -s WordPress-NOTIFS shows "entering Pending Drafts Receiver from BOOT_COMPLETED"

WorkManager:

  1. Edit a post offline, go online, background the app
  • Upload completes; no "WorkManager is not initialized" in logcat

Play Console names the main-thread frames behind the Sentry "Background
ANR" bucket: WordPressDB open, WorkManager init, the Gravatar Quick
Editor startup initializer (AndroidKeyStore/Tink in a ContentProvider),
the BOOT_COMPLETED receiver, and analytics metadata DB reads.

- Remove the Gravatar androidx.startup initializer from the manifest
  and initialize it on demand from MeFragment
- Open WordPressDB lazily (synchronized getter) and warm it on IO
- Initialize WorkManager on demand via Configuration.Provider; enqueue
  the periodic upload work off the main thread
- Run NotificationsPendingDraftsReceiver work on a background thread
  with goAsync()
- Move refreshMetadata, install/upgrade tracks and the notification
  state check into one ordered IO coroutine; move the media-sanitize
  site lookup into its worker thread
- Drop the dead fatal_db_error launch check and its string
@dangermattic

dangermattic commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator
2 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@wpmobilebot

wpmobilebot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Project manifest changes for WordPress

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

--- ./build/reports/diff_manifest/WordPress/jetpackRelease/base_manifest.txt	2026-09-03 22:06:27.333777227 +0000
+++ ./build/reports/diff_manifest/WordPress/jetpackRelease/head_manifest.txt	2026-09-03 22:06:45.330945053 +0000
@@ -1316,9 +1316,6 @@
             android:authorities="com.jetpack.android.androidx-startup"
             android:exported="false" >
             <meta-data
-                android:name="com.gravatar.quickeditor.initializer.QuickEditorContainerInitializer"
-                android:value="androidx.startup" />
-            <meta-data
                 android:name="androidx.emoji2.text.EmojiCompatInitializer"
                 android:value="androidx.startup" />
             <meta-data
@@ -1401,16 +1398,6 @@
             android:name="org.wordpress.android.posttypes.CptHierarchicalPostListActivity"
             android:exported="false"
             android:theme="@style/Cpt.NoActionBar" />
-        <activity
-            android:name="com.gravatar.quickeditor.ui.GravatarQuickEditorActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-            android:theme="@style/GravatarQETheme.Transparent" />
-        <activity
-            android:name="com.gravatar.quickeditor.ui.oauth.GravatarOAuthActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-            android:theme="@style/GravatarQETheme.Transparent" />
 
         <provider
             android:name="com.gravatar.quickeditor.QuickEditorFileProvider"

Go to https://buildkite.com/automattic/wordpress-android/builds/28518/canvas?sid=01a0694c-e1de-4e72-9ebe-733882310219, click on the Artifacts tab and audit the files.

@wpmobilebot

wpmobilebot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Project manifest changes for WordPress

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

--- ./build/reports/diff_manifest/WordPress/wordpressRelease/base_manifest.txt	2026-09-03 22:06:25.674183293 +0000
+++ ./build/reports/diff_manifest/WordPress/wordpressRelease/head_manifest.txt	2026-09-03 22:06:44.327034249 +0000
@@ -1291,9 +1291,6 @@
             android:authorities="org.wordpress.android.androidx-startup"
             android:exported="false" >
             <meta-data
-                android:name="com.gravatar.quickeditor.initializer.QuickEditorContainerInitializer"
-                android:value="androidx.startup" />
-            <meta-data
                 android:name="androidx.emoji2.text.EmojiCompatInitializer"
                 android:value="androidx.startup" />
             <meta-data
@@ -1376,16 +1373,6 @@
             android:name="org.wordpress.android.posttypes.CptHierarchicalPostListActivity"
             android:exported="false"
             android:theme="@style/Cpt.NoActionBar" />
-        <activity
-            android:name="com.gravatar.quickeditor.ui.GravatarQuickEditorActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-            android:theme="@style/GravatarQETheme.Transparent" />
-        <activity
-            android:name="com.gravatar.quickeditor.ui.oauth.GravatarOAuthActivity"
-            android:exported="true"
-            android:launchMode="singleTask"
-            android:theme="@style/GravatarQETheme.Transparent" />
 
         <provider
             android:name="com.gravatar.quickeditor.QuickEditorFileProvider"

Go to https://buildkite.com/automattic/wordpress-android/builds/28518/canvas?sid=01a0694c-e1dd-4d3d-a4fc-e2f40aad9106, click on the Artifacts tab and audit the files.

@wpmobilebot

wpmobilebot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr23285-c88a98e
Build Number1498
Application IDcom.jetpack.android.prealpha
Commitc88a98e
Installation URL4r58t9quupego
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr23285-c88a98e
Build Number1498
Application IDorg.wordpress.android.prealpha
Commitc88a98e
Installation URL4dkavof11fdh8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🤖 Build Failure Analysis

This build has failures. Claude has analyzed them - check the build annotations for details.

- Make WordPress.wpDB a lazy property; collapse the duplicate catch and
  remember a failure so a broken database isn't deleted on every access
- Add a launchIo helper in AppInitializer so every background step is
  guarded the same way; use it for the media-sanitize step too
- Keep install/upgrade tracking on the main thread so it precedes
  APPLICATION_OPENED; only the metadata refresh moves to IO
- Catch Exception rather than Throwable in the boot receiver
- Move the Gravatar initializer lookup into GravatarQuickEditorInitializer,
  warm it from MeFragment off the main thread, and initialize it before any
  of the library's exported activities is created
Lint's UnsafeProtectedBroadcastReceiver only looks at onReceive itself, so
read the action there and pass it to the background handler.
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 1.44928% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.93%. Comparing base (b8157ff) to head (c88a98e).

Files with missing lines Patch % Lines
.../main/java/org/wordpress/android/AppInitializer.kt 0.00% 19 Missing ⚠️
.../receivers/NotificationsPendingDraftsReceiver.java 0.00% 18 Missing ⚠️
...s/src/main/java/org/wordpress/android/WordPress.kt 9.09% 10 Missing ⚠️
...ess/android/util/GravatarQuickEditorInitializer.kt 0.00% 7 Missing ⚠️
.../wordpress/android/datasets/SiteSettingsTable.java 0.00% 4 Missing ⚠️
...s/notification/local/LocalNotificationScheduler.kt 0.00% 3 Missing ⚠️
...va/org/wordpress/android/datasets/PeopleTable.java 0.00% 2 Missing ⚠️
...org/wordpress/android/datasets/PublicizeTable.java 0.00% 2 Missing ⚠️
...ordpress/android/datasets/UserSuggestionTable.java 0.00% 2 Missing ⚠️
...wordpress/android/datasets/NotificationsTable.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            trunk   #23285      +/-   ##
==========================================
- Coverage   37.93%   37.93%   -0.01%     
==========================================
  Files        2352     2353       +1     
  Lines      128925   128935      +10     
  Branches    17940    17937       -3     
==========================================
+ Hits        48907    48908       +1     
- Misses      76012    76021       +9     
  Partials     4006     4006              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

nbradbury and others added 5 commits September 3, 2026 11:22
- Restore trunk's synchronous analytics init; moving the metadata refresh
  off main kept surfacing attribution gaps for background-started
  processes, so that stays as-is until the tracker persists the username
- MeFragment: replace the warm-up/guard pair with one Deferred started
  in onCreate; retry the init once on tap if the warm-up failed; skip
  showing if the view is no longer started
- Catch LinkageError in GravatarQuickEditorInitializer; add an explicit
  keep rule so the by-name lookup doesn't depend on library rules
- Remove the two unused exported Gravatar activities from the manifest
  instead of initializing the container for them at runtime
- LocalNotificationScheduler resolves WorkManager lazily so injecting it
  no longer initializes WorkManager on the main thread
- launchIo uses the injected IO dispatcher; drop the WordPressDB
  recreate flag; keep the pending-drafts receiver's behaviour unchanged
@nbradbury
nbradbury marked this pull request as ready for review September 3, 2026 21:46
@nbradbury
nbradbury requested a review from a team as a code owner September 3, 2026 21:46
@nbradbury
nbradbury requested review from adalpari and removed request for a team September 3, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants