diff --git a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt index ea0bd60..5e87564 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt @@ -8,8 +8,7 @@ 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.bulkPair -import im.angry.openeuicc.core.usb.endpoints +import im.angry.openeuicc.core.usb.getIoEndpoints import im.angry.openeuicc.util.* import java.lang.IllegalArgumentException @@ -62,7 +61,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha } override fun tryOpenUsbEuiccChannel(usbDevice: UsbDevice, usbInterface: UsbInterface): EuiccChannel? { - val (bulkIn, bulkOut) = usbInterface.endpoints.bulkPair + val (bulkIn, bulkOut) = usbInterface.getIoEndpoints() if (bulkIn == null || bulkOut == null) return null val conn = usbManager.openDevice(usbDevice) ?: return null if (!conn.claimInterface(usbInterface, true)) return null diff --git a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt index dd57eab..293042c 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt @@ -5,8 +5,7 @@ import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager import android.telephony.SubscriptionManager import android.util.Log -import im.angry.openeuicc.core.usb.smartCard -import im.angry.openeuicc.core.usb.interfaces +import im.angry.openeuicc.core.usb.getSmartCardInterface import im.angry.openeuicc.di.AppContainer import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers @@ -245,7 +244,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.interfaces.smartCard ?: return@forEach + val iface = device.getSmartCardInterface() ?: 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) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt index 0ecca21..5f399ea 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt @@ -1,8 +1,6 @@ package im.angry.openeuicc.core import im.angry.openeuicc.util.* -import im.angry.openeuicc.vendored.ESTKmeInfo -import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant interface EuiccChannel { @@ -30,10 +28,5 @@ interface EuiccChannel { */ val intrinsicChannelName: String? - /** - * The underlying APDU interface for this channel - */ - val apduInterface: ApduInterface - fun close() } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt index a56b1cc..3da829a 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt @@ -1,7 +1,6 @@ package im.angry.openeuicc.core -import im.angry.openeuicc.util.UiccPortInfoCompat -import im.angry.openeuicc.util.decodeHex +import im.angry.openeuicc.util.* import kotlinx.coroutines.flow.Flow import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant @@ -12,7 +11,7 @@ class EuiccChannelImpl( override val type: String, override val port: UiccPortInfoCompat, override val intrinsicChannelName: String?, - override val apduInterface: ApduInterface, + private val apduInterface: ApduInterface, verboseLoggingFlow: Flow, ignoreTLSCertificateFlow: Flow ) : EuiccChannel { diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt index 09004d3..4204e82 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt @@ -1,7 +1,6 @@ package im.angry.openeuicc.core import im.angry.openeuicc.util.* -import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { @@ -34,8 +33,6 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { get() = channel.valid override val intrinsicChannelName: String? get() = channel.intrinsicChannelName - override val apduInterface: ApduInterface - get() = channel.apduInterface override val atr: ByteArray? get() = channel.atr diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt index 93ef4c7..5123d53 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt +++ b/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_EXCHANGE_LEVEL_EXTENDED_APDU = 0x40000 + private const val FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU = 0x40000 // bVoltageSupport Masks - private const val VOLTAGE_5V0: Byte = 1 - private const val VOLTAGE_3V0: Byte = 2 - private const val VOLTAGE_1V8: Byte = 4 + 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 SLOT_OFFSET = 4 private const val FEATURES_OFFSET = 40 @@ -71,12 +71,10 @@ data class UsbCcidDescription( } enum class Voltage(powerOnValue: Int, mask: Int) { - // @formatter:off - AUTO(0, 0), - V50(1, VOLTAGE_5V0.toInt()), - V30(2, VOLTAGE_3V0.toInt()), - V18(3, VOLTAGE_1V8.toInt()); - // @formatter:on + AUTO(0, 0), _5V(1, VOLTAGE_5V.toInt()), _3V(2, VOLTAGE_3V.toInt()), _1_8V( + 3, + VOLTAGE_1_8V.toInt() + ); val mask = powerOnValue.toByte() val powerOnValue = mask.toByte() @@ -85,11 +83,19 @@ data class UsbCcidDescription( private fun hasFeature(feature: Int): Boolean = (dwFeatures and feature) != 0 - val voltages: List - get() { - if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) return listOf(Voltage.AUTO) - return Voltage.entries.filter { (it.mask.toInt() and bVoltageSupport.toInt()) != 0 } - } + val voltages: Array + 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 hasAutomaticPps: Boolean get() = hasFeature(FEATURE_AUTOMATIC_PPS) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt index 9155721..5ef35af 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt @@ -95,7 +95,6 @@ class UsbCcidTransceiver( data class UsbCcidErrorException(val msg: String, val errorResponse: CcidDataBlock) : Exception(msg) - @Suppress("ArrayInDataClass") data class CcidDataBlock( val dwLength: Int, val bSlot: Byte, @@ -184,26 +183,31 @@ 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()) { - 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)) - } - }) + 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] + ) } 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) @@ -214,7 +218,9 @@ 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 @@ -279,7 +285,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 } @@ -287,13 +293,13 @@ class UsbCcidTransceiver( val startTime = SystemClock.elapsedRealtime() skipAvailableInput() var response: CcidDataBlock? = null - for (voltage in usbCcidDescription.voltages) { - Log.v(TAG, "CCID: attempting to power on with voltage $voltage") + for (v in usbCcidDescription.voltages) { + Log.v(TAG, "CCID: attempting to power on with voltage $v") response = try { - iccPowerOnVoltage(voltage.powerOnValue) + iccPowerOnVoltage(v.powerOnValue) } catch (e: UsbCcidErrorException) { if (e.errorResponse.bError.toInt() == 7) { // Power select error - Log.v(TAG, "CCID: failed to power on with voltage $voltage") + Log.v(TAG, "CCID: failed to power on with voltage $v") iccPowerOff() Log.v(TAG, "CCID: powered off") continue @@ -308,11 +314,8 @@ class UsbCcidTransceiver( val elapsedTime = SystemClock.elapsedRealtime() - startTime Log.d( TAG, - buildString { - append("Usb transport connected") - append(", took ", elapsedTime, "ms") - append(", ATR=", response.data?.encodeHex()) - } + "Usb transport connected, took " + elapsedTime + "ms, ATR=" + + response.data?.encodeHex() ) return response } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt index 877c7fd..edca7a0 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt @@ -6,22 +6,31 @@ import android.hardware.usb.UsbDevice import android.hardware.usb.UsbEndpoint import android.hardware.usb.UsbInterface -class UsbTransportException(message: String) : Exception(message) +class UsbTransportException(msg: String) : Exception(msg) -val UsbDevice.interfaces: Iterable - get() = (0 until interfaceCount).map(::getInterface) - -val Iterable.smartCard: UsbInterface? - get() = find { it.interfaceClass == UsbConstants.USB_CLASS_CSCID } - -val UsbInterface.endpoints: Iterable - get() = (0 until endpointCount).map(::getEndpoint) - -val Iterable.bulkPair: Pair - 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 }, - ) +fun UsbInterface.getIoEndpoints(): Pair { + 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) +} + +fun UsbDevice.getSmartCardInterface(): UsbInterface? { + for (i in 0 until interfaceCount) { + val anInterface = getInterface(i) + if (anInterface.interfaceClass == UsbConstants.USB_CLASS_CSCID) { + return anInterface + } + } + return null +} \ No newline at end of file