Skip to content

fix(android): guard getLongVersionCode() for API < 28 to prevent launch crash#219

Open
FeodorFitsner wants to merge 1 commit into
mainfrom
fix/android-old-version-crash
Open

fix(android): guard getLongVersionCode() for API < 28 to prevent launch crash#219
FeodorFitsner wants to merge 1 commit into
mainfrom
fix/android-old-version-crash

Conversation

@FeodorFitsner

Copy link
Copy Markdown
Contributor

Problem

Flet/serious_python apps crash on launch on Android 8.1 and below (API < 28), while working on Android 9+ (API 28+). Reported in flet-dev/flet#6619 as a regression that started in Flet 0.81.0.

Root cause

AndroidPlugin.getAppVersion — which serious_python calls on every app startup (to build the site-packages cache-invalidation key, before Python starts) — calls PackageInfo.getLongVersionCode(), which is API 28+, unconditionally.

R8 (release builds) outlines API-28 calls into a synthetic class (AndroidPlugin$$ExternalSyntheticApiModelOutline0) and horizontally merges into it the API-28 outlines from other code — notably Flutter 3.41's new io.flutter.embedding.engine.image.ImageDecoderDefaultImpl (which uses android.graphics.ImageDecoder). Invoking the version-code outline on API < 28 forces that merged class to load/verify, and its references to android.graphics.ImageDecoder$OnHeaderDecodedListener (API 28) fail to resolve:

java.lang.NoClassDefFoundError: Failed resolution of: Landroid/graphics/ImageDecoder$OnHeaderDecodedListener;
  at com.flet.serious_python_android.AndroidPlugin.onMethodCall(...)
Caused by: java.lang.ClassNotFoundException: android.graphics.ImageDecoder$OnHeaderDecodedListener

→ uncaught on the platform-channel handler → app crash.

This explains the exact regression boundary: Flet 0.80.5 shipped Flutter 3.38.7 (no ImageDecoderDefaultImpl); Flet 0.81.0 bumped to Flutter 3.41.2, which added it. The unguarded getLongVersionCode() was always present but only became fatal once Flutter contributed an ImageDecoder outline for R8 to merge with.

Fix

Guard the call with Build.VERSION.SDK_INT and fall back to the deprecated versionCode int field on API < 28. The API-26 path then never invokes the outlined method, so the merged synthetic class is never loaded on old devices (it's only loaded lazily on first invocation).

Verification

Reproduced and verified on Android emulators with a stock flet create counter app (Flet 0.85.3 / Flutter 3.41.7, release APK):

Device Before After
Android 8.0 (API 26) ❌ crash on launch ✅ runs (counter renders, CPython loads)
Android 10 (API 29) ✅ runs ✅ runs

Deobfuscated the release mapping.txt to confirm the merged outline class contained both getLongVersionCode(PackageInfo) and the ImageDecoder methods.

Fixes flet-dev/flet#6619.

…ch crash

getAppVersion runs on every startup and called PackageInfo.getLongVersionCode()
(API 28+) unconditionally. R8 outlines the call into a synthetic class it may
merge with other API 28+ outlines (e.g. Flutter 3.41's ImageDecoder-based image
decoder); invoking that class on API < 28 fails verification with
NoClassDefFoundError (android.graphics.ImageDecoder$OnHeaderDecodedListener) and
crashes the app on launch on Android 8.1 and below.

Guard the call with Build.VERSION.SDK_INT and fall back to the deprecated
versionCode int field on older devices.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Application crashes (only Android 8.1 and below and flet 0.81.0 and later)

1 participant