瀏覽代碼

refactor: usb ccid driver (#149)

Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/149
Co-authored-by: septs <github@septs.pw>
Co-committed-by: septs <github@septs.pw>
septs 11 月之前
父節點
當前提交
d5aefcaec7

+ 3 - 2
app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt

@@ -8,7 +8,8 @@ import android.se.omapi.SEService
 import android.util.Log
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.core.usb.UsbApduInterface
-import im.angry.openeuicc.core.usb.getIoEndpoints
+import im.angry.openeuicc.core.usb.bulkPair
+import im.angry.openeuicc.core.usb.endpoints
 import im.angry.openeuicc.util.*
 import java.lang.IllegalArgumentException
 
@@ -61,7 +62,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha
     }
 
     override fun tryOpenUsbEuiccChannel(usbDevice: UsbDevice, usbInterface: UsbInterface): EuiccChannel? {
-        val (bulkIn, bulkOut) = usbInterface.getIoEndpoints()
+        val (bulkIn, bulkOut) = usbInterface.endpoints.bulkPair
         if (bulkIn == null || bulkOut == null) return null
         val conn = usbManager.openDevice(usbDevice) ?: return null
         if (!conn.claimInterface(usbInterface, true)) return null

+ 3 - 2
app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt

@@ -5,7 +5,8 @@ import android.hardware.usb.UsbDevice
 import android.hardware.usb.UsbManager
 import android.telephony.SubscriptionManager
 import android.util.Log
-import im.angry.openeuicc.core.usb.getSmartCardInterface
+import im.angry.openeuicc.core.usb.smartCard
+import im.angry.openeuicc.core.usb.interfaces
 import im.angry.openeuicc.di.AppContainer
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
@@ -244,7 +245,7 @@ open class DefaultEuiccChannelManager(
         withContext(Dispatchers.IO) {
             usbManager.deviceList.values.forEach { device ->
                 Log.i(TAG, "Scanning USB device ${device.deviceId}:${device.vendorId}")
-                val iface = device.getSmartCardInterface() ?: return@forEach
+                val iface = device.interfaces.smartCard ?: return@forEach
                 // If we don't have permission, tell UI code that we found a candidate device, but we
                 // need permission to be able to do anything with it
                 if (!usbManager.hasPermission(device)) return@withContext Pair(device, false)

+ 16 - 23
app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt

@@ -20,12 +20,12 @@ data class UsbCcidDescription(
 
         private const val FEATURE_EXCHANGE_LEVEL_TPDU = 0x10000
         private const val FEATURE_EXCHANGE_LEVEL_SHORT_APDU = 0x20000
-        private const val FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU = 0x40000
+        private const val FEATURE_EXCHANGE_LEVEL_EXTENDED_APDU = 0x40000
 
         // bVoltageSupport Masks
-        private const val VOLTAGE_5V: Byte = 1
-        private const val VOLTAGE_3V: Byte = 2
-        private const val VOLTAGE_1_8V: Byte = 4
+        private const val VOLTAGE_5V0: Byte = 1
+        private const val VOLTAGE_3V0: Byte = 2
+        private const val VOLTAGE_1V8: Byte = 4
 
         private const val SLOT_OFFSET = 4
         private const val FEATURES_OFFSET = 40
@@ -71,31 +71,24 @@ data class UsbCcidDescription(
     }
 
     enum class Voltage(powerOnValue: Int, mask: Int) {
-        AUTO(0, 0), _5V(1, VOLTAGE_5V.toInt()), _3V(2, VOLTAGE_3V.toInt()), _1_8V(
-            3,
-            VOLTAGE_1_8V.toInt()
-        );
+        // @formatter:off
+        AUTO(0, 0),
+        V50(1, VOLTAGE_5V0.toInt()),
+        V30(2, VOLTAGE_3V0.toInt()),
+        V18(3, VOLTAGE_1V8.toInt());
+        // @formatter:on
 
         val mask = powerOnValue.toByte()
         val powerOnValue = mask.toByte()
     }
 
-    private fun hasFeature(feature: Int): Boolean =
-        (dwFeatures and feature) != 0
+    private fun hasFeature(feature: Int) = (dwFeatures and feature) != 0
 
-    val voltages: Array<Voltage>
-        get() =
-            if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) {
-                arrayOf(Voltage.AUTO)
-            } else {
-                Voltage.values().mapNotNull {
-                    if ((it.mask.toInt() and bVoltageSupport.toInt()) != 0) {
-                        it
-                    } else {
-                        null
-                    }
-                }.toTypedArray()
-            }
+    val voltages: List<Voltage>
+        get() {
+            if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) return listOf(Voltage.AUTO)
+            return Voltage.entries.filter { (it.mask.toInt() and bVoltageSupport.toInt()) != 0 }
+        }
 
     val hasAutomaticPps: Boolean
         get() = hasFeature(FEATURE_AUTOMATIC_PPS)

+ 23 - 26
app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt

@@ -95,6 +95,7 @@ class UsbCcidTransceiver(
     data class UsbCcidErrorException(val msg: String, val errorResponse: CcidDataBlock) :
         Exception(msg)
 
+    @Suppress("ArrayInDataClass")
     data class CcidDataBlock(
         val dwLength: Int,
         val bSlot: Byte,
@@ -183,31 +184,26 @@ class UsbCcidTransceiver(
                 usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
             )
             if (runBlocking { verboseLoggingFlow.first() }) {
-                Log.d(TAG, "Received " + readBytes + " bytes: " + inputBuffer.encodeHex())
+                Log.d(TAG, "Received $readBytes bytes: ${inputBuffer.encodeHex()}")
             }
         } while (readBytes <= 0 && attempts-- > 0)
         if (readBytes < CCID_HEADER_LENGTH) {
             throw UsbTransportException("USB-CCID error - failed to receive CCID header")
         }
         if (inputBuffer[0] != MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK.toByte()) {
-            if (expectedSequenceNumber != inputBuffer[6]) {
-                throw UsbTransportException(
-                    ((("USB-CCID error - bad CCID header, type " + inputBuffer[0]) + " (expected " +
-                            MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) + "), sequence number " + inputBuffer[6]
-                            ) + " (expected " +
-                            expectedSequenceNumber + ")"
-                )
-            }
-            throw UsbTransportException(
-                "USB-CCID error - bad CCID header type " + inputBuffer[0]
-            )
+            throw UsbTransportException(buildString {
+                append("USB-CCID error - bad CCID header")
+                append(", type ")
+                append("%d (expected %d)".format(inputBuffer[0], MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK))
+                if (expectedSequenceNumber != inputBuffer[6]) {
+                    append(", sequence number ")
+                    append("%d (expected %d)".format(inputBuffer[6], expectedSequenceNumber))
+                }
+            })
         }
         var result = CcidDataBlock.parseHeaderFromBytes(inputBuffer)
         if (expectedSequenceNumber != result.bSeq) {
-            throw UsbTransportException(
-                ("USB-CCID error - expected sequence number " +
-                        expectedSequenceNumber + ", got " + result)
-            )
+            throw UsbTransportException("USB-CCID error - expected sequence number $expectedSequenceNumber, got $result")
         }
 
         val dataBuffer = ByteArray(result.dwLength)
@@ -218,9 +214,7 @@ class UsbCcidTransceiver(
                 usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
             )
             if (readBytes < 0) {
-                throw UsbTransportException(
-                    "USB error - failed reading response data! Header: $result"
-                )
+                throw UsbTransportException("USB error - failed reading response data! Header: $result")
             }
             System.arraycopy(inputBuffer, 0, dataBuffer, bufferedBytes, readBytes)
             bufferedBytes += readBytes
@@ -285,7 +279,7 @@ class UsbCcidTransceiver(
         }
         val ccidDataBlock = receiveDataBlock(sequenceNumber)
         val elapsedTime = SystemClock.elapsedRealtime() - startTime
-        Log.d(TAG, "USB XferBlock call took " + elapsedTime + "ms")
+        Log.d(TAG, "USB XferBlock call took ${elapsedTime}ms")
         return ccidDataBlock
     }
 
@@ -293,13 +287,13 @@ class UsbCcidTransceiver(
         val startTime = SystemClock.elapsedRealtime()
         skipAvailableInput()
         var response: CcidDataBlock? = null
-        for (v in usbCcidDescription.voltages) {
-            Log.v(TAG, "CCID: attempting to power on with voltage $v")
+        for (voltage in usbCcidDescription.voltages) {
+            Log.v(TAG, "CCID: attempting to power on with voltage $voltage")
             response = try {
-                iccPowerOnVoltage(v.powerOnValue)
+                iccPowerOnVoltage(voltage.powerOnValue)
             } catch (e: UsbCcidErrorException) {
                 if (e.errorResponse.bError.toInt() == 7) { // Power select error
-                    Log.v(TAG, "CCID: failed to power on with voltage $v")
+                    Log.v(TAG, "CCID: failed to power on with voltage $voltage")
                     iccPowerOff()
                     Log.v(TAG, "CCID: powered off")
                     continue
@@ -314,8 +308,11 @@ class UsbCcidTransceiver(
         val elapsedTime = SystemClock.elapsedRealtime() - startTime
         Log.d(
             TAG,
-            "Usb transport connected, took " + elapsedTime + "ms, ATR=" +
-                    response.data?.encodeHex()
+            buildString {
+                append("Usb transport connected")
+                append(", took ", elapsedTime, "ms")
+                append(", ATR=", response.data?.encodeHex())
+            }
         )
         return response
     }

+ 16 - 25
app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt

@@ -6,31 +6,22 @@ import android.hardware.usb.UsbDevice
 import android.hardware.usb.UsbEndpoint
 import android.hardware.usb.UsbInterface
 
-class UsbTransportException(msg: String) : Exception(msg)
+class UsbTransportException(message: String) : Exception(message)
 
-fun UsbInterface.getIoEndpoints(): Pair<UsbEndpoint?, UsbEndpoint?> {
-    var bulkIn: UsbEndpoint? = null
-    var bulkOut: UsbEndpoint? = null
-    for (i in 0 until endpointCount) {
-        val endpoint = getEndpoint(i)
-        if (endpoint.type != UsbConstants.USB_ENDPOINT_XFER_BULK) {
-            continue
-        }
-        if (endpoint.direction == UsbConstants.USB_DIR_IN) {
-            bulkIn = endpoint
-        } else if (endpoint.direction == UsbConstants.USB_DIR_OUT) {
-            bulkOut = endpoint
-        }
-    }
-    return Pair(bulkIn, bulkOut)
-}
+val UsbDevice.interfaces: Iterable<UsbInterface>
+    get() = (0 until interfaceCount).map(::getInterface)
+
+val Iterable<UsbInterface>.smartCard: UsbInterface?
+    get() = find { it.interfaceClass == UsbConstants.USB_CLASS_CSCID }
+
+val UsbInterface.endpoints: Iterable<UsbEndpoint>
+    get() = (0 until endpointCount).map(::getEndpoint)
 
-fun UsbDevice.getSmartCardInterface(): UsbInterface? {
-    for (i in 0 until interfaceCount) {
-        val anInterface = getInterface(i)
-        if (anInterface.interfaceClass == UsbConstants.USB_CLASS_CSCID) {
-            return anInterface
-        }
+val Iterable<UsbEndpoint>.bulkPair: Pair<UsbEndpoint?, UsbEndpoint?>
+    get() {
+        val endpoints = filter { it.type == UsbConstants.USB_ENDPOINT_XFER_BULK }
+        return Pair(
+            endpoints.find { it.direction == UsbConstants.USB_DIR_IN },
+            endpoints.find { it.direction == UsbConstants.USB_DIR_OUT },
+        )
     }
-    return null
-}