@capacitor/camera - Android: `BitmapFactory.decodeFile` without `inSampleSize` decodes the full-resolution bitmap in `IonCameraFlow`
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 76/100
Research direction
Start in IonCameraFlow.kt at handleMediaResult, including the lines around 726 and 806, then inspect LegacyCameraFlow.java at processPickedImage and processPickedImages. Compare these decode calls with IONCAMRImageEditorView.decodeSampledBitmapFromUri and run the reported multi-image reproduction. Done means image validation no longer allocates full-resolution pixels unnecessarily and requested resizing still behaves correctly.
Written by the indexing model from the issue text.
Description
Bug Report
Plugin(s)
@capacitor/camera 8.2.4 (also 8.2.2), @capacitor/core 8.5.2, Android targetSdk 36
Capacitor Version
$ npx cap doctor
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 8.5.2
@capacitor/core: 8.5.2
@capacitor/android: 8.5.2
@capacitor/ios: 8.5.2
Installed Dependencies:
@capacitor/cli: 8.5.2
@capacitor/android: 8.5.2
@capacitor/core: 8.5.2
@capacitor/ios: 8.5.2
[success] Android looking great! 👌
Platform(s)
Android
Current Behavior
IonCameraFlow.kt handleMediaResult calls BitmapFactory.decodeFile(mediaResult.uri) (line ~726, and again ~806) with no BitmapFactory.Options. On a 12 MP photo that allocates roughly 48 MB as ARGB_8888. LegacyCameraFlow.java does the same with decodeStream.
Expected Behavior
The decode should not allocate pixel memory it never uses.
In both places the decoded Bitmap is used only as a null check — "is this file an
image?". The very next line, ImageUtils.getExifData(context, bitmap, uri), reads the EXIF
from the uri and ignores the bitmap argument entirely. So a bounds-only decode
(inJustDecodeBounds = true) answers the same question for a few bytes instead of ~48 MB.
Where a bitmap really is needed later, BitmapFactory.Options.inSampleSize should be
computed from the requested width/height before the full decode — which the library
already knows how to do: IONCAMRImageEditorView.decodeSampledBitmapFromUri does exactly
that. These call sites bypass it.
Code Reproduction
No special app is needed — any Capacitor app with the plugin reproduces it:
npm i @capacitor/[email protected] && npx cap sync android- Call
Camera.pickImages({ quality: 90, width: 2400, height: 2400 })and choose three
12 MP photos (4000×3000). - Watch the heap in Android Studio's Memory Profiler, or
adb shell dumpsys meminfo <package> | grep -i "Native Heap"before and during the pick.
Each picked photo shows a transient allocation of roughly width × height × 4 bytes —
about 48 MB per 12 MP image — before any resizing happens. Passing a smaller width/
height does not prevent it: the full decode happens first.
A breakpoint on IonCameraFlow.handleMediaResult shows the bitmap local going unused
after the null check.
Other Technical Details
@capacitor/camera8.2.4 (and 8.2.2 — I unpacked both npm tarballs and compared; the
decode is identical)io.ionic.libs:ioncamera-android1.0.2, the newest on Maven Central, which the plugin
pins- Android targetSdk 36, AGP 8.13.0, Gradle 8.14.3
- Affected lines:
IonCameraFlow.kt~726 (handleMediaResult) and ~806 (the multi-pick
path);LegacyCameraFlow.javausesBitmapFactory.decodeStreamwithoutinSampleSize
inprocessPickedImage/processPickedImages
Additional Context
Google Play reports this. The Play Console flags it as a bitmap-downsampling
recommendation with the obfuscated symbols w4.k.c, w4.k.l, w4.m.d, and names these
entry points in the deprecated-API section:
com.capacitorjs.plugins.camera.LegacyCameraFlow.processPickedImage/processPickedImagesio.ionic.libs.ioncameralib.view.IONCAMRImageEditorView.decodeSampledBitmapFromUri/loadBitmapSafelyio.ionic.libs.ioncameralib.helper.IONCAMRImageHelper.getBitmapForInputStream/decodeFileio.ionic.libs.ioncameralib.helper.IONCAMRMediaHelper.getImageResolution
This has a deadline. Google's August 2026 app-quality post makes bitmap memory a
bad-behaviour threshold enforced from February 2027, alongside anonymous RSS + swap.
Apps over the threshold can lose visibility.
Why it bites us. Our app scans multi-page recipes, so a session decodes one full-size
bitmap per page. On a low-RAM device the WebView renderer is killed mid-scan.
Workaround we ship, via patch-package on IonCameraFlow.kt — happy to send this as a PR:
// was: val bitmap = BitmapFactory.decodeFile(mediaResult.uri)
// if (bitmap == null) { sendError(IONCAMRError.PROCESS_IMAGE_ERROR); return }
val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeFile(mediaResult.uri, bounds)
if (bounds.outWidth <= 0 || bounds.outHeight <= 0) {
sendError(IONCAMRError.PROCESS_IMAGE_ERROR)
return
}
val exif = ImageUtils.getExifData(context, null, uri)
Verified on an Android emulator: camera capture, gallery pick and a three-page recipe scan
of 4000×3000 JPEGs all behave identically, with no error and no crash.
Note ioncamera-android is shipped as an AAR, so the IONCAMR* call sites above cannot be
patched this way — only an upstream fix reaches them.
- Dominant language
- Java
- Stars
- 678
- Forks
- 688
- Avg merge
- 5d 11h
- Merged PRs (30d)
- 3
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ionic-team/capacitor-plugins
-
[@capacitor/app] iOS: 'Expression implicitly coerced from String? to Any' warning in getAppLanguageOpentriage
Difficulty 1/5 Under an hour Newbie friendliness 88/100
ionic-team/capacitor-plugins#2604 ·
-
platform: ios
Difficulty 4/5 3-5 days Newbie friendliness 58/100
ionic-team/capacitor-plugins#2603 · 1 comment ·
-
platform: ios
Difficulty 3/5 1-2 days Newbie friendliness 68/100
ionic-team/capacitor-plugins#2595 · 1 reaction ·
-
platform: android triage
Difficulty 4/5 3-5 days Newbie friendliness 48/100
ionic-team/capacitor-plugins#2593 ·
-
platform: ios
Difficulty 3/5 1-2 days Newbie friendliness 68/100
ionic-team/capacitor-plugins#2565 ·
All issues in ionic-team/capacitor-plugins
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
apache/arrow-java#1311 ·
Maintainers usually reply within 2 days
-
bug triage
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
security
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
IBM/networking-java-sdk#204 ·
-
bug Technical Debt
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
avniproject/avni-server#1080 ·