refactor: usb ccid driver #149
5 changed files with 61 additions and 78 deletions
|
@ -8,7 +8,8 @@ import android.se.omapi.SEService
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
import im.angry.openeuicc.core.usb.UsbApduInterface
|
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 im.angry.openeuicc.util.*
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tryOpenUsbEuiccChannel(usbDevice: UsbDevice, usbInterface: UsbInterface): EuiccChannel? {
|
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
|
if (bulkIn == null || bulkOut == null) return null
|
||||||
val conn = usbManager.openDevice(usbDevice) ?: return null
|
val conn = usbManager.openDevice(usbDevice) ?: return null
|
||||||
if (!conn.claimInterface(usbInterface, true)) return null
|
if (!conn.claimInterface(usbInterface, true)) return null
|
||||||
|
|
|
@ -5,7 +5,8 @@ import android.hardware.usb.UsbDevice
|
||||||
import android.hardware.usb.UsbManager
|
import android.hardware.usb.UsbManager
|
||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
import android.util.Log
|
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.di.AppContainer
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -244,7 +245,7 @@ open class DefaultEuiccChannelManager(
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
usbManager.deviceList.values.forEach { device ->
|
usbManager.deviceList.values.forEach { device ->
|
||||||
Log.i(TAG, "Scanning USB device ${device.deviceId}:${device.vendorId}")
|
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
|
// 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
|
// need permission to be able to do anything with it
|
||||||
if (!usbManager.hasPermission(device)) return@withContext Pair(device, false)
|
if (!usbManager.hasPermission(device)) return@withContext Pair(device, false)
|
||||||
|
|
|
@ -20,12 +20,12 @@ data class UsbCcidDescription(
|
||||||
|
|
||||||
private const val FEATURE_EXCHANGE_LEVEL_TPDU = 0x10000
|
private const val FEATURE_EXCHANGE_LEVEL_TPDU = 0x10000
|
||||||
private const val FEATURE_EXCHANGE_LEVEL_SHORT_APDU = 0x20000
|
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
|
// bVoltageSupport Masks
|
||||||
private const val VOLTAGE_5V: Byte = 1
|
private const val VOLTAGE_5V0: Byte = 1
|
||||||
private const val VOLTAGE_3V: Byte = 2
|
private const val VOLTAGE_3V0: Byte = 2
|
||||||
private const val VOLTAGE_1_8V: Byte = 4
|
private const val VOLTAGE_1V8: Byte = 4
|
||||||
|
|
||||||
private const val SLOT_OFFSET = 4
|
private const val SLOT_OFFSET = 4
|
||||||
private const val FEATURES_OFFSET = 40
|
private const val FEATURES_OFFSET = 40
|
||||||
|
@ -71,31 +71,24 @@ data class UsbCcidDescription(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Voltage(powerOnValue: Int, mask: Int) {
|
enum class Voltage(powerOnValue: Int, mask: Int) {
|
||||||
AUTO(0, 0), _5V(1, VOLTAGE_5V.toInt()), _3V(2, VOLTAGE_3V.toInt()), _1_8V(
|
// @formatter:off
|
||||||
3,
|
AUTO(0, 0),
|
||||||
VOLTAGE_1_8V.toInt()
|
V50(1, VOLTAGE_5V0.toInt()),
|
||||||
);
|
V30(2, VOLTAGE_3V0.toInt()),
|
||||||
|
V18(3, VOLTAGE_1V8.toInt());
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
val mask = powerOnValue.toByte()
|
val mask = powerOnValue.toByte()
|
||||||
val powerOnValue = mask.toByte()
|
val powerOnValue = mask.toByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasFeature(feature: Int): Boolean =
|
private fun hasFeature(feature: Int) = (dwFeatures and feature) != 0
|
||||||
(dwFeatures and feature) != 0
|
|
||||||
|
|
||||||
val voltages: Array<Voltage>
|
val voltages: List<Voltage>
|
||||||
get() =
|
get() {
|
||||||
if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) {
|
if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) return listOf(Voltage.AUTO)
|
||||||
arrayOf(Voltage.AUTO)
|
return Voltage.entries.filter { (it.mask.toInt() and bVoltageSupport.toInt()) != 0 }
|
||||||
} else {
|
}
|
||||||
Voltage.values().mapNotNull {
|
|
||||||
if ((it.mask.toInt() and bVoltageSupport.toInt()) != 0) {
|
|
||||||
it
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
val hasAutomaticPps: Boolean
|
val hasAutomaticPps: Boolean
|
||||||
get() = hasFeature(FEATURE_AUTOMATIC_PPS)
|
get() = hasFeature(FEATURE_AUTOMATIC_PPS)
|
||||||
|
|
|
@ -95,6 +95,7 @@ class UsbCcidTransceiver(
|
||||||
data class UsbCcidErrorException(val msg: String, val errorResponse: CcidDataBlock) :
|
data class UsbCcidErrorException(val msg: String, val errorResponse: CcidDataBlock) :
|
||||||
Exception(msg)
|
Exception(msg)
|
||||||
|
|
||||||
|
@Suppress("ArrayInDataClass")
|
||||||
data class CcidDataBlock(
|
data class CcidDataBlock(
|
||||||
val dwLength: Int,
|
val dwLength: Int,
|
||||||
val bSlot: Byte,
|
val bSlot: Byte,
|
||||||
|
@ -183,31 +184,26 @@ class UsbCcidTransceiver(
|
||||||
usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
|
usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
|
||||||
)
|
)
|
||||||
if (runBlocking { verboseLoggingFlow.first() }) {
|
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)
|
} while (readBytes <= 0 && attempts-- > 0)
|
||||||
if (readBytes < CCID_HEADER_LENGTH) {
|
if (readBytes < CCID_HEADER_LENGTH) {
|
||||||
throw UsbTransportException("USB-CCID error - failed to receive CCID header")
|
throw UsbTransportException("USB-CCID error - failed to receive CCID header")
|
||||||
}
|
}
|
||||||
if (inputBuffer[0] != MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK.toByte()) {
|
if (inputBuffer[0] != MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK.toByte()) {
|
||||||
if (expectedSequenceNumber != inputBuffer[6]) {
|
throw UsbTransportException(buildString {
|
||||||
throw UsbTransportException(
|
append("USB-CCID error - bad CCID header")
|
||||||
((("USB-CCID error - bad CCID header, type " + inputBuffer[0]) + " (expected " +
|
append(", type ")
|
||||||
MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) + "), sequence number " + inputBuffer[6]
|
append("%d (expected %d)".format(inputBuffer[0], MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK))
|
||||||
) + " (expected " +
|
if (expectedSequenceNumber != inputBuffer[6]) {
|
||||||
expectedSequenceNumber + ")"
|
append(", sequence number ")
|
||||||
)
|
append("%d (expected %d)".format(inputBuffer[6], expectedSequenceNumber))
|
||||||
}
|
}
|
||||||
throw UsbTransportException(
|
})
|
||||||
"USB-CCID error - bad CCID header type " + inputBuffer[0]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
var result = CcidDataBlock.parseHeaderFromBytes(inputBuffer)
|
var result = CcidDataBlock.parseHeaderFromBytes(inputBuffer)
|
||||||
if (expectedSequenceNumber != result.bSeq) {
|
if (expectedSequenceNumber != result.bSeq) {
|
||||||
throw UsbTransportException(
|
throw UsbTransportException("USB-CCID error - expected sequence number $expectedSequenceNumber, got $result")
|
||||||
("USB-CCID error - expected sequence number " +
|
|
||||||
expectedSequenceNumber + ", got " + result)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataBuffer = ByteArray(result.dwLength)
|
val dataBuffer = ByteArray(result.dwLength)
|
||||||
|
@ -218,9 +214,7 @@ class UsbCcidTransceiver(
|
||||||
usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
|
usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS
|
||||||
)
|
)
|
||||||
if (readBytes < 0) {
|
if (readBytes < 0) {
|
||||||
throw UsbTransportException(
|
throw UsbTransportException("USB error - failed reading response data! Header: $result")
|
||||||
"USB error - failed reading response data! Header: $result"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
System.arraycopy(inputBuffer, 0, dataBuffer, bufferedBytes, readBytes)
|
System.arraycopy(inputBuffer, 0, dataBuffer, bufferedBytes, readBytes)
|
||||||
bufferedBytes += readBytes
|
bufferedBytes += readBytes
|
||||||
|
@ -285,7 +279,7 @@ class UsbCcidTransceiver(
|
||||||
}
|
}
|
||||||
val ccidDataBlock = receiveDataBlock(sequenceNumber)
|
val ccidDataBlock = receiveDataBlock(sequenceNumber)
|
||||||
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
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
|
return ccidDataBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,13 +287,13 @@ class UsbCcidTransceiver(
|
||||||
val startTime = SystemClock.elapsedRealtime()
|
val startTime = SystemClock.elapsedRealtime()
|
||||||
skipAvailableInput()
|
skipAvailableInput()
|
||||||
var response: CcidDataBlock? = null
|
var response: CcidDataBlock? = null
|
||||||
for (v in usbCcidDescription.voltages) {
|
for (voltage in usbCcidDescription.voltages) {
|
||||||
Log.v(TAG, "CCID: attempting to power on with voltage $v")
|
Log.v(TAG, "CCID: attempting to power on with voltage $voltage")
|
||||||
response = try {
|
response = try {
|
||||||
iccPowerOnVoltage(v.powerOnValue)
|
iccPowerOnVoltage(voltage.powerOnValue)
|
||||||
} catch (e: UsbCcidErrorException) {
|
} catch (e: UsbCcidErrorException) {
|
||||||
if (e.errorResponse.bError.toInt() == 7) { // Power select error
|
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()
|
iccPowerOff()
|
||||||
Log.v(TAG, "CCID: powered off")
|
Log.v(TAG, "CCID: powered off")
|
||||||
continue
|
continue
|
||||||
|
@ -314,8 +308,11 @@ class UsbCcidTransceiver(
|
||||||
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
val elapsedTime = SystemClock.elapsedRealtime() - startTime
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"Usb transport connected, took " + elapsedTime + "ms, ATR=" +
|
buildString {
|
||||||
response.data?.encodeHex()
|
append("Usb transport connected")
|
||||||
|
append(", took ", elapsedTime, "ms")
|
||||||
|
append(", ATR=", response.data?.encodeHex())
|
||||||
|
}
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,31 +6,22 @@ import android.hardware.usb.UsbDevice
|
||||||
import android.hardware.usb.UsbEndpoint
|
import android.hardware.usb.UsbEndpoint
|
||||||
import android.hardware.usb.UsbInterface
|
import android.hardware.usb.UsbInterface
|
||||||
|
|
||||||
class UsbTransportException(msg: String) : Exception(msg)
|
class UsbTransportException(message: String) : Exception(message)
|
||||||
|
|
||||||
fun UsbInterface.getIoEndpoints(): Pair<UsbEndpoint?, UsbEndpoint?> {
|
val UsbDevice.interfaces: Iterable<UsbInterface>
|
||||||
var bulkIn: UsbEndpoint? = null
|
get() = (0 until interfaceCount).map(::getInterface)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun UsbDevice.getSmartCardInterface(): UsbInterface? {
|
val Iterable<UsbInterface>.smartCard: UsbInterface?
|
||||||
for (i in 0 until interfaceCount) {
|
get() = find { it.interfaceClass == UsbConstants.USB_CLASS_CSCID }
|
||||||
val anInterface = getInterface(i)
|
|
||||||
if (anInterface.interfaceClass == UsbConstants.USB_CLASS_CSCID) {
|
val UsbInterface.endpoints: Iterable<UsbEndpoint>
|
||||||
return anInterface
|
get() = (0 until endpointCount).map(::getEndpoint)
|
||||||
}
|
|
||||||
|
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
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue