Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

@capacitor/camera - Android: `BitmapFactory.decodeFile` without `inSampleSize` decodes the full-resolution bitmap in `IonCameraFlow`

Open
#2,599 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
android, java, kotlin
Domain
mobile

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

platform: android

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:

  1. npm i @capacitor/[email protected] && npx cap sync android
  2. Call Camera.pickImages({ quality: 90, width: 2400, height: 2400 }) and choose three
    12 MP photos (4000×3000).
  3. 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/camera 8.2.4 (and 8.2.2 — I unpacked both npm tarballs and compared; the
    decode is identical)
  • io.ionic.libs:ioncamera-android 1.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.java uses BitmapFactory.decodeStream without inSampleSize
    in processPickedImage / 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 / processPickedImages
  • io.ionic.libs.ioncameralib.view.IONCAMRImageEditorView.decodeSampledBitmapFromUri / loadBitmapSafely
  • io.ionic.libs.ioncameralib.helper.IONCAMRImageHelper.getBitmapForInputStream / decodeFile
  • io.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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ionic-team/capacitor-plugins

All issues in ionic-team/capacitor-plugins

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.