瀏覽代碼

refactor: [6/n] Implement getProfiles()

Peter Cai 2 年之前
父節點
當前提交
d19109d62b

+ 22 - 5
app/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt

@@ -1,33 +1,50 @@
 package im.angry.openeuicc.core
 package im.angry.openeuicc.core
 
 
+import android.se.omapi.Channel
 import android.se.omapi.SEService
 import android.se.omapi.SEService
+import android.se.omapi.Session
 import net.typeblog.lpac_jni.ApduInterface
 import net.typeblog.lpac_jni.ApduInterface
 import net.typeblog.lpac_jni.LocalProfileAssistant
 import net.typeblog.lpac_jni.LocalProfileAssistant
 import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
 import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
 import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
 import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
+import java.lang.IllegalStateException
 
 
 class OmapiApduInterface(
 class OmapiApduInterface(
     private val service: SEService,
     private val service: SEService,
     private val info: EuiccChannelInfo
     private val info: EuiccChannelInfo
 ): ApduInterface {
 ): ApduInterface {
+    private lateinit var session: Session
+    private lateinit var lastChannel: Channel
+
     override fun connect() {
     override fun connect() {
-        TODO("Not yet implemented")
+        session = service.getUiccReader(info.slotId + 1).openSession()
     }
     }
 
 
     override fun disconnect() {
     override fun disconnect() {
-        TODO("Not yet implemented")
+        session.close()
     }
     }
 
 
     override fun logicalChannelOpen(aid: ByteArray): Int {
     override fun logicalChannelOpen(aid: ByteArray): Int {
-        TODO("Not yet implemented")
+        if (this::lastChannel.isInitialized) {
+            throw IllegalStateException("Can only open one channel")
+        }
+        lastChannel = session.openLogicalChannel(aid)!!;
+        return 0;
     }
     }
 
 
     override fun logicalChannelClose(handle: Int) {
     override fun logicalChannelClose(handle: Int) {
-        TODO("Not yet implemented")
+        if (handle != 0 || !this::lastChannel.isInitialized) {
+            throw IllegalStateException("Unknown channel")
+        }
+        lastChannel.close()
     }
     }
 
 
     override fun transmit(tx: ByteArray): ByteArray {
     override fun transmit(tx: ByteArray): ByteArray {
-        TODO("Not yet implemented")
+        if (!this::lastChannel.isInitialized) {
+            throw IllegalStateException("Unknown channel")
+        }
+
+        return lastChannel.transmit(tx)
     }
     }
 
 
 }
 }

+ 2 - 1
app/src/main/java/im/angry/openeuicc/util/TelephonyUtils.kt

@@ -29,5 +29,6 @@ val LocalProfileInfo.displayName: String
 
 
 val List<LocalProfileInfo>.operational: List<LocalProfileInfo>
 val List<LocalProfileInfo>.operational: List<LocalProfileInfo>
     get() = filter {
     get() = filter {
-        it.profileClass == LocalProfileInfo.Clazz.Operational
+        // TODO: Profiles get marked as PROVISIONING with lpac even if it is OPERATIONAL. Why?
+        it.profileClass != LocalProfileInfo.Clazz.Testing
     }
     }

+ 9 - 0
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt

@@ -7,4 +7,13 @@ internal object LpacJni {
 
 
     external fun createContext(apduInterface: ApduInterface, httpInterface: HttpInterface): Long
     external fun createContext(apduInterface: ApduInterface, httpInterface: HttpInterface): Long
     external fun destroyContext(handle: Long)
     external fun destroyContext(handle: Long)
+
+    // es10x
+    external fun es10xInit(handle: Long): Int
+    external fun es10xFini(handle: Long)
+
+    // es10c
+    // null returns signify errors
+    external fun es10cGetEid(handle: Long): String?
+    external fun es10cGetProfilesInfo(handle: Long): Array<LocalProfileInfo>?
 }
 }

+ 18 - 5
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt

@@ -6,11 +6,23 @@ import net.typeblog.lpac_jni.HttpInterface
 import net.typeblog.lpac_jni.LocalProfileAssistant
 import net.typeblog.lpac_jni.LocalProfileAssistant
 import net.typeblog.lpac_jni.LocalProfileInfo
 import net.typeblog.lpac_jni.LocalProfileInfo
 
 
-class LocalProfileAssistantImpl(val apduInterface: ApduInterface, val httpInterface: HttpInterface): LocalProfileAssistant {
+class LocalProfileAssistantImpl(
+    apduInterface: ApduInterface,
+    httpInterface: HttpInterface
+): LocalProfileAssistant {
+    private val contextHandle: Long = LpacJni.createContext(apduInterface, httpInterface)
+    init {
+        if (LpacJni.es10xInit(contextHandle) < 0) {
+            throw IllegalArgumentException("Failed to initialize LPA")
+        }
+    }
+
     override val profiles: List<LocalProfileInfo>
     override val profiles: List<LocalProfileInfo>
-        get() = listOf()
-    override val eID: String
-        get() = "1234567890"
+        get() = LpacJni.es10cGetProfilesInfo(contextHandle)!!.asList() // TODO: Maybe we need better error handling
+
+    override val eID: String by lazy {
+        LpacJni.es10cGetEid(contextHandle)!!
+    }
 
 
     override fun enableProfile(iccid: String): Boolean {
     override fun enableProfile(iccid: String): Boolean {
         TODO("Not yet implemented")
         TODO("Not yet implemented")
@@ -33,6 +45,7 @@ class LocalProfileAssistantImpl(val apduInterface: ApduInterface, val httpInterf
     }
     }
 
 
     override fun close() {
     override fun close() {
-        // TODO: use es10x_fini
+        LpacJni.es10xFini(contextHandle)
+        LpacJni.destroyContext(contextHandle)
     }
     }
 }
 }

+ 0 - 3
libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h

@@ -9,9 +9,6 @@ void interface_wrapper_init();
 extern struct euicc_apdu_interface lpac_jni_apdu_interface;
 extern struct euicc_apdu_interface lpac_jni_apdu_interface;
 extern struct euicc_http_interface lpac_jni_http_interface;
 extern struct euicc_http_interface lpac_jni_http_interface;
 
 
-#define LPAC_JNI_SETUP_ENV \
-    JNIEnv *env; \
-    (*jvm)->AttachCurrentThread(jvm, &env, NULL)
 #define LPAC_JNI_EXCEPTION_RETURN \
 #define LPAC_JNI_EXCEPTION_RETURN \
     if ((*env)->ExceptionCheck(env) == JNI_TRUE) { \
     if ((*env)->ExceptionCheck(env) == JNI_TRUE) { \
         (*env)->ExceptionClear(env); \
         (*env)->ExceptionClear(env); \

+ 151 - 2
libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c

@@ -1,15 +1,66 @@
+#include <euicc/es10x.h>
 #include <euicc/interface.h>
 #include <euicc/interface.h>
 #include <malloc.h>
 #include <malloc.h>
 #include <string.h>
 #include <string.h>
+#include <syslog.h>
 #include "lpac-jni.h"
 #include "lpac-jni.h"
 #include "interface-wrapper.h"
 #include "interface-wrapper.h"
 
 
 JavaVM  *jvm = NULL;
 JavaVM  *jvm = NULL;
 
 
+jclass local_profile_info_class;
+jmethodID local_profile_info_constructor;
+
+jobject local_profile_state_enabled;
+jobject local_profile_state_disabled;
+
+jobject local_profile_class_testing;
+jobject local_profile_class_provisioning;
+jobject local_profile_class_operational;
+
+jstring empty_string;
+
+jclass string_class;
+jmethodID string_constructor;
+
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     jvm = vm;
     jvm = vm;
     interface_wrapper_init();
     interface_wrapper_init();
-    return 1;
+
+    LPAC_JNI_SETUP_ENV;
+    string_class = (*env)->FindClass(env, "java/lang/String");
+    string_class = (*env)->NewGlobalRef(env, string_class);
+    string_constructor = (*env)->GetMethodID(env, string_class, "<init>", "([BLjava/lang/String;)V");
+
+    local_profile_info_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/LocalProfileInfo");
+    local_profile_info_class = (*env)->NewGlobalRef(env, local_profile_info_class);
+    local_profile_info_constructor = (*env)->GetMethodID(env, local_profile_info_class, "<init>",
+                                                         "(Ljava/lang/String;Lnet/typeblog/lpac_jni/LocalProfileInfo$State;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lnet/typeblog/lpac_jni/LocalProfileInfo$Clazz;)V");
+
+    jclass local_profile_state_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/LocalProfileInfo$State");
+    jfieldID field_enabled = (*env)->GetStaticFieldID(env, local_profile_state_class, "Enabled", "Lnet/typeblog/lpac_jni/LocalProfileInfo$State;");
+    local_profile_state_enabled = (*env)->GetStaticObjectField(env, local_profile_state_class, field_enabled);
+    local_profile_state_enabled = (*env)->NewGlobalRef(env, local_profile_state_enabled);
+    jfieldID field_disabled = (*env)->GetStaticFieldID(env, local_profile_state_class, "Disabled", "Lnet/typeblog/lpac_jni/LocalProfileInfo$State;");
+    local_profile_state_disabled = (*env)->GetStaticObjectField(env, local_profile_state_class, field_disabled);
+    local_profile_state_disabled = (*env)->NewGlobalRef(env, local_profile_state_disabled);
+
+    jclass local_profile_class_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/LocalProfileInfo$Clazz");
+    jfieldID field_testing = (*env)->GetStaticFieldID(env, local_profile_class_class, "Testing", "Lnet/typeblog/lpac_jni/LocalProfileInfo$Clazz;");
+    local_profile_class_testing = (*env)->GetStaticObjectField(env, local_profile_class_class, field_testing);
+    local_profile_class_testing = (*env)->NewGlobalRef(env, local_profile_class_testing);
+    jfieldID field_provisioning = (*env)->GetStaticFieldID(env, local_profile_class_class, "Provisioning", "Lnet/typeblog/lpac_jni/LocalProfileInfo$Clazz;");
+    local_profile_class_provisioning = (*env)->GetStaticObjectField(env, local_profile_class_class, field_provisioning);
+    local_profile_class_provisioning = (*env)->NewGlobalRef(env, local_profile_class_provisioning);
+    jfieldID field_operational = (*env)->GetStaticFieldID(env, local_profile_class_class, "Operational", "Lnet/typeblog/lpac_jni/LocalProfileInfo$Clazz;");
+    local_profile_class_operational = (*env)->GetStaticObjectField(env, local_profile_class_class, field_operational);
+    local_profile_class_operational = (*env)->NewGlobalRef(env, local_profile_class_operational);
+
+    const char _unused[1];
+    empty_string = (*env)->NewString(env, _unused, 0);
+    empty_string = (*env)->NewGlobalRef(env, empty_string);
+
+    return JNI_VERSION_1_6;
 }
 }
 
 
 JNIEXPORT jlong JNICALL
 JNIEXPORT jlong JNICALL
@@ -21,7 +72,7 @@ Java_net_typeblog_lpac_1jni_LpacJni_createContext(JNIEnv *env, jobject thiz,
     memset(ctx, 0, sizeof(struct lpac_jni_ctx));
     memset(ctx, 0, sizeof(struct lpac_jni_ctx));
     memset(_ctx, 0, sizeof(struct lpac_jni_ctx));
     memset(_ctx, 0, sizeof(struct lpac_jni_ctx));
     ctx->interface.apdu = &lpac_jni_apdu_interface;
     ctx->interface.apdu = &lpac_jni_apdu_interface;
-    ctx->interface.http = &lpac_jni_apdu_interface;
+    ctx->interface.http = &lpac_jni_http_interface;
     _ctx->apdu_interface = (*env)->NewGlobalRef(env, apdu_interface);
     _ctx->apdu_interface = (*env)->NewGlobalRef(env, apdu_interface);
     _ctx->http_interface = (*env)->NewGlobalRef(env, http_interface);
     _ctx->http_interface = (*env)->NewGlobalRef(env, http_interface);
     ctx->userdata = (void *) _ctx;
     ctx->userdata = (void *) _ctx;
@@ -36,4 +87,102 @@ Java_net_typeblog_lpac_1jni_LpacJni_destroyContext(JNIEnv *env, jobject thiz, jl
     (*env)->DeleteGlobalRef(env, _ctx->http_interface);
     (*env)->DeleteGlobalRef(env, _ctx->http_interface);
     free(_ctx);
     free(_ctx);
     free(ctx);
     free(ctx);
+}
+
+JNIEXPORT jint JNICALL
+Java_net_typeblog_lpac_1jni_LpacJni_es10xInit(JNIEnv *env, jobject thiz, jlong handle) {
+    struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
+    return es10x_init(ctx);
+}
+
+JNIEXPORT void JNICALL
+Java_net_typeblog_lpac_1jni_LpacJni_es10xFini(JNIEnv *env, jobject thiz, jlong handle) {
+    struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
+    es10x_fini(ctx);
+}
+
+jstring toJString(JNIEnv *env, const char *pat) {
+    int len = strlen(pat);
+    jbyteArray bytes = (*env)->NewByteArray(env, len);
+    (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) pat);
+    jstring encoding = (*env)->NewStringUTF(env, "utf-8");
+    jstring jstr = (jstring) (*env)->NewObject(env, string_class,
+                                               string_constructor, bytes, encoding);
+    (*env)->DeleteLocalRef(env, encoding);
+    (*env)->DeleteLocalRef(env, bytes);
+    return jstr;
+}
+
+JNIEXPORT jstring JNICALL
+Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong handle) {
+    struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
+    char *buf;
+    if (es10c_get_eid(ctx, &buf) < 0) {
+        return NULL;
+    }
+    jstring ret = toJString(env, buf);
+    free(buf);
+    return ret;
+}
+
+JNIEXPORT jobjectArray JNICALL
+Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo(JNIEnv *env, jobject thiz, jlong handle) {
+    struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
+    struct es10c_profile_info *info;
+    int count;
+    if (es10c_get_profiles_info(ctx, &info, &count) < 0) {
+        return NULL;
+    }
+
+    jobjectArray ret = (*env)->NewObjectArray(env, count, local_profile_info_class, NULL);
+
+    // Convert the native info array to Java
+    for (int i = 0; i < count; i++) {
+        jstring iccid = toJString(env, info[i].iccid);
+        jstring isdpAid = toJString(env, info[i].isdpAid);
+        jstring name = toJString(env, info[i].profileName);
+        jstring nickName = info[i].profileNickname ? toJString(env, info[i].profileNickname) : (*env)->NewLocalRef(env, empty_string);
+        jstring serviceProvider = info[i].serviceProviderName ? toJString(env, info[i].serviceProviderName) : (*env)->NewLocalRef(env, empty_string);
+
+        jobject state;
+        switch (info->profileState) {
+            case ES10C_PROFILE_INFO_STATE_ENABLED:
+                state = local_profile_state_enabled;
+                break;
+            case ES10C_PROFILE_INFO_STATE_DISABLED:
+                state = local_profile_state_disabled;
+                break;
+        }
+        state = (*env)->NewLocalRef(env, state);
+
+        jobject class;
+        switch (info->profileClass) {
+            case ES10C_PROFILE_INFO_CLASS_TEST:
+                class = local_profile_class_testing;
+                break;
+            case ES10C_PROFILE_INFO_CLASS_PROVISIONING:
+                class = local_profile_class_provisioning;
+                break;
+            case ES10C_PROFILE_INFO_CLASS_OPERATIONAL:
+                class = local_profile_class_operational;
+                break;
+        }
+        class = (*env)->NewLocalRef(env, class);
+
+        jobject jinfo = (*env)->NewObject(env, local_profile_info_class, local_profile_info_constructor,
+                                          iccid, state, name, nickName, serviceProvider, isdpAid, class);
+        (*env)->SetObjectArrayElement(env, ret, i, jinfo);
+
+        (*env)->DeleteLocalRef(env, jinfo);
+        (*env)->DeleteLocalRef(env, class);
+        (*env)->DeleteLocalRef(env, state);
+        (*env)->DeleteLocalRef(env, serviceProvider);
+        (*env)->DeleteLocalRef(env, nickName);
+        (*env)->DeleteLocalRef(env, name);
+        (*env)->DeleteLocalRef(env, isdpAid);
+        (*env)->DeleteLocalRef(env, iccid);
+    }
+
+    es10c_profile_info_free_all(info, count);
+    return ret;
 }
 }

+ 3 - 0
libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h

@@ -9,5 +9,8 @@ struct lpac_jni_ctx {
 };
 };
 
 
 #define LPAC_JNI_CTX(ctx) ((struct lpac_jni_ctx *) ctx->userdata)
 #define LPAC_JNI_CTX(ctx) ((struct lpac_jni_ctx *) ctx->userdata)
+#define LPAC_JNI_SETUP_ENV \
+    JNIEnv *env; \
+    (*jvm)->AttachCurrentThread(jvm, &env, NULL)
 
 
 extern JavaVM *jvm;
 extern JavaVM *jvm;