
androidx.pdf beta01 runs your Application.onCreate a second time, in an isolated process, and the PDF silently never opens
androidx.pdf's isolated PDF service runs Application.onCreate too. Make Process.isIsolated() the first statement of onCreate, return early, and pin that position with a test so nobody moves it.
if (Process.isIsolated()) return is the first line of Application.onCreate in HWP Viewer SugarDoc, our Android viewer for Korean word-processor files and PDFs. On October 5, 2026, a release build without that line could not open a PDF on a Galaxy S21 running Android 15. The PDF service's isolated process ran our onCreate, crashed on SharedPreferences, and the app got no error callback for the roughly 6.5 seconds of log we saved. With the guard, the same phone opened a 5-page PDF in 81 ms.
Sources: the V8 section of the M0 spike report in SugarMount's app repository (sugar-utils, dated 2026-10-05), the two S21 logcat files saved during that spike, and SugarDocApp.kt and ViewerArchitectureTest.kt as of commit ca1ade4 (2026-10-08). Checked on October 21, 2026.
Contents (7)
The isolated process creates your Application too
androidx.pdf 1.0.0-beta01 does its PDF parsing in a service called PdfDocumentServiceImpl, declared with isolatedProcess. Android starts that process with your Application class and calls its onCreate. An isolated process has no access to your app's storage, so storage work in onCreate kills it. We measured SharedPreferences; the code comment in our app also names DataStore and Firebase.
Our spike app (Compose PdfViewer plus SandboxedPdfLoader, an R8-minified release build) wrote a SharedPreferences value in onCreate on purpose. This is the unguarded run:
02:35:47.092 I M0V8: APP_ONCREATE pid=1678 isolated=true guard=false proc=kr.sugar.m0v8:androidx.pdf.service.PdfDocumentServiceImpl
02:35:47.093 E M0V8: APP_INIT failed isolated=true: java.lang.IllegalStateException: SharedPreferences cannot be accessed if UserManager is not available. (e.g. from inside an isolated process)
02:35:47.093 E AndroidRuntime: FATAL EXCEPTION: main
02:35:47.110 I ActivityManager: Process kr.sugar.m0v8:androidx.pdf.service.PdfDocumentServiceImpl (pid 1678) has died
The app process stayed alive, so no crash dialog appeared. Between the service dying and the last log line at 02:35:53.689, the "document opened" line (V8_OPEN) never showed up, and nothing told the app the open had failed. The guarded build, run on the same phone about two minutes earlier, let the isolated process return from onCreate at once and opened a 466,529-byte, 5-page PDF in 81 ms.

How it was found, October 4 to October 8
This order comes only from the docs and git. There are no intermediate commits: everything below landed in one commit on October 8.
- Oct 4: the design cross-check document noted that the PDF sandbox service is an isolated process and that Application.onCreate runs there too, tagged "verified/estimated". The implementation plan written the same day added spike V8 to test it.
- Oct 5, 02:33: guarded release build on the S21. The isolated process logged
isolated=true guard=true, and the PDF opened. - Oct 5, 02:35: unguarded build. The isolated process died as shown above, and the PDF never opened.
- Oct 5 (spike report): a verification agent recomputed the measuring agent's numbers. This is where "8 seconds" became "about 6.5 seconds".
- Oct 5 (M1 results): the guard went into the real app, with a record that deleting it makes the test fail.
- Oct 8, 08:56: commit ca1ade4 shipped the guard and the architecture test together with the app's milestones M1 to M5.
The design cross-check flagged it first, and the spike then reproduced the failure on purpose. (The plan was to install the spike on a Galaxy S20 as well, but another session's app was using that phone, so it only read system properties there.)
A unit test keeps the guard as the first statement
In SugarDocApp.kt the guard is line 17, the first statement of onCreate. The comment above it says it must stay first and names ViewerArchitectureTest as the rule that enforces it.
override fun onCreate() {
if (Process.isIsolated()) return
super.onCreate()
container = AppContainer(this)
}
The test reads the source file and checks that the first statement after override fun onCreate() is exactly that string. It also rejects code that runs before onCreate, because that runs in the isolated process as well: member properties with initializers, init blocks, a companion object and attachBaseContext. Only lateinit and by lazy members are allowed. A second test feeds the detector a sample with five such lines and requires exactly five hits, so the detector itself cannot go quiet.
If your app uses androidx.pdf, put this guard first in onCreate and pin it with a test. Code review will not hold it there. Initialization code piles up in onCreate over time, and most of it touches storage. We checked the test by deleting the guard and watching it fail; the method is in 3,066 green tests hid a line that breaks opening the database: checking tests by deleting the guard.
The measuring agent said 8 seconds; the log said 6.5
Two AI coding agents split spike V8. A measuring agent ran the phone and reported. A separate verification agent reopened the saved evidence files and recomputed every number. The spike report's header says it plainly: the numbers in it are the verification agent's corrected values.
The measuring agent reported that the unguarded build sat unresponsive for "8 seconds". The verification agent went back to the logcat and changed it to "about 6.5 seconds". That 6.5 is how long the saved log runs, and the PDF never opened inside it. Recounting while writing this note: from the process death at 02:35:47.110 to the last line is 6.6 seconds.
The same section lists seven corrections. Open times went from 183/99 ms to 81/59 ms, and search times from 95 to 211 ms to 286/64 ms. The scroll evidence was thrown out because the first-page and last-page screenshots had the same md5. The agent that was wrong and the agent that fixed it were both AI; there is no record of a person correcting these numbers. Measurement and verification go to different agents, and only corrected values reach the document.
The isolated service had other ways to die
The guard covers one route: dying inside onCreate. The isolated service can die for other reasons, so the reader's ReaderController puts an 8-second timeout on openDocument (PDF_OPEN_TIMEOUT_MS = 8_000L, counted only while the reader is on screen) and ends on an error screen. In the M4 checks we killed the service with am crash during openDocument, 3 runs on each of 2 devices. Each run either showed E14 (try again) or reconnected and drew the first page, all within 0.5 to 2.4 seconds.
The nastier route came after a document had opened. When the service died, androidx.pdf showed a Material error Snackbar styled with ThemeOverlay.AppCompat.Dark. It read attributes such as ?attr/colorAccent that our platform theme does not define, threw InflateException, and this time took down the app process itself. Adding 7 AppCompat color attributes to the reader theme fixed it, with the parent theme still a platform theme. After the fix, 9 runs on the S21 had zero app crashes.
We added no R8 keep rules and the release build still worked. The APK grew from 1,263,741 to 2,640,995 bytes, +1,377,254 bytes (dex +666,472, arsc +532,888, res +134,546). Measurements for the same app's HWP engine are in Building an Android HWP viewer: rhwp in a WebView, measured on 222 documents.

If I did it again
I would not measure "no response" by the length of a log file. Two log lines in the spike app, one when openDocument is called and one when the timeout fires, would have left nothing to argue about between 8 and 6.5 seconds.
I would also build a way to reproduce the no-callback 8-second timeout on a phone first. Today that path is covered only by JVM tests, because a release build gave us no way to freeze the isolated process (SIGSTOP). I would check transitive dependencies in the spike, too. The V8 evidence had no dependency tree, so that item stayed unconfirmed; we only learned that androidx.pdf brings in appcompat 1.7.0, fragment 1.5.4 and material 1.13.0 later that day, while building the real app in M1. Next spike, the output of ./gradlew :app:dependencies goes into the evidence folder from the start.
FAQ
Why does androidx.pdf fail to open a PDF without any error?
Usually because its isolated service process died. Filter logcat by the process name :androidx.pdf.service.PdfDocumentServiceImpl and look for FATAL EXCEPTION. If Application.onCreate touches storage, only that process dies, and the app gets no callback.
Why does SharedPreferences throw IllegalStateException in an isolated process?
An isolated process cannot use UserManager, so SharedPreferences cannot open. The message we saw was "SharedPreferences cannot be accessed if UserManager is not available. (e.g. from inside an isolated process)". Return early from Application.onCreate when Process.isIsolated() is true.
Does androidx.pdf beta01 need extra R8 keep rules?
Not in our app. On 2026-10-05 an R8 release build with no added keep rules opened, searched and selected text on a Galaxy S21. We also confirmed that four of its AARs ship no consumer rules file.
This note is written from values SugarMount measured itself; an AI coding agent was used to draft it.
More AX notes
- Pre-gzipping Android assets, measured: zero APK savings, and .pack.gz became .pack with a FileNotFoundException
- A market:// link opens a store chooser on SKT-sold Samsung phones: set the Play package first
- A splash that never moved on: addOnCompleteListener(this) and onStop
- "6.7" < "6.11" is false: comparing Remote Config versions as strings froze content updates
- A reverse-construction generator and the gate behind 1,100 arrow-puzzle levels
- Daily users rose after the redesign while organic D1 retention fell from 15.2% to 11.2%