From 85af3bcfc0f430d5f276e966bc185d3a4b63f788 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 14 Nov 2023 20:59:27 -0500 Subject: [PATCH] refactor: [1/n] Introduce the lpac project and lpac_jni --- .gitmodules | 3 + .idea/compiler.xml | 7 +- .idea/gradle.xml | 1 + .idea/kotlinc.xml | 2 +- app/build.gradle | 13 +- build.gradle | 4 +- libs/lpac-jni/build.gradle | 46 +++++++ libs/lpac-jni/consumer-rules.pro | 0 libs/lpac-jni/proguard-rules.pro | 21 ++++ .../lpac_jni/ExampleInstrumentedTest.kt | 24 ++++ libs/lpac-jni/src/main/AndroidManifest.xml | 4 + .../net/typeblog/lpac_jni/ApduInterface.kt | 12 ++ .../net/typeblog/lpac_jni/HttpInterface.kt | 28 +++++ .../java/net/typeblog/lpac_jni/LpacJni.kt | 11 ++ libs/lpac-jni/src/main/jni/Android.mk | 46 +++++++ libs/lpac-jni/src/main/jni/Application.mk | 1 + libs/lpac-jni/src/main/jni/lpac | 1 + .../src/main/jni/lpac-jni/interface-wrapper.c | 117 ++++++++++++++++++ .../src/main/jni/lpac-jni/interface-wrapper.h | 14 +++ .../lpac-jni/src/main/jni/lpac-jni/lpac-jni.c | 49 ++++++++ .../lpac-jni/src/main/jni/lpac-jni/lpac-jni.h | 18 +++ .../net/typeblog/lpac_jni/ExampleUnitTest.kt | 17 +++ libs/lpad-sm-dp-plus-connector/build.gradle | 7 +- settings.gradle | 1 + 24 files changed, 429 insertions(+), 18 deletions(-) create mode 100644 .gitmodules create mode 100644 libs/lpac-jni/build.gradle create mode 100644 libs/lpac-jni/consumer-rules.pro create mode 100644 libs/lpac-jni/proguard-rules.pro create mode 100644 libs/lpac-jni/src/androidTest/java/net/typeblog/lpac_jni/ExampleInstrumentedTest.kt create mode 100644 libs/lpac-jni/src/main/AndroidManifest.xml create mode 100644 libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt create mode 100644 libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt create mode 100644 libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt create mode 100644 libs/lpac-jni/src/main/jni/Android.mk create mode 100644 libs/lpac-jni/src/main/jni/Application.mk create mode 160000 libs/lpac-jni/src/main/jni/lpac create mode 100644 libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c create mode 100644 libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h create mode 100644 libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c create mode 100644 libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h create mode 100644 libs/lpac-jni/src/test/java/net/typeblog/lpac_jni/ExampleUnitTest.kt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f888959 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/lpac-jni/src/main/jni/lpac"] + path = libs/lpac-jni/src/main/jni/lpac + url = https://github.com/estkme/lpac diff --git a/.idea/compiler.xml b/.idea/compiler.xml index f87d4c7..69fd9ef 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -4,9 +4,10 @@ - - - + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index e8e911f..45786ac 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -16,6 +16,7 @@ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 7e340a7..e805548 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 2e98b83..bf58142 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,7 +37,7 @@ def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { - compileSdk 31 + compileSdk 34 defaultConfig { applicationId "im.angry.openeuicc" @@ -81,11 +81,12 @@ dependencies { compileOnly project(':libs:hidden-apis-stub') implementation project(':libs:hidden-apis-shim') implementation project(":libs:lpad-sm-dp-plus-connector") - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.6.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' + implementation project(":libs:lpac-jni") + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.cardview:cardview:1.0.0" implementation 'com.journeyapps:zxing-android-embedded:4.3.0' diff --git a/build.gradle b/build.gradle index aeac5db..b7f2df2 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,8 @@ plugins { id 'com.android.application' version '8.1.2' apply false id 'com.android.library' version '8.1.2' apply false - id 'org.jetbrains.kotlin.android' version '1.6.21' apply false - id 'org.jetbrains.kotlin.multiplatform' version '1.6.21' apply false + id 'org.jetbrains.kotlin.android' version '1.9.20' apply false + id 'org.jetbrains.kotlin.multiplatform' version '1.9.20' apply false } task clean(type: Delete) { diff --git a/libs/lpac-jni/build.gradle b/libs/lpac-jni/build.gradle new file mode 100644 index 0000000..f3c9d40 --- /dev/null +++ b/libs/lpac-jni/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'net.typeblog.lpac_jni' + compileSdk 33 + + defaultConfig { + minSdk 27 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + ndkBuild { + path "src/main/jni/Android.mk" + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.12.0' + implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} \ No newline at end of file diff --git a/libs/lpac-jni/consumer-rules.pro b/libs/lpac-jni/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/libs/lpac-jni/proguard-rules.pro b/libs/lpac-jni/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/libs/lpac-jni/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/libs/lpac-jni/src/androidTest/java/net/typeblog/lpac_jni/ExampleInstrumentedTest.kt b/libs/lpac-jni/src/androidTest/java/net/typeblog/lpac_jni/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..581a79b --- /dev/null +++ b/libs/lpac-jni/src/androidTest/java/net/typeblog/lpac_jni/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package net.typeblog.lpac_jni + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("net.typeblog.lpac_jni.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/AndroidManifest.xml b/libs/lpac-jni/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/libs/lpac-jni/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt new file mode 100644 index 0000000..aa977a6 --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt @@ -0,0 +1,12 @@ +package net.typeblog.lpac_jni + +/* + * Should reflect euicc_apdu_interface in lpac/euicc/interface.h + */ +sealed interface ApduInterface { + fun connect() + fun disconnect() + fun logicalChannelOpen(aid: ByteArray): Int + fun logicalChannelClose(handle: Int) + fun transmit(tx: ByteArray): ByteArray +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt new file mode 100644 index 0000000..705b21e --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt @@ -0,0 +1,28 @@ +package net.typeblog.lpac_jni + +/* + * Should reflect euicc_http_interface in lpac/euicc/interface.h + */ +sealed interface HttpInterface { + data class HttpResponse(val rcode: Int, val data: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as HttpResponse + + if (rcode != other.rcode) return false + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + var result = rcode + result = 31 * result + data.contentHashCode() + return result + } + } + + fun transmit(url: String, tx: ByteArray): HttpResponse +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt new file mode 100644 index 0000000..5dfeebe --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt @@ -0,0 +1,11 @@ +package net.typeblog.lpac_jni + +private class LpacJni { + init { + System.loadLibrary("lpac-jni") + } + + external fun createContext(apduInterface: ApduInterface, httpInterface: HttpInterface): Long + external fun destroyContext(handle: Long) + external fun setCurrentContext(handle: Long) +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/Android.mk b/libs/lpac-jni/src/main/jni/Android.mk new file mode 100644 index 0000000..cd5678c --- /dev/null +++ b/libs/lpac-jni/src/main/jni/Android.mk @@ -0,0 +1,46 @@ +LOCAL_PATH := $(call my-dir) + +# function to find all *.c files under a directory +define all-c-files-under +$(patsubst ./%,%, \ + $(shell cd $(LOCAL_PATH) ; \ + find $(1) -name "*.c" -and -not -name ".*" -maxdepth 1) \ + ) +endef + +include $(CLEAR_VARS) +# libcjson +LOCAL_MODULE := lpac-cjson +LOCAL_SRC_FILES := \ + $(call all-c-files-under, lpac/cjson) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +# libasn1c, the ASN parser component from lpac +LOCAL_MODULE := lpac-asn1c +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/lpac/euicc/asn1c +LOCAL_SRC_FILES := \ + $(call all-c-files-under, lpac/euicc/asn1c/asn1) +LOCAL_CFLAGS := -DHAVE_CONFIG_H +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +# libeuicc component from lpac, which contains the actual implementation +LOCAL_MODULE := lpac-euicc +LOCAL_STATIC_LIBRARIES := lpac-asn1c lpac-cjson +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/lpac +LOCAL_SRC_FILES := \ + $(call all-c-files-under, lpac/euicc) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := lpac-jni +LOCAL_STATIC_LIBRARIES := lpac-euicc +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/lpac +LOCAL_SRC_FILES := \ + lpac-jni/lpac-jni.c \ + lpac-jni/interface-wrapper.c +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/Application.mk b/libs/lpac-jni/src/main/jni/Application.mk new file mode 100644 index 0000000..e619d92 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := all \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac b/libs/lpac-jni/src/main/jni/lpac new file mode 160000 index 0000000..2eaefa6 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac @@ -0,0 +1 @@ +Subproject commit 2eaefa6f8d79f68eff6a8c03932b861425767330 diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c new file mode 100644 index 0000000..a744946 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c @@ -0,0 +1,117 @@ +#include +#include +#include "interface-wrapper.h" +#include "lpac-jni.h" + +jmethodID method_apdu_connect; +jmethodID method_apdu_disconnect; +jmethodID method_apdu_logical_channel_open; +jmethodID method_apdu_logical_channel_close; +jmethodID method_apdu_transmit; + +jmethodID method_http_transmit; + +jfieldID field_resp_rcode; +jfieldID field_resp_data; + +void interface_wrapper_init() { + LPAC_JNI_SETUP_ENV; + jclass apdu_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/ApduInterface"); + method_apdu_connect = (*env)->GetMethodID(env, apdu_class, "connect", "()V"); + method_apdu_disconnect = (*env)->GetMethodID(env, apdu_class, "disconnect", "()V"); + method_apdu_logical_channel_open = (*env)->GetMethodID(env, apdu_class, "logicalChannelOpen", "([B)I"); + method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose", "(I)V"); + method_apdu_transmit = (*env)->GetMethodID(env, apdu_class, "transmit", "([B)[B"); + + jclass http_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface"); + method_http_transmit = (*env)->GetMethodID(env, http_class, "transmit", + "(Ljava/lang/String;[B)Lnet/typeblog/lpac_jni/HttpInterface$HttpResponse;"); + + jclass resp_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface$HttpResponse"); + field_resp_rcode = (*env)->GetFieldID(env, resp_class, "rcode", "I"); + field_resp_data = (*env)->GetFieldID(env, resp_class, "data", "[B"); +} + +static int apdu_interface_connect(void) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + (*env)->CallVoidMethod(env, jni_ctx->apdu_interface, method_apdu_connect); + LPAC_JNI_END(!((*env)->ExceptionCheck(env) == JNI_FALSE)); +} + +static void apdu_interface_disconnect(void) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + (*env)->CallVoidMethod(env, jni_ctx->apdu_interface, method_apdu_disconnect); + LPAC_JNI_END0; +} + +static int apdu_interface_logical_channel_open(const uint8_t *aid, uint8_t aid_len) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + jbyteArray jbarr = (*env)->NewByteArray(env, aid_len); + (*env)->SetByteArrayRegion(env, jbarr, 0, aid_len, (const jbyte *) aid); + jint ret = (*env)->CallIntMethod(env, jni_ctx->apdu_interface, method_apdu_logical_channel_open, jbarr); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + LPAC_JNI_END(-1); + } else { + LPAC_JNI_END(ret); + } +} + +static void apdu_interface_logical_channel_close(uint8_t channel) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + (*env)->CallVoidMethod(env, jni_ctx->apdu_interface, method_apdu_logical_channel_close, channel); + LPAC_JNI_END0; +} + +static int apdu_interface_transmit(uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + jbyteArray txArr = (*env)->NewByteArray(env, tx_len); + (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); + jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod(env, jni_ctx->apdu_interface, method_apdu_transmit, txArr); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + LPAC_JNI_END(-1); + } + *rx_len = (*env)->GetArrayLength(env, ret); + *rx = malloc(*rx_len * sizeof(uint8_t)); + (*env)->GetByteArrayRegion(env, ret, 0, *rx_len, *rx); + LPAC_JNI_END(0); +} + +static int http_interface_transmit(const char *url, uint32_t *rcode, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { + LPAC_JNI_BEGIN; + LPAC_JNI_ASSERT_CTX; + LPAC_JNI_SETUP_ENV; + jstring jurl = (*env)->NewString(env, url, strlen(url)); + jbyteArray txArr = (*env)->NewByteArray(env, tx_len); + (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); + jobject ret = (*env)->CallObjectMethod(env, jni_ctx->http_interface, method_http_transmit, jurl, txArr); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + LPAC_JNI_END(-1); + } + *rcode = (*env)->GetIntField(env, ret, field_resp_rcode); + jbyteArray rxArr = (jbyteArray) (*env)->GetObjectField(env, ret, field_resp_data); + *rx_len = (*env)->GetArrayLength(env, rxArr); + *rx = malloc(*rx_len * sizeof(uint8_t)); + (*env)->GetByteArrayRegion(env, rxArr, 0, *rx_len, *rx); + LPAC_JNI_END(0); +} + +struct euicc_apdu_interface apdu_interface_wrapper = { + .connect = apdu_interface_connect, + .disconnect = apdu_interface_disconnect, + .logic_channel_open = apdu_interface_logical_channel_open, + .logic_channel_close = apdu_interface_logical_channel_close, + .transmit = apdu_interface_transmit +}; +struct euicc_http_interface http_interface_wrapper = { + .transmit = http_interface_transmit +}; \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h new file mode 100644 index 0000000..e034965 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h @@ -0,0 +1,14 @@ +#pragma once +#undef NDEBUG +#include +#include + +extern struct euicc_apdu_interface apdu_interface_wrapper; +extern struct euicc_http_interface http_interface_wrapper; + +void interface_wrapper_init(); + +#define LPAC_JNI_ASSERT_CTX assert(jni_ctx != NULL) +#define LPAC_JNI_SETUP_ENV \ + JNIEnv *env; \ + (*jvm)->AttachCurrentThread(jvm, &env, NULL) \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c new file mode 100644 index 0000000..4f48b2c --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include "lpac-jni.h" +#include "interface-wrapper.h" + +pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; +struct lpac_jni_ctx *jni_ctx = NULL; +JavaVM *jvm = NULL; + +jint JNI_OnLoad(JavaVM *vm, void *reserved) { + jvm = vm; + interface_wrapper_init(); + return 1; +} + +JNIEXPORT jlong JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_createContext(JNIEnv *env, jobject thiz, + jobject apdu_interface, + jobject http_interface) { + LPAC_JNI_BEGIN; + struct lpac_jni_ctx *_ctx = malloc(sizeof(struct lpac_jni_ctx)); + memset(_ctx, 0, sizeof(struct lpac_jni_ctx)); + _ctx->ctx.interface.apdu = &apdu_interface_wrapper; + _ctx->ctx.interface.http = &http_interface_wrapper; + _ctx->apdu_interface = (*env)->NewGlobalRef(env, apdu_interface); + _ctx->http_interface = (*env)->NewGlobalRef(env, http_interface); + LPAC_JNI_END((jlong) _ctx); +} + +JNIEXPORT void JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_destroyContext(JNIEnv *env, jobject thiz, jlong handle) { + LPAC_JNI_BEGIN; + struct lpac_jni_ctx *_ctx = (struct lpac_jni_ctx *) handle; + (*env)->DeleteGlobalRef(env, _ctx->apdu_interface); + (*env)->DeleteGlobalRef(env, _ctx->http_interface); + if (jni_ctx == _ctx) { + jni_ctx = NULL; + } + free(_ctx); + LPAC_JNI_END0; +} + +JNIEXPORT void JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_setCurrentContext(JNIEnv *env, jobject thiz, jlong handle) { + LPAC_JNI_BEGIN; + jni_ctx = (struct lpac_jni_ctx *) handle; + LPAC_JNI_END0; +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h new file mode 100644 index 0000000..7f652ce --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include + +struct lpac_jni_ctx { + struct euicc_ctx ctx; + jobject apdu_interface; + jobject http_interface; +}; + +extern JavaVM *jvm; +extern pthread_mutex_t global_lock; +extern struct lpac_jni_ctx *jni_ctx; + +#define LPAC_JNI_BEGIN pthread_mutex_lock(&global_lock) +#define LPAC_JNI_END0 pthread_mutex_unlock(&global_lock) +#define LPAC_JNI_END(ret) LPAC_JNI_END0; return ret diff --git a/libs/lpac-jni/src/test/java/net/typeblog/lpac_jni/ExampleUnitTest.kt b/libs/lpac-jni/src/test/java/net/typeblog/lpac_jni/ExampleUnitTest.kt new file mode 100644 index 0000000..0334685 --- /dev/null +++ b/libs/lpac-jni/src/test/java/net/typeblog/lpac_jni/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package net.typeblog.lpac_jni + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/libs/lpad-sm-dp-plus-connector/build.gradle b/libs/lpad-sm-dp-plus-connector/build.gradle index 3314139..32a4686 100644 --- a/libs/lpad-sm-dp-plus-connector/build.gradle +++ b/libs/lpad-sm-dp-plus-connector/build.gradle @@ -26,9 +26,4 @@ task genAsn1(type: JavaExec) { compileJava.dependsOn genAsn1 compileKotlin.dependsOn genAsn1 -description = 'LPAd SM-DP+ Connector' - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} \ No newline at end of file +description = 'LPAd SM-DP+ Connector' \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 534a3ba..00b4a24 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,3 +16,4 @@ rootProject.name = "OpenEUICC" include ':app', ':libs:lpad-sm-dp-plus-connector' include ':libs:hidden-apis-stub' include ':libs:hidden-apis-shim' +include ':libs:lpac-jni'