Fix Android 'Already closed' crash on receipt PDF render#94894
Fix Android 'Already closed' crash on receipt PDF render#94894wildan-m wants to merge 6 commits into
Conversation
…crash Repoint react-native-pdf's pdfiumandroid dependency to a JitPack fork whose PdfDocument.openPage/openTextPage route through handleAlreadyClosed instead of an unconditional check(!isClosed). The IGNORE behavior patch 002 already configures now reaches the exact call that crashed on the renderer thread when a receipt render raced with PDF view teardown.
…ready-closed-render-crash
|
|
A native Android build surfaced a duplicate-class failure (:checkDuplicateClasses): AndroidPdfViewer pulls io.legere:pdfiumandroid transitively, which carries the same io.legere.pdfiumandroid.* classes as the fork under a different coordinate. Exclude the transitive copy and depend on the fork directly so a single coordinate provides those classes. Verified on a standalone NewDot Android build (BUILD SUCCESSFUL, checkDuplicateClasses passes; dependencyInsight shows the fork resolved and io.legere:pdfiumandroid absent).
|
@mountiny @eVoloshchak — quick question. It needs a small custom build of the PDF library, and I'd like to host it wherever you're comfortable. Why we need it: the crash comes from one method in the Where should it live? It's temporarily on my personal fork ( |
|
You can make patch to the library cant you |
|
@mountiny good idea, and that's actually what we already do where we can — The problem is the bug is one method inside |
|
Ok I think given all the options our fork with the fix is the best approach Can you point me to the right repo and version that we need to fork? |
|
@mountiny fork https://github.com/wildan-m/PdfiumAndroidKt (a fork of the official |
|
Tracked the Expensify org fork request in an internal Ring Zero issue: https://github.com/Expensify/Expensify/issues/655446 @wildan-m @mountiny — once Ring Zero creates |
|
@mountiny can't acess the link. is it created? |
|
Fork is here https://github.com/Expensify/PdfiumAndroidKt |
|
thread to discuss here https://expensify.slack.com/archives/C01GTK53T8Q/p1783261052206059 @eVoloshchak |
…ready-closed-render-crash
Move the PdfiumAndroidKt fork dependency off the personal com.github.wildan-m coordinate onto the Expensify org: com.github.Expensify:PdfiumAndroidKt:e6c06c2905d1adf8b76f28c7440a2006033ca4c8 Pinned by commit rather than the expensify-already-closed-fix-v1 tag because JitPack cached a transient failed build of that tag (an UnknownHostException on services.gradle.org while fetching the Gradle wrapper). The commit coordinate is a fresh JitPack version key that builds green (BUILD SUCCESSFUL, pom+aar 200) and pins the exact same immutable commit the tag points to. Can switch to the tag once an Expensify JitPack admin rebuilds it.
Explanation of Change
On Android, the app crashes (
java.lang.IllegalStateException: Already closed, SentryAPP-1VG) when a PDF is closed while it's still rendering — for example navigating away from a receipt mid-render. A previous fix (patch002) already told thepdfiumandroidlibrary to ignore this case instead of crashing, but two methods on the render path —PdfDocument.openPage()andopenTextPage()— don't respect that setting and still throw when the document is closed. That's the one path that keeps crashing.There's no published version of the library that fixes this and still works with our PDF viewer — the stable line is stuck on the buggy version, and the newer line changes the API and is only in alpha. So this patch points
react-native-pdfat a small fork of the current version where those two methods honor the ignore setting like the rest of the library — a Kotlin-only change, with the native code untouched and no change to how PDFs render. It also drops the older copy of the library that the PDF viewer pulls in alongside the fork, since otherwise the build fails with two copies of the same classes. The fork follows the same pattern as the other forksreact-native-pdfalready uses (zacharee,IvanIhnatsiuk) and can be dropped once an official fixed release is usable.Fixed Issues
$ #93839
PROPOSAL: #93839 (comment)
Tests
openPage(0)on a closedPdfDocumentreturns instead of throwing.IllegalStateException: Already closed at PdfDocument.openPage— the exactAPP-1VGsignature.📹 Recording of both runs (fail → pass):
Run it — re-runnable (clones if absent, otherwise reuses and resets the existing clone). Needs
ANDROID_HOMEset and an AVD; it boots a headless emulator if none is connected (then shuts it down), accepts SDK licenses, and repoints the fork's pinned NDK to whatever you have installed (the native part is a thin JNI wrapper over the prebuiltlibpdfium.so, so any recent NDK works).runpasses with the fix; the last line reverts only the fix, re-runs (fails withAlready closed), then restores it:run(with the fix) → PASSES (BUILD SUCCESSFUL).IllegalStateException: Already closed at PdfDocument.openPage, then restores the fix.The definitive production signal is the
APP-1VGcrash rate in Sentry after release.Offline tests
This change does not affect offline behavior. It only changes how the native PDF library handles a render that races view teardown; an already-downloaded PDF renders identically offline.
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)Avatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Built and run on a standalone NewDot Android build (Pixel 8 / API 34). Deterministic before/after via an instrumentation test that forces
openPage()on a closed document: baselineio.legere:pdfiumandroid:1.0.35FAILS withjava.lang.IllegalStateException: Already closed at io.legere.pdfiumandroid.PdfDocument.openPage(PdfDocument.kt:144)(the exactAPP-1VGframe), and the fork PASSES. The fix also builds cleanly (checkDuplicateClassespasses,dependencyInsightconfirms the fork resolved andio.legere:pdfiumandroidexcluded) and shows no PDF-rendering regression (a multi-page receipt renders and scrolls through all pages). See Verification for details; the production signal is theAPP-1VGcrash rate in Sentry post-deploy.Android: mWeb Chrome
N/A — Android native PDF library change; mWeb uses a different PDF renderer.
iOS: Native
N/A — Android-only patch (
react-native-pdfAndroidbuild.gradle).iOS: mWeb Safari
N/A — Android-only patch.
MacOS: Chrome / Safari
N/A — Android-only patch; web uses a different PDF renderer.