[r8] Enable private-member obfuscation and optimization by default - #12668
[r8] Enable private-member obfuscation and optimization by default#12668jonathanpeppers wants to merge 4 commits into
Conversation
Preserve Java class names and public/protected member names while allowing R8 to obfuscate private and package-private members. Keep _AndroidR8DontObfuscate as a private escape hatch that only restores the previous global -dontobfuscate behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf67f43d-2eef-46ac-9d52-e9a677671fc8
There was a problem hiding this comment.
🟡 Changes recommended
The new device test can leave apps installed on failure paths, which risks flakiness and cross-test interference in device CI runs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR changes the default R8 behavior to allow obfuscation of private/package-private Java implementation details while keeping Java class names and public/protected member names stable for JNI compatibility, with a private MSBuild escape hatch to restore the previous global -dontobfuscate behavior.
Changes:
- Add a new selective-obfuscation default by generating base ProGuard/R8 rules at build time (and wiring an internal
$(_AndroidR8DontObfuscate)toggle). - Update embedded ProGuard config resources to no longer globally disable obfuscation, and add a NativeAOT-specific keep rule for
mono.android.Runtimestartup fields. - Add device integration tests validating mapping + DEX output + runtime execution for CoreCLR and NativeAOT.
File summaries
| File | Description |
|---|---|
| tests/MSBuildDeviceIntegration/Tests/R8ObfuscationTests.cs | Adds device test coverage verifying selective obfuscation via mapping/DEX inspection and a runtime probe. |
| src/Xamarin.Android.Build.Tasks/Xamarin.Android.D8.targets | Plumbs a private MSBuild escape hatch property into the R8 task invocation. |
| src/Xamarin.Android.Build.Tasks/Tasks/R8.cs | Implements default selective-obfuscation ProGuard directives generation and supports DontObfuscate. |
| src/Xamarin.Android.Build.Tasks/Resources/proguard_xamarin.cfg | Removes the global -dontobfuscate default from the common R8 config resource. |
| src/Xamarin.Android.Build.Tasks/Resources/proguard_trimmable_nativeaot.cfg | Removes -dontobfuscate and adds a keep rule for mono.android.Runtime fields needed at NativeAOT startup. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Lite
Track successful installation and attempt uninstall from a finally block so assertion failures do not leave packages on the test device. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf67f43d-2eef-46ac-9d52-e9a677671fc8
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf67f43d-2eef-46ac-9d52-e9a677671fc8
|
/review |
|
✅ Android PR Reviewer completed successfully!
|
There was a problem hiding this comment.
The selective R8 rules preserve class and JNI-visible public/protected member names while allowing private implementation details to be renamed, and the runtime-specific keep configuration is consistent with the native field lookups. The new device coverage exercises both CoreCLR and NativeAOT, but its shared logcat marker is not isolated between parameterized cases and can produce a false pass; please address the inline testing comment. CI is green.
Generated by Android PR Reviewer for #12668 · gpt56 · 125.5 AIC · ⌖ 9.02 AIC · ⊞ 25.7K
Comment /review to run again
| AssertDexDoesNotContainMethod (dexFiles, "packagePrivateEntry"); | ||
| AssertDexDoesNotContainMethod (dexFiles, "privateEntry"); | ||
|
|
||
| RunProjectAndAssert (proj, builder, doNotCleanupOnUpdate: true); |
There was a problem hiding this comment.
🤖 SuccessMarker runtime-specific. MonitorAdbLogcat() first scans all buffered output via logcat -d, and both TestCases emit the same marker, so the NativeAOT case can satisfy line 134 with the earlier CoreCLR run even if the NativeAOT app never reaches this code.
Rule: Deterministic test isolation
Use Android's optimized default R8 configuration unless the private _AndroidR8DontOptimize escape hatch is enabled. Ship both configurations and preserve ApplicationRegistration, which is loaded by name from managed code. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf67f43d-2eef-46ac-9d52-e9a677671fc8


Description
Enable R8 optimization and selective obfuscation by default while preserving JNI-sensitive Java names without rewriting managed IL.
The selective obfuscation defaults keep Java class names and public/protected member names stable while allowing private and package-private members to be renamed:
The SDK now ships both Android default R8 configurations and selects
proguard-android-optimize.txtby default. Runtime rules explicitly preservenet.dot.android.ApplicationRegistration, which managed code loads by name. NativeAOT also preserves the package-privatejava.lang.Classfields onmono.android.Runtimebecause native hosts resolve them by literal name during startup.Escape hatches
$(_AndroidR8DontObfuscate)and$(_AndroidR8DontOptimize)are private escape hatches only. Setting either totruerestores the corresponding previous global behavior (-dontobfuscateor the legacy non-optimizing Android configuration). They are not supported public build properties or alternative configuration modes, and both remain blank by default.Tests
R8ObfuscationTestsdevice coverage for CoreCLR and NativeAOT using anAndroidJavaSourceprobe. It verifies stable class/public/protected names, renamed private/package-private methods, and successful JNI execution.