From dbf8301f3425f145f925f971f211bbb7c1402be9 Mon Sep 17 00:00:00 2001 From: Salomon BRYS Date: Thu, 16 Jul 2020 22:21:30 +0200 Subject: [PATCH] Android loading may fallback to standard JVM (#9) * Android loading may fallback to standard JVM * Java code must target JVM 1.8 in order to be compatible Android. Co-authored-by: Salomon BRYS --- build.gradle.kts | 2 +- .../jni/NativeSecp256k1AndroidLoader.kt | 31 +++++++++++++++++++ .../secp256k1/jni/NativeSecp256k1Loader.kt | 16 ---------- jni/build.gradle.kts | 5 +++ ...1Loader.kt => NativeSecp256k1JvmLoader.kt} | 10 +++--- .../kotlin/fr/acinq/secp256k1/Secp256k1Jvm.kt | 13 +++++--- 6 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 jni/android/src/main/java/fr/acinq/secp256k1/jni/NativeSecp256k1AndroidLoader.kt delete mode 100644 jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt rename jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/{NativeSecp256k1Loader.kt => NativeSecp256k1JvmLoader.kt} (94%) diff --git a/build.gradle.kts b/build.gradle.kts index 6eef144..099df01 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,7 @@ buildscript { allprojects { group = "fr.acinq.secp256k1" - version = "0.2.1-1.4-M3" + version = "0.3.0-1.4-M3" repositories { jcenter() diff --git a/jni/android/src/main/java/fr/acinq/secp256k1/jni/NativeSecp256k1AndroidLoader.kt b/jni/android/src/main/java/fr/acinq/secp256k1/jni/NativeSecp256k1AndroidLoader.kt new file mode 100644 index 0000000..6c66508 --- /dev/null +++ b/jni/android/src/main/java/fr/acinq/secp256k1/jni/NativeSecp256k1AndroidLoader.kt @@ -0,0 +1,31 @@ +package fr.acinq.secp256k1.jni + +import android.util.Log +import fr.acinq.secp256k1.Secp256k1 +import fr.acinq.secp256k1.NativeSecp256k1 +import java.util.* + +public object NativeSecp256k1AndroidLoader { + + @JvmStatic + @Synchronized + @Throws(Exception::class) + fun load(): Secp256k1 { + try { + System.loadLibrary("secp256k1-jni") + return NativeSecp256k1 + } catch (ex: UnsatisfiedLinkError) { + // Purposefully not using Android Log + println("Could not load Android Secp256k1. Trying to extract JVM platform specific version.") + try { + val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1JvmLoader") + val load = cls.getMethod("load") + return load.invoke(null) as Secp256k1 + } catch (_: ClassNotFoundException) { + throw ex + } + + } + } + +} diff --git a/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt b/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt deleted file mode 100644 index bb761fd..0000000 --- a/jni/android/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt +++ /dev/null @@ -1,16 +0,0 @@ -package fr.acinq.secp256k1.jni - -import fr.acinq.secp256k1.Secp256k1 -import fr.acinq.secp256k1.NativeSecp256k1 - -public object NativeSecp256k1Loader { - - @JvmStatic - @Synchronized - @Throws(Exception::class) - fun load(): Secp256k1 { - System.loadLibrary("secp256k1-jni") - return NativeSecp256k1 - } - -} diff --git a/jni/build.gradle.kts b/jni/build.gradle.kts index 33ac6d6..61fbe5e 100644 --- a/jni/build.gradle.kts +++ b/jni/build.gradle.kts @@ -3,6 +3,11 @@ plugins { `maven-publish` } +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + kotlin { explicitApi() } diff --git a/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt b/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1JvmLoader.kt similarity index 94% rename from jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt rename to jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1JvmLoader.kt index 4758ceb..db53b0d 100644 --- a/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1Loader.kt +++ b/jni/jvm/src/main/kotlin/fr/acinq/secp256k1/jni/NativeSecp256k1JvmLoader.kt @@ -14,7 +14,7 @@ import java.util.* * * @author leo */ -public object NativeSecp256k1Loader { +public object NativeSecp256k1JvmLoader { private var extracted = false /** @@ -106,7 +106,7 @@ public object NativeSecp256k1Loader { val extractedLckFile = File(targetDirectory, extractedLckFileName) return try { // Extract a native library file into the target directory - val reader = NativeSecp256k1Loader::class.java.getResourceAsStream(libPath) + val reader = NativeSecp256k1JvmLoader::class.java.getResourceAsStream(libPath) if (!extractedLckFile.exists()) { FileOutputStream(extractedLckFile).close() } @@ -132,7 +132,7 @@ public object NativeSecp256k1Loader { extractedLibFile.setExecutable(true) // Check whether the contents are properly copied from the resource folder - NativeSecp256k1Loader::class.java.getResourceAsStream(libPath).use { nativeIn -> + NativeSecp256k1JvmLoader::class.java.getResourceAsStream(libPath).use { nativeIn -> FileInputStream(extractedLibFile).use { extractedLibIn -> if (!nativeIn.contentsEquals(extractedLibIn)) { throw RuntimeException( @@ -196,9 +196,9 @@ public object NativeSecp256k1Loader { } // Load the os-dependent library from the jar file - val packagePath = NativeSecp256k1Loader::class.java.getPackage().name.replace("\\.".toRegex(), "/") + val packagePath = NativeSecp256k1JvmLoader::class.java.getPackage().name.replace("\\.".toRegex(), "/") val embeddedLibraryPath = "/$packagePath/native/${OSInfo.nativeSuffix}" - val hasNativeLib = NativeSecp256k1Loader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null + val hasNativeLib = NativeSecp256k1JvmLoader::class.java.getResource("$embeddedLibraryPath/$libraryName") != null if (!hasNativeLib) { error("No native library found: at $embeddedLibraryPath/$libraryName") } diff --git a/src/jvmMain/kotlin/fr/acinq/secp256k1/Secp256k1Jvm.kt b/src/jvmMain/kotlin/fr/acinq/secp256k1/Secp256k1Jvm.kt index 6bd80ef..e27ad8d 100644 --- a/src/jvmMain/kotlin/fr/acinq/secp256k1/Secp256k1Jvm.kt +++ b/src/jvmMain/kotlin/fr/acinq/secp256k1/Secp256k1Jvm.kt @@ -16,15 +16,20 @@ package fr.acinq.secp256k1 -import java.lang.IllegalStateException +import java.util.* -internal actual fun getSecpk256k1(): Secp256k1 { +private fun tryLoad(platform: String): Secp256k1? { try { - val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1Loader") + val cls = Class.forName("fr.acinq.secp256k1.jni.NativeSecp256k1${platform.capitalize(Locale.ROOT)}Loader") val load = cls.getMethod("load") return load.invoke(null) as Secp256k1 } catch (ex: ClassNotFoundException) { - throw IllegalStateException("Could not load native Secp256k1 JNI library. Have you added the JNI dependency?", ex) + return null } } + +internal actual fun getSecpk256k1(): Secp256k1 = + tryLoad("android") + ?: tryLoad("jvm") + ?: error("Could not load native Secp256k1 JNI library. Have you added the JNI dependency?")