ソースを参照

refactor: [1/n] Add compat classes for UiccCardInfo and UiccPortInfo

Peter Cai 2 年 前
コミット
2ccfe02204

+ 13 - 13
app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt

@@ -5,9 +5,9 @@ import android.content.Context
 import android.os.Handler
 import android.os.HandlerThread
 import android.se.omapi.SEService
-import android.telephony.UiccCardInfo
 import android.util.Log
 import im.angry.openeuicc.OpenEuiccApplication
+import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.sync.Mutex
@@ -52,27 +52,27 @@ open class EuiccChannelManager(protected val context: Context) {
          }
     }
 
-    protected open fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
+    protected open fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
         // No-op when unprivileged
         return null
     }
 
-    protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
-        Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.slotIndex}")
+    protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
+        Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.physicalSlotIndex}")
         try {
             return OmapiChannel(seService!!, channelInfo)
         } catch (e: IllegalArgumentException) {
             // Failed
-            Log.w(TAG, "OMAPI APDU interface unavailable for slot ${uiccInfo.slotIndex}.")
+            Log.w(TAG, "OMAPI APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}.")
         }
 
         return null
     }
 
-    private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfo): EuiccChannel? {
+    private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfoCompat): EuiccChannel? {
         lock.withLock {
             ensureSEService()
-            val existing = channels.find { it.slotId == uiccInfo.slotIndex }
+            val existing = channels.find { it.slotId == uiccInfo.physicalSlotIndex }
             if (existing != null) {
                 if (existing.valid) {
                     return existing
@@ -83,10 +83,10 @@ open class EuiccChannelManager(protected val context: Context) {
             }
 
             val channelInfo = EuiccChannelInfo(
-                uiccInfo.slotIndex,
+                uiccInfo.physicalSlotIndex,
                 uiccInfo.cardId,
-                "SIM ${uiccInfo.slotIndex}",
-                tm.getImei(uiccInfo.slotIndex) ?: return null,
+                "SIM ${uiccInfo.physicalSlotIndex}",
+                tm.getImei(uiccInfo.physicalSlotIndex) ?: return null,
                 uiccInfo.isRemovable
             )
 
@@ -105,7 +105,7 @@ open class EuiccChannelManager(protected val context: Context) {
     }
 
     private suspend fun findEuiccChannelBySlot(slotId: Int): EuiccChannel? {
-        return tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let {
+        return tm.uiccCardsInfoCompat.find { it.physicalSlotIndex == slotId }?.let {
             tryOpenEuiccChannel(it)
         }
     }
@@ -123,9 +123,9 @@ open class EuiccChannelManager(protected val context: Context) {
         withContext(Dispatchers.IO) {
             ensureSEService()
 
-            for (uiccInfo in tm.uiccCardsInfo) {
+            for (uiccInfo in tm.uiccCardsInfoCompat) {
                 if (tryOpenEuiccChannel(uiccInfo) != null) {
-                    Log.d(TAG, "Found eUICC on slot ${uiccInfo.slotIndex}")
+                    Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex}")
                 }
             }
         }

+ 75 - 0
app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt

@@ -0,0 +1,75 @@
+package im.angry.openeuicc.util
+
+import android.annotation.SuppressLint
+import android.os.Build
+import android.telephony.TelephonyManager
+import android.telephony.UiccCardInfo
+import android.telephony.UiccPortInfo
+import im.angry.openeuicc.util.*
+import java.lang.RuntimeException
+
+@Suppress("DEPRECATION")
+class UiccCardInfoCompat(val inner: UiccCardInfo) {
+    val physicalSlotIndex: Int
+        get() =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                inner.physicalSlotIndex
+            } else {
+                inner.slotIndex
+            }
+
+    val ports: Collection<UiccPortInfoCompat>
+        get() =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                inner.ports.map { UiccPortInfoCompat(it, this) }
+            } else {
+                listOf(UiccPortInfoCompat(null, this))
+            }
+
+    val isEuicc: Boolean
+        get() = inner.isEuicc
+
+    val isRemovable: Boolean
+        get() = inner.isRemovable
+
+    val isMultipleEnabledProfilesSupported: Boolean
+        get() =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                inner.isMultipleEnabledProfilesSupported
+            } else {
+                false
+            }
+
+    val cardId: Int
+        get() = inner.cardId
+}
+
+class UiccPortInfoCompat(private val _inner: Any?, val card: UiccCardInfoCompat) {
+    init {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            check(_inner != null && _inner is UiccPortInfo) {
+                "_inner is not UiccPortInfo on TIRAMISU"
+            }
+        }
+    }
+
+    val inner: UiccPortInfo
+        get() =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                _inner as UiccPortInfo
+            } else {
+                throw RuntimeException("UiccPortInfo does not exist before T")
+            }
+
+    val portIndex: Int
+        get() =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                inner.portIndex
+            } else {
+                0
+            }
+}
+
+val TelephonyManager.uiccCardsInfoCompat: List<UiccCardInfoCompat>
+    @SuppressLint("MissingPermission")
+    get() = uiccCardsInfo.map { UiccCardInfoCompat(it) }

