diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 04af821..6a15324 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -37,6 +37,13 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.getByName("debug") + + // proguard-rules.pro keeps the names JNI resolves at runtime. + // Without it a release build aborts on the first callback. + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) } } diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..9f188aa --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# JNI reaches these by name. R8 renames by default, so without these rules a +# release build resolves nothing: GetMethodID("onOutput", ...) returns NULL, +# raises NoSuchMethodError, and the next JNI call aborts the process with +# NoSuchMethodError: no non-static method "Lp0;.onOutput(Ljava/lang/String;)V" +# Debug builds do not minify, so this only ever appears in release. + +# The plugin object holds the native method declarations and is looked up from +# C by its fully qualified name (Java_com_novoyuuparosk_iperf3gui_IperfRunner_*). +-keep class com.novoyuuparosk.iperf3gui.IperfRunner { *; } + +# The callback interface and everything implementing it: iperf_jni.c resolves +# onOutput/onError/onComplete by name on the concrete object's class, so the +# implementors' method names must survive too, not just the interface's. +-keep interface com.novoyuuparosk.iperf3gui.IperfRunner$IperfCallback { *; } +-keep class * implements com.novoyuuparosk.iperf3gui.IperfRunner$IperfCallback { *; } + +# Belt and braces for any future native method: keep the name of any class +# declaring one, and the native methods themselves. +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +}