瀏覽代碼

refactor: [4/n] Kill Truphone LPAd dependency

nothing is re-implemented just yet
Peter Cai 2 年之前
父節點
當前提交
92bc390ee5
共有 22 個文件被更改,包括 240 次插入153 次删除
  1. 0 1
      app/build.gradle
  2. 1 1
      app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt
  3. 11 2
      app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt
  4. 0 29
      app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt
  5. 65 0
      app/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt
  6. 0 39
      app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt
  7. 0 42
      app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt
  8. 39 17
      app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt
  9. 6 7
      app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt
  10. 3 4
      app/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
  11. 1 1
      app/src/main/java/im/angry/openeuicc/ui/MainActivity.kt
  12. 1 2
      app/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt
  13. 2 3
      app/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt
  14. 1 1
      app/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt
  15. 1 1
      app/src/main/java/im/angry/openeuicc/util/TelephonyUtils.kt
  16. 1 1
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt
  17. 1 1
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt
  18. 18 0
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt
  19. 41 0
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt
  20. 1 1
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt
  21. 9 0
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt
  22. 38 0
      libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt

+ 0 - 1
app/build.gradle

@@ -80,7 +80,6 @@ android {
 dependencies {
     compileOnly project(':libs:hidden-apis-stub')
     implementation project(':libs:hidden-apis-shim')
-    implementation project(":libs:lpad-sm-dp-plus-connector")
     implementation project(":libs:lpac-jni")
     implementation 'androidx.core:core-ktx:1.12.0'
     implementation 'androidx.appcompat:appcompat:1.6.1'

+ 1 - 1
app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt

@@ -1,6 +1,6 @@
 package im.angry.openeuicc.core
 
-import com.truphone.lpa.LocalProfileAssistant
+import net.typeblog.lpac_jni.LocalProfileAssistant
 
 // A custom type to avoid compatibility issues with UiccCardInfo / UiccPortInfo
 data class EuiccChannelInfo(

+ 11 - 2
app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt

@@ -14,6 +14,7 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
 import java.lang.Exception
+import java.lang.IllegalArgumentException
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 
@@ -75,11 +76,19 @@ class EuiccChannelManager(private val context: Context) {
             if (uiccInfo.isEuicc && !uiccInfo.isRemovable) {
                 Log.d(TAG, "Using TelephonyManager for slot ${uiccInfo.slotIndex}")
                 // TODO: On Tiramisu, we should also connect all available "ports" for MEP support
-                euiccChannel = TelephonyManagerChannel.tryConnect(tm, channelInfo)
+                try {
+                    euiccChannel = TelephonyManagerChannel(channelInfo, tm)
+                } catch (e: IllegalArgumentException) {
+                    // Failed
+                }
             }
 
             if (euiccChannel == null) {
-                euiccChannel = OmapiChannel.tryConnect(seService!!, channelInfo)
+                try {
+                    euiccChannel = OmapiChannel(seService!!, channelInfo)
+                } catch (e: IllegalArgumentException) {
+                    // Failed
+                }
             }
 
             if (euiccChannel != null) {

+ 0 - 29
app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt

@@ -1,29 +0,0 @@
-package im.angry.openeuicc.core
-
-import android.se.omapi.Channel
-import com.truphone.lpa.ApduChannel
-import com.truphone.lpa.ApduTransmittedListener
-import im.angry.openeuicc.util.byteArrayToHex
-import im.angry.openeuicc.util.hexStringToByteArray
-
-class OmapiApduChannel(private val channel: Channel) : ApduChannel {
-    override fun transmitAPDU(apdu: String): String =
-        byteArrayToHex(channel.transmit(hexStringToByteArray(apdu)))
-
-    override fun transmitAPDUS(apdus: MutableList<String>): String {
-        var res = ""
-        for (pdu in apdus) {
-            res = transmitAPDU(pdu)
-        }
-        return res
-    }
-
-    override fun sendStatus() {
-    }
-
-    override fun setApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
-    }
-
-    override fun removeApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
-    }
-}

+ 65 - 0
app/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt

@@ -0,0 +1,65 @@
+package im.angry.openeuicc.core
+
+import android.se.omapi.SEService
+import net.typeblog.lpac_jni.ApduInterface
+import net.typeblog.lpac_jni.LocalProfileAssistant
+import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
+import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
+
+class OmapiApduInterface(
+    private val service: SEService,
+    private val info: EuiccChannelInfo
+): ApduInterface {
+    override fun connect() {
+        TODO("Not yet implemented")
+    }
+
+    override fun disconnect() {
+        TODO("Not yet implemented")
+    }
+
+    override fun logicalChannelOpen(aid: ByteArray): Int {
+        TODO("Not yet implemented")
+    }
+
+    override fun logicalChannelClose(handle: Int) {
+        TODO("Not yet implemented")
+    }
+
+    override fun transmit(tx: ByteArray): ByteArray {
+        TODO("Not yet implemented")
+    }
+
+}
+
+class OmapiChannel(
+    service: SEService,
+    info: EuiccChannelInfo,
+) : EuiccChannel(info) {
+    companion object {
+        private const val TAG = "OmapiChannel"
+        private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0)
+
+        /*fun tryConnect(service: SEService, info: EuiccChannelInfo): OmapiChannel? {
+            try {
+                val reader = service.getUiccReader(info.slotId + 1) // slotId from telephony starts from 0
+                val session = reader.openSession()
+                val channel = session.openLogicalChannel(APPLET_ID) ?: return null
+                return OmapiChannel(info, channel)
+            } catch (e: Exception) {
+                Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId}, skipping")
+                Log.e(TAG, Log.getStackTraceString(e))
+                return null
+            }
+        }*/
+    }
+
+    override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
+        OmapiApduInterface(service, info),
+        HttpInterfaceImpl())
+
+    override val valid: Boolean
+        get() = true // TODO: This has to be implemented properly
+
+    override fun close() = lpa.close()
+}

+ 0 - 39
app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt

@@ -1,39 +0,0 @@
-package im.angry.openeuicc.core
-
-import android.se.omapi.Channel
-import android.se.omapi.SEService
-import android.util.Log
-import com.truphone.lpa.LocalProfileAssistant
-import com.truphone.lpa.impl.LocalProfileAssistantImpl
-import java.lang.Exception
-
-class OmapiChannel private constructor(
-    info: EuiccChannelInfo,
-    private val channel: Channel
-) : EuiccChannel(info) {
-    companion object {
-        private const val TAG = "OmapiChannel"
-        private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0)
-
-        fun tryConnect(service: SEService, info: EuiccChannelInfo): OmapiChannel? {
-            try {
-                val reader = service.getUiccReader(info.slotId + 1) // slotId from telephony starts from 0
-                val session = reader.openSession()
-                val channel = session.openLogicalChannel(APPLET_ID) ?: return null
-                return OmapiChannel(info, channel)
-            } catch (e: Exception) {
-                Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId}, skipping")
-                Log.e(TAG, Log.getStackTraceString(e))
-                return null
-            }
-        }
-    }
-
-    override val lpa: LocalProfileAssistant by lazy {
-        LocalProfileAssistantImpl(OmapiApduChannel(channel))
-    }
-    override val valid: Boolean
-        get() = channel.isOpen // TODO: This has to be implemented properly
-
-    override fun close() = channel.close()
-}

+ 0 - 42
app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt

@@ -1,42 +0,0 @@
-package im.angry.openeuicc.core
-
-import android.telephony.TelephonyManager
-import com.truphone.lpa.ApduChannel
-import com.truphone.lpa.ApduTransmittedListener
-import im.angry.openeuicc.util.*
-
-class TelephonyManagerApduChannel(
-    private val tm: TelephonyManager,
-    private val slotId: Int,
-    private val channelId: Int) : ApduChannel {
-
-    override fun transmitAPDU(apdu: String): String? {
-        val cla = Integer.parseInt(apdu.substring(0, 2), 16)
-        val instruction = Integer.parseInt(apdu.substring(2, 4), 16)
-        val p1 = Integer.parseInt(apdu.substring(4, 6), 16)
-        val p2 = Integer.parseInt(apdu.substring(6, 8), 16)
-        val p3 = Integer.parseInt(apdu.substring(8, 10), 16)
-        val p4 = apdu.substring(10)
-
-        return tm.iccTransmitApduLogicalChannelBySlot(
-            slotId, channelId,
-            cla, instruction, p1, p2, p3, p4)
-    }
-
-    override fun transmitAPDUS(apdus: MutableList<String>): String? {
-        var res: String? = ""
-        for (pdu in apdus) {
-            res = transmitAPDU(pdu)
-        }
-        return res
-    }
-
-    override fun sendStatus() {
-    }
-
-    override fun setApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
-    }
-
-    override fun removeApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
-    }
-}

+ 39 - 17
app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt → app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt

@@ -1,24 +1,47 @@
 package im.angry.openeuicc.core
 
-import android.telephony.IccOpenLogicalChannelResponse
 import android.telephony.TelephonyManager
-import android.util.Log
-import com.truphone.lpa.LocalProfileAssistant
-import com.truphone.lpa.impl.LocalProfileAssistantImpl
-import im.angry.openeuicc.util.*
-import java.lang.Exception
+import net.typeblog.lpac_jni.LocalProfileAssistant
+import net.typeblog.lpac_jni.ApduInterface
+import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
+import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
 
-class TelephonyManagerChannel private constructor(
+class TelephonyManagerApduInterface(
+    private val info: EuiccChannelInfo,
+    private val tm: TelephonyManager
+): ApduInterface {
+    override fun connect() {
+        TODO("Not yet implemented")
+    }
+
+    override fun disconnect() {
+        TODO("Not yet implemented")
+    }
+
+    override fun logicalChannelOpen(aid: ByteArray): Int {
+        TODO("Not yet implemented")
+    }
+
+    override fun logicalChannelClose(handle: Int) {
+        TODO("Not yet implemented")
+    }
+
+    override fun transmit(tx: ByteArray): ByteArray {
+        TODO("Not yet implemented")
+    }
+
+}
+
+class TelephonyManagerChannel(
     info: EuiccChannelInfo,
-    private val tm: TelephonyManager,
-    private val channelId: Int
+    private val tm: TelephonyManager
 ) : EuiccChannel(info) {
     companion object {
         private const val TAG = "TelephonyManagerApduChannel"
         private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
 
         // TODO: On Tiramisu, we need to specify the portId also if we want MEP support
-        fun tryConnect(tm: TelephonyManager, info: EuiccChannelInfo): TelephonyManagerChannel? {
+        /*fun tryConnect(tm: TelephonyManager, info: EuiccChannelInfo): TelephonyManagerChannel? {
             try {
                 val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, EUICC_APP_ID, 0)
                 if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
@@ -34,16 +57,15 @@ class TelephonyManagerChannel private constructor(
                 Log.e(TAG, Log.getStackTraceString(e))
                 return null
             }
-        }
+        }*/
     }
 
-    override val lpa: LocalProfileAssistant by lazy {
-        LocalProfileAssistantImpl(TelephonyManagerApduChannel(tm, slotId, channelId))
-    }
+    override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
+        TelephonyManagerApduInterface(info, tm),
+        HttpInterfaceImpl()
+    )
     override val valid: Boolean
         get() = true // TODO: Fix this
 
-    override fun close() {
-        tm.iccCloseLogicalChannelBySlot(slotId, channelId)
-    }
+    override fun close() = lpa.close()
 }

+ 6 - 7
app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt

@@ -3,8 +3,7 @@ package im.angry.openeuicc.service
 import android.service.euicc.*
 import android.telephony.euicc.DownloadableSubscription
 import android.telephony.euicc.EuiccInfo
-import com.truphone.lpa.LocalProfileInfo
-import com.truphone.lpad.progress.Progress
+import net.typeblog.lpac_jni.LocalProfileInfo
 import im.angry.openeuicc.OpenEuiccApplication
 import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.util.*
@@ -18,7 +17,7 @@ class OpenEuiccService : EuiccService() {
             .findEuiccChannelBySlotBlocking(slotId)
 
     override fun onGetEid(slotId: Int): String? =
-        findChannel(slotId)?.lpa?.eid
+        findChannel(slotId)?.lpa?.eID
 
     // When two eSIM cards are present on one device, the Android settings UI
     // gets confused and sets the incorrect slotId for profiles from one of
@@ -103,7 +102,7 @@ class OpenEuiccService : EuiccService() {
                 return RESULT_FIRST_USER
             }
 
-            return if (channel.lpa.deleteProfile(iccid, Progress())) {
+            return if (channel.lpa.deleteProfile(iccid)) {
                 RESULT_OK
             } else {
                 RESULT_FIRST_USER
@@ -135,13 +134,13 @@ class OpenEuiccService : EuiccService() {
                     it.state == LocalProfileInfo.State.Enabled
                 } ?: return RESULT_OK
 
-                return if (channel.lpa.disableProfile(activeProfile.iccid, Progress())) {
+                return if (channel.lpa.disableProfile(activeProfile.iccid)) {
                     RESULT_OK
                 } else {
                     RESULT_FIRST_USER
                 }
             } else {
-                return if (channel.lpa.enableProfile(iccid, Progress())) {
+                return if (channel.lpa.enableProfile(iccid)) {
                     RESULT_OK
                 } else {
                     RESULT_FIRST_USER
@@ -160,7 +159,7 @@ class OpenEuiccService : EuiccService() {
             return RESULT_FIRST_USER
         }
         val success = channel.lpa
-            .setNickname(iccid, nickname)
+            .setNickname(iccid, nickname!!)
         openEuiccApplication.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId)
         return if (success) {
             RESULT_OK

+ 3 - 4
app/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt

@@ -18,8 +18,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
 import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.truphone.lpa.LocalProfileInfo
-import com.truphone.lpad.progress.Progress
+import net.typeblog.lpac_jni.LocalProfileInfo
 import im.angry.openeuicc.R
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
@@ -123,12 +122,12 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
 
     private suspend fun doEnableProfile(iccid: String) =
         withContext(Dispatchers.IO) {
-            channel.lpa.enableProfile(iccid, Progress())
+            channel.lpa.enableProfile(iccid)
         }
 
     private suspend fun doDisableProfile(iccid: String) =
         withContext(Dispatchers.IO) {
-            channel.lpa.disableProfile(iccid, Progress())
+            channel.lpa.disableProfile(iccid)
         }
 
     inner class ViewHolder(private val root: View) : RecyclerView.ViewHolder(root) {

+ 1 - 1
app/src/main/java/im/angry/openeuicc/ui/MainActivity.kt

@@ -95,7 +95,7 @@ class MainActivity : AppCompatActivity() {
             manager.enumerateEuiccChannels()
             manager.knownChannels.forEach {
                 Log.d(TAG, it.name)
-                Log.d(TAG, it.lpa.eid)
+                Log.d(TAG, it.lpa.eID)
                 openEuiccApplication.subscriptionManager.tryRefreshCachedEuiccInfo(it.cardId)
             }
         }

+ 1 - 2
app/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt

@@ -6,7 +6,6 @@ import android.util.Log
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
 import androidx.lifecycle.lifecycleScope
-import com.truphone.lpad.progress.Progress
 import im.angry.openeuicc.R
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -72,6 +71,6 @@ class ProfileDeleteFragment : DialogFragment(), EuiccFragmentMarker {
     }
 
     private suspend fun doDelete() = withContext(Dispatchers.IO) {
-        channel.lpa.deleteProfile(requireArguments().getString("iccid"), Progress())
+        channel.lpa.deleteProfile(requireArguments().getString("iccid")!!)
     }
 }

+ 2 - 3
app/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt

@@ -12,7 +12,6 @@ import androidx.lifecycle.lifecycleScope
 import com.google.android.material.textfield.TextInputLayout
 import com.journeyapps.barcodescanner.ScanContract
 import com.journeyapps.barcodescanner.ScanOptions
-import com.truphone.lpa.progress.DownloadProgress
 import im.angry.openeuicc.R
 import im.angry.openeuicc.util.setWidthPercent
 import kotlinx.coroutines.Dispatchers
@@ -138,11 +137,11 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
     }
 
     private suspend fun doDownloadProfile(server: String, code: String) = withContext(Dispatchers.IO) {
-        channel.lpa.downloadProfile("1\$${server}\$${code}", channel.imei, DownloadProgress().apply {
+        channel.lpa.downloadProfile("1\$${server}\$${code}", channel.imei/*, DownloadProgress().apply {
             setProgressListener { _, _, percentage, _ ->
                 progress.isIndeterminate = false
                 progress.progress = (percentage * 100).toInt()
             }
-        })
+        }*/)
     }
 }

+ 1 - 1
app/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt

@@ -115,7 +115,7 @@ class ProfileRenameFragment : DialogFragment(), EuiccFragmentMarker {
     }
 
     private suspend fun doRename(name: String) = withContext(Dispatchers.IO) {
-        if (!channel.lpa.setNickname(requireArguments().getString("iccid"), name)) {
+        if (!channel.lpa.setNickname(requireArguments().getString("iccid")!!, name)) {
             throw RuntimeException("Profile nickname not changed")
         }
     }

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

@@ -2,7 +2,7 @@ package im.angry.openeuicc.util
 
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
-import com.truphone.lpa.LocalProfileInfo
+import net.typeblog.lpac_jni.LocalProfileInfo
 import java.lang.Exception
 
 val TelephonyManager.supportsDSDS: Boolean

+ 1 - 1
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt

@@ -3,7 +3,7 @@ package net.typeblog.lpac_jni
 /*
  * Should reflect euicc_apdu_interface in lpac/euicc/interface.h
  */
-sealed interface ApduInterface {
+interface ApduInterface {
     fun connect()
     fun disconnect()
     fun logicalChannelOpen(aid: ByteArray): Int

+ 1 - 1
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt

@@ -3,7 +3,7 @@ package net.typeblog.lpac_jni
 /*
  * Should reflect euicc_http_interface in lpac/euicc/interface.h
  */
-sealed interface HttpInterface {
+interface HttpInterface {
     data class HttpResponse(val rcode: Int, val data: ByteArray) {
         override fun equals(other: Any?): Boolean {
             if (this === other) return true

+ 18 - 0
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt

@@ -0,0 +1,18 @@
+package net.typeblog.lpac_jni
+
+interface LocalProfileAssistant {
+    val profiles: List<LocalProfileInfo>
+    val eID: String
+
+    fun enableProfile(iccid: String): Boolean
+    fun disableProfile(iccid: String): Boolean
+    fun deleteProfile(iccid: String): Boolean
+
+    fun downloadProfile(matchingId: String, imei: String)
+
+    fun setNickname(
+        iccid: String, nickname: String
+    ): Boolean
+
+    fun close()
+}

+ 41 - 0
libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt

@@ -0,0 +1,41 @@
+package net.typeblog.lpac_jni
+
+import java.lang.IllegalArgumentException
+
+data class LocalProfileInfo(
+    val iccid: String,
+    val state: State,
+    val name: String,
+    val nickName: String,
+    val providerName: String,
+    val isdpAID: String,
+    val profileClass: Clazz
+) {
+    enum class State {
+        Enabled,
+        Disabled
+    }
+
+    enum class Clazz {
+        Testing,
+        Provisioning,
+        Operational
+    }
+
+    companion object {
+        fun stateFromString(state: String?): State =
+            if (state == "0") {
+                State.Disabled
+            } else {
+                State.Enabled
+            }
+
+        fun classFromString(clazz: String?): Clazz =
+            when (clazz) {
+                "0" -> Clazz.Testing
+                "1" -> Clazz.Provisioning
+                "2" -> Clazz.Operational
+                else -> throw IllegalArgumentException("Unknown profile class $clazz")
+            }
+    }
+}

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

@@ -1,6 +1,6 @@
 package net.typeblog.lpac_jni
 
-private object LpacJni {
+internal object LpacJni {
     init {
         System.loadLibrary("lpac-jni")
     }

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

@@ -0,0 +1,9 @@
+package net.typeblog.lpac_jni.impl
+
+import net.typeblog.lpac_jni.HttpInterface
+
+class HttpInterfaceImpl: HttpInterface {
+    override fun transmit(url: String, tx: ByteArray): HttpInterface.HttpResponse {
+        TODO("Not yet implemented")
+    }
+}

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

@@ -0,0 +1,38 @@
+package net.typeblog.lpac_jni.impl
+
+import net.typeblog.lpac_jni.LpacJni
+import net.typeblog.lpac_jni.ApduInterface
+import net.typeblog.lpac_jni.HttpInterface
+import net.typeblog.lpac_jni.LocalProfileAssistant
+import net.typeblog.lpac_jni.LocalProfileInfo
+
+class LocalProfileAssistantImpl(val apduInterface: ApduInterface, val httpInterface: HttpInterface): LocalProfileAssistant {
+    override val profiles: List<LocalProfileInfo>
+        get() = listOf()
+    override val eID: String
+        get() = "1234567890"
+
+    override fun enableProfile(iccid: String): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun disableProfile(iccid: String): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun deleteProfile(iccid: String): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun downloadProfile(matchingId: String, imei: String) {
+        TODO("Not yet implemented")
+    }
+
+    override fun setNickname(iccid: String, nickname: String): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun close() {
+        // TODO: use es10x_fini
+    }
+}