Skip to content

Obfuscate the Android DEX with R8 - #2

Merged
awatertrevi merged 1 commit into
mainfrom
obfuscate-dex-with-r8
Sep 17, 2026
Merged

awatertrevi merged 1 commit into
mainfrom
obfuscate-dex-with-r8

Conversation

@awatertrevi

Copy link
Copy Markdown
Member

Same change as lojack-connect#2, applied to the Android head here.

Play Console's DEX code optimization report scores obfuscation at ~1% on a stock .NET MAUI build, under its 25% threshold. Two things are in the way:

  1. .NET for Android runs no DEX shrinker at all by default.
  2. Even with AndroidLinkTool=r8, the ProGuard config the SDK generates hardcodes -dontobfuscate — and nothing later in the config list can undo that flag.

So _AndroidObfuscateDex drops the SDK's proguard_xamarin.cfg from R8's --pg-conf list and substitutes Xamarin.Neo4j.Android/proguard_xamarin.cfg (the same file minus that one line), plus a generated file carrying the -printmapping/-keepattributes tail the SDK would have appended.

Result: 49.8% of DEX classes renamed.

Keep rules

Only library-internal classes are renamed — the trimmer emits a -keep for every Java type the managed bindings reference, aapt2 one for every class named in a layout or the manifest, and each Android Callable Wrapper gets its own. Xamarin.Neo4j.Android/proguard.cfg covers what those miss, found by running the obfuscated build on lojack-connect:

Symptom Cause Fix
ClassNotFoundException: net.dot.android.ApplicationRegistration Resolved by name from native code; nothing in the DEX references it keep rule
NoSuchFieldError on Lifecycle$State.DESTROYED, Insets.left The trimmer's generated keeps cover bound methods, not fields keep public/protected fields
InflateException: FitWindowsFrameLayout aapt2's keep rules are a FileWrite added inside _CreateBaseApkWithAapt2; an incremental Release build that skips the resource link loses the item and has IncrementalClean delete the file cache a copy outside FileWrites, hard-error if even that is missing

Verification

Pixel 9 emulator (API 36), obfuscated Release APK: launch, connection list, Add Connection form (scheme picker, password toggle), and a Bolt connection attempt that exercises the network/TLS stack and surfaces the driver's own error dialog. No fatals, no JNI errors.

Notes

  • mapping.txt is embedded in the AAB (BUNDLE-METADATA/com.android.tools.build.obfuscation/proguard.map), so Play retraces Java stacks itself. The deploy workflow uploads the AAB straight to the internal track, so nothing there needed changing.
  • Managed (C#) stack traces are unaffected; R8 only touches DEX.
  • Local Release builds must be clean (rm -rf Xamarin.Neo4j.Android/obj/Release); the new error message says so if it trips.

🤖 Generated with Claude Code

Mirrors lojack-connect. .NET for Android runs no DEX shrinker by default, and
even with AndroidLinkTool=r8 the ProGuard config the SDK generates hardcodes
-dontobfuscate -- a flag nothing later in the config list can undo -- so Play
Console's "DEX code optimization" report scores obfuscation at ~1%.

_AndroidObfuscateDex drops the SDK's proguard_xamarin.cfg from R8's --pg-conf
list and substitutes a copy without that line, regenerating the
-printmapping/-keepattributes tail the SDK would have appended (absolute path:
R8 resolves a relative -printmapping against the config file's own directory).
49.8% of DEX classes are now renamed.

proguard.cfg carries the keeps the generated rules miss: classes the .NET
runtime resolves by name from native code (net.dot.android.**), and public and
protected fields, which the trimmer covers for methods only. The target also
caches aapt2's keep rules outside FileWrites -- the SDK adds them to
@(ProguardConfiguration) inside _CreateBaseApkWithAapt2 and registers the file
as a FileWrite, so an incremental Release build that skips the resource link
loses them and ships an APK that dies inflating FitWindowsFrameLayout.

Verified on a Pixel 9 emulator (API 36) with the obfuscated Release APK:
launch, connection list, Add Connection form, and a Bolt connection attempt
that exercises the network/TLS stack and surfaces the driver's error dialog.

The AAB carries mapping.txt, so Play retraces Java stacks itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@awatertrevi
awatertrevi merged commit 1c319e6 into main Sep 17, 2026
1 check passed
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.

1 participant