mirror of
https://github.com/awatertrevi/xamarin-neo4j.git
synced 2026-09-22 09:05:29 +00:00
Obfuscate the Android DEX with R8
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>
This commit is contained in:
@@ -53,4 +53,56 @@
|
||||
<ProjectReference Include="..\Xamarin.Neo4j\Xamarin.Neo4j.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Android Release: shrink and obfuscate the Java/DEX half of the app with
|
||||
R8. .NET for Android runs no DEX shrinker by default, so Play Console's
|
||||
"DEX code optimization" report scores obfuscation at ~1%. The trimmer emits
|
||||
a keep rule for every Java type the managed bindings reference
|
||||
(obj/.../proguard/proguard_project_references.cfg), aapt2 one for every
|
||||
class named in a layout, and each Android Callable Wrapper gets its own,
|
||||
so only library-internal classes are renamed. -->
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<AndroidLinkTool>r8</AndroidLinkTool>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProguardConfiguration Include="proguard.cfg" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Swap the SDK's generated proguard_xamarin.cfg (which hardcodes
|
||||
-dontobfuscate, a flag nothing later in the config list can undo) for our
|
||||
copy without it, and regenerate the -printmapping/-keepattributes tail the
|
||||
SDK appends to that same file so crash stacks stay retraceable. -->
|
||||
<Target Name="_AndroidObfuscateDex" AfterTargets="_CalculateProguardConfigurationFiles" Condition="'$(AndroidLinkTool)' == 'r8'">
|
||||
<PropertyGroup>
|
||||
<_AndroidObfuscationProguardConfig>$(IntermediateOutputPath)proguard\proguard_obfuscate.cfg</_AndroidObfuscationProguardConfig>
|
||||
<_AndroidAaptProguardRules>$(IntermediateOutputPath)proguard\aapt_rules.cached.txt</_AndroidAaptProguardRules>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<_AndroidObfuscationProguardLines Include="-ignorewarnings" Condition="'$(AndroidR8IgnoreWarnings)' == 'True'" />
|
||||
<_AndroidObfuscationProguardLines Include="-keepattributes SourceFile" />
|
||||
<_AndroidObfuscationProguardLines Include="-keepattributes LineNumberTable" />
|
||||
<!-- Absolute: R8 resolves a relative -printmapping against the directory
|
||||
of the config file that declares it, not the working directory. -->
|
||||
<_AndroidObfuscationProguardLines Include="-printmapping "$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(AndroidProguardMappingFile)'))"" Condition="'$(AndroidProguardMappingFile)' != ''" />
|
||||
</ItemGroup>
|
||||
<MakeDir Directories="$(IntermediateOutputPath)proguard" />
|
||||
<WriteLinesToFile File="$(_AndroidObfuscationProguardConfig)" Lines="@(_AndroidObfuscationProguardLines)" Overwrite="true" WriteOnlyWhenDifferent="true" />
|
||||
<!-- aapt2 derives keep rules for every class named in a layout or the
|
||||
manifest, but 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 both loses the
|
||||
item and has IncrementalClean delete the file, shipping an APK that
|
||||
dies inflating androidx.appcompat.widget.FitWindowsFrameLayout. Keep a
|
||||
copy outside FileWrites and feed R8 that one instead. -->
|
||||
<Copy SourceFiles="$(IntermediateOutputPath)aapt_rules.txt" DestinationFiles="$(_AndroidAaptProguardRules)" SkipUnchangedFiles="true" Condition="Exists('$(IntermediateOutputPath)aapt_rules.txt')" />
|
||||
<Error Condition="!Exists('$(_AndroidAaptProguardRules)')" Text="R8 is enabled but no aapt2 keep rules are available: delete obj\$(Configuration) and publish again to force a full resource link." />
|
||||
<ItemGroup>
|
||||
<_ProguardConfiguration Remove="$(IntermediateOutputPath)proguard\proguard_xamarin.cfg" />
|
||||
<_ProguardConfiguration Remove="$(IntermediateOutputPath)aapt_rules.txt" />
|
||||
<_ProguardConfiguration Include="$(MSBuildProjectDirectory)\proguard_xamarin.cfg" />
|
||||
<_ProguardConfiguration Include="$(_AndroidObfuscationProguardConfig)" />
|
||||
<_ProguardConfiguration Include="$(_AndroidAaptProguardRules)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# App-level R8 keep rules, on top of the ones .NET for Android generates
|
||||
# (see proguard_xamarin.cfg and the _AndroidObfuscateDex target in the csproj).
|
||||
|
||||
# The .NET runtime resolves this one by name from native code -- no DEX
|
||||
# reference points at it, so R8 renames it and startup dies with
|
||||
# "ClassNotFoundException: net.dot.android.ApplicationRegistration".
|
||||
# The SDK's own config only covers net.dot.jni.** and net.dot.android.crypto.**,
|
||||
# because upstream never obfuscates.
|
||||
-keep class net.dot.android.** { *; <init>(...); }
|
||||
|
||||
# The trimmer's generated keep rules (proguard_project_references.cfg) cover the
|
||||
# Java *methods* the bindings call, but not their fields -- upstream never
|
||||
# obfuscates, so the gap never showed. A bound property backed by a Java field
|
||||
# reads it through JNI by name, and renaming breaks that:
|
||||
# NoSuchFieldError: no "Landroidx/lifecycle/Lifecycle$State;" field "DESTROYED"
|
||||
# NoSuchFieldError: no "I" field "left" in class "Landroidx/core/graphics/Insets;"
|
||||
# Only the API surface a binding can reach needs it; private fields still get
|
||||
# renamed, and class names -- what Play's report counts -- are untouched by this.
|
||||
-keepclassmembers class * {
|
||||
public <fields>;
|
||||
protected <fields>;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
# Verbatim copy of the ProGuard configuration .NET for Android generates at
|
||||
# obj/<Config>/<TFM>/proguard/proguard_xamarin.cfg, minus its hardcoded
|
||||
# "-dontobfuscate". ProGuard has no switch that undoes that flag, so the only
|
||||
# way to let R8 rename DEX classes is to drop the file that sets it from R8's
|
||||
# --pg-conf list and substitute this one (see the _AndroidObfuscateDex target
|
||||
# in the csproj).
|
||||
#
|
||||
# Taken from Microsoft.Android.Sdk 36.1.69 (.NET 10). When the Android SDK pack
|
||||
# is updated, diff this against the generated file after a Release build --
|
||||
# a keep rule added upstream and missed here means a runtime JNI failure.
|
||||
# The trailing -ignorewarnings/-keepattributes/-printmapping lines the SDK
|
||||
# appends to that file are regenerated by the csproj target instead.
|
||||
|
||||
-keep class android.support.multidex.MultiDexApplication { <init>(); }
|
||||
-keep class net.dot.jni.** { *; <init>(); }
|
||||
-keep class mono.MonoRuntimeProvider* { *; <init>(...); }
|
||||
-keep class mono.MonoPackageManager { *; <init>(...); }
|
||||
-keep class mono.MonoPackageManager_Resources { *; <init>(...); }
|
||||
-keep class mono.android.** { *; <init>(...); }
|
||||
-keep class mono.java.** { *; <init>(...); }
|
||||
-keep class mono.javax.** { *; <init>(...); }
|
||||
-keep class net.dot.jni.ManagedPeer { *; <init>(...); }
|
||||
-keep class xamarin.android.net.ServerCertificateCustomValidator_TrustManager { *; <init>(...); }
|
||||
-keep class xamarin.android.net.ServerCertificateCustomValidator_TrustManager_FakeSSLSession { *; <init>(...); }
|
||||
-keep class xamarin.android.net.ServerCertificateCustomValidator_AlwaysAcceptingHostnameVerifier { *; <init>(...); }
|
||||
|
||||
-keep class android.runtime.** { <init>(...); }
|
||||
-keep class assembly_mono_android.android.runtime.** { <init>(...); }
|
||||
# hash for android.runtime and assembly_mono_android.android.runtime.
|
||||
-keep class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
|
||||
-keepclassmembers class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
|
||||
|
||||
# .NET runtime
|
||||
-keep class net.dot.android.crypto.** { *; <init>(...); }
|
||||
|
||||
# Android's template misses fluent setters...
|
||||
-keepclassmembers class * extends android.view.View {
|
||||
*** set*(...);
|
||||
}
|
||||
|
||||
# also misses those inflated custom layout stuff from xml...
|
||||
-keepclassmembers class * extends android.view.View {
|
||||
<init>(android.content.Context,android.util.AttributeSet);
|
||||
<init>(android.content.Context,android.util.AttributeSet,int);
|
||||
}
|
||||
Reference in New Issue
Block a user