+ 3 - 4
app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt

@@ -1,7 +1,6 @@
 package im.angry.openeuicc.core
 
 import android.content.Context
-import android.telephony.UiccCardInfo
 import android.util.Log
 import im.angry.openeuicc.OpenEuiccApplication
 import im.angry.openeuicc.util.*
@@ -11,7 +10,7 @@ import java.lang.IllegalArgumentException
 class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) {
     override fun checkPrivileges() = true // TODO: Implement proper system app check
 
-    override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
+    override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
         if (uiccInfo.isRemovable) {
             // Attempt unprivileged (OMAPI) before TelephonyManager
             // but still try TelephonyManager in case OMAPI is broken
@@ -19,13 +18,13 @@ class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(conte
         }
 
         if (uiccInfo.isEuicc) {
-            Log.i(TAG, "Trying TelephonyManager for slot ${uiccInfo.slotIndex}")
+            Log.i(TAG, "Trying TelephonyManager for slot ${uiccInfo.physicalSlotIndex}")
             // TODO: On Tiramisu, we should also connect all available "ports" for MEP support
             try {
                 return TelephonyManagerChannel(channelInfo, tm)
             } catch (e: IllegalArgumentException) {
                 // Failed
-                Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${uiccInfo.slotIndex}, falling back")
+                Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}, falling back")
             }
         }
         return null

+ 39 - 0
app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt

@@ -0,0 +1,39 @@
+package im.angry.openeuicc.util
+
+import android.os.Build
+import android.telephony.IccOpenLogicalChannelResponse
+import android.telephony.TelephonyManager
+
+// TODO: Usage of *byPort APIs will still break build in-tree on lower AOSP versions
+//       Maybe older versions should simply include hidden-apis-shim when building?
+fun TelephonyManager.iccOpenLogicalChannelByPortCompat(
+    slotIndex: Int, portIndex: Int, aid: String?, p2: Int
+): IccOpenLogicalChannelResponse =
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        iccOpenLogicalChannelByPort(slotIndex, portIndex, aid, p2)
+    } else {
+        iccOpenLogicalChannelBySlot(slotIndex, aid, p2)
+    }
+
+fun TelephonyManager.iccCloseLogicalChannelByPortCompat(
+    slotIndex: Int, portIndex: Int, channel: Int
+) =
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        iccCloseLogicalChannelByPort(slotIndex, portIndex, channel)
+    } else {
+        iccCloseLogicalChannelBySlot(slotIndex, channel)
+    }
+
+fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat(
+    slotIndex: Int, portIndex: Int, channel: Int,
+    cla: Int, inst: Int, p1: Int, p2: Int, p3: Int, data: String?
+): String? =
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+        iccTransmitApduLogicalChannelByPort(
+            slotIndex, portIndex, channel, cla, inst, p1, p2, p3, data
+        )
+    } else {
+        iccTransmitApduLogicalChannelBySlot(
+            slotIndex, channel, cla, inst, p1, p2, p3, data
+        )
+    }

