mirror of
https://github.com/awatertrevi/xamarin-neo4j.git
synced 2026-09-22 09:05:29 +00:00
Compare commits
2 Commits
927e7627a6
...
1c319e6619
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c319e6619 | ||
|
|
fb361f0f4c |
30
README.md
30
README.md
@@ -50,6 +50,36 @@ dotnet test Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.Tests/
|
||||
dotnet test Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.IntegrationTests/
|
||||
```
|
||||
|
||||
### Android DEX shrinking & obfuscation (R8)
|
||||
|
||||
Release builds of the Android head run R8 over the Java/DEX side
|
||||
(`AndroidLinkTool=r8` in `Xamarin.Neo4j.Android.csproj`). Play Console's *DEX code
|
||||
optimization* report scores obfuscation at ~1% on a stock .NET MAUI build: .NET for
|
||||
Android runs no DEX shrinker by default, and even with R8 on, the ProGuard config the
|
||||
SDK generates hardcodes `-dontobfuscate` — a flag nothing later in the config list can
|
||||
undo. The `_AndroidObfuscateDex` target therefore drops that file from R8's `--pg-conf`
|
||||
list and substitutes `Xamarin.Neo4j.Android/proguard_xamarin.cfg` (the same file minus
|
||||
that line), plus a generated file carrying the `-printmapping`/`-keepattributes` tail
|
||||
the SDK would have appended. Measured on the Release APK: **49.8% of DEX classes
|
||||
renamed, up from ~0%**.
|
||||
|
||||
Only library-internal classes are renamed: the trimmer emits a `-keep` rule 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 — things resolved by name
|
||||
from native code. Add to it if a Release build dies with `ClassNotFoundException`,
|
||||
`NoSuchFieldError` or `NoSuchMethodError` where a Debug build does not.
|
||||
|
||||
`mapping.txt` is embedded in the AAB
|
||||
(`BUNDLE-METADATA/com.android.tools.build.obfuscation/proguard.map`), so Play Console
|
||||
retraces obfuscated Java stacks by itself. Managed (C#) stack traces are unaffected —
|
||||
R8 only touches DEX.
|
||||
|
||||
Local Release builds must be clean: aapt2's keep rules are registered as a `FileWrite`,
|
||||
so an incremental build that skips the resource link has `IncrementalClean` delete them.
|
||||
The target caches a copy outside `FileWrites` and errors out if even that is missing —
|
||||
delete `Xamarin.Neo4j.Android/obj/Release` if it fires.
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- .NET MAUI
|
||||
|
||||
@@ -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