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

Aperta
#2,599 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
android, java, kotlin
Ambito
mobile

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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/camera@8.2.4 && 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.


Lingua principale
Java
Stelle
678
Fork
688
Merge medio
8g 18h
PR unite (30g)
1

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di ionic-team/capacitor-plugins

Tutte le issue di ionic-team/capacitor-plugins

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.