+ 36 - 1
libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt

@@ -14,12 +14,24 @@ private val iccOpenLogicalChannelBySlot: Method by lazy {
         Int::class.java, String::class.java, Int::class.java
     )
 }
+private val iccOpenLogicalChannelByPort: Method by lazy {
+    TelephonyManager::class.java.getMethod(
+        "iccOpenLogicalChannelByPort",
+        Int::class.java, Int::class.java, String::class.java, Int::class.java
+    )
+}
 private val iccCloseLogicalChannelBySlot: Method by lazy {
     TelephonyManager::class.java.getMethod(
         "iccCloseLogicalChannelBySlot",
         Int::class.java, Int::class.java
     )
 }
+private val iccCloseLogicalChannelByPort: Method by lazy {
+    TelephonyManager::class.java.getMethod(
+        "iccCloseLogicalChannelByPort",
+        Int::class.java, Int::class.java, Int::class.java
+    )
+}
 private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
     TelephonyManager::class.java.getMethod(
         "iccTransmitApduLogicalChannelBySlot",
@@ -27,15 +39,30 @@ private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
         Int::class.java, Int::class.java, Int::class.java, String::class.java
     )
 }
+private val iccTransmitApduLogicalChannelByPort: Method by lazy {
+    TelephonyManager::class.java.getMethod(
+        "iccTransmitApduLogicalChannelByPort",
+        Int::class.java, Int::class.java, Int::class.java, Int::class.java, Int::class.java,
+        Int::class.java, Int::class.java, Int::class.java, String::class.java
+    )
+}
 
 fun TelephonyManager.iccOpenLogicalChannelBySlot(
-    slotId: Int, appletId: String, p2: Int
+    slotId: Int, appletId: String?, p2: Int
 ): IccOpenLogicalChannelResponse =
     iccOpenLogicalChannelBySlot.invoke(this, slotId, appletId, p2) as IccOpenLogicalChannelResponse
 
+fun TelephonyManager.iccOpenLogicalChannelByPort(
+    slotId: Int, portId: Int, appletId: String?, p2: Int
+): IccOpenLogicalChannelResponse =
+    iccOpenLogicalChannelByPort.invoke(this, slotId, portId, appletId, p2) as IccOpenLogicalChannelResponse
+
 fun TelephonyManager.iccCloseLogicalChannelBySlot(slotId: Int, channel: Int): Boolean =
     iccCloseLogicalChannelBySlot.invoke(this, slotId, channel) as Boolean
 
+fun TelephonyManager.iccCloseLogicalChannelByPort(slotId: Int, portId: Int, channel: Int): Boolean =
+    iccCloseLogicalChannelByPort.invoke(this, slotId, portId, channel) as Boolean
+
 fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
     slotId: Int, channel: Int, cla: Int, instruction: Int,
     p1: Int, p2: Int, p3: Int, data: String?
@@ -44,6 +71,14 @@ fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
         this, slotId, channel, cla, instruction, p1, p2, p3, data
     ) as String?
 
+fun TelephonyManager.iccTransmitApduLogicalChannelByPort(
+    slotId: Int, portId: Int, channel: Int, cla: Int, instruction: Int,
+    p1: Int, p2: Int, p3: Int, data: String?
+): String? =
+    iccTransmitApduLogicalChannelByPort.invoke(
+        this, slotId, portId, channel, cla, instruction, p1, p2, p3, data
+    ) as String?
+
 private val requestEmbeddedSubscriptionInfoListRefresh: Method by lazy {
     SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java)
 }