|
@@ -32,7 +32,7 @@ open class DefaultEuiccChannelManager(
|
|
|
|
|
|
|
|
private val channelCache = mutableListOf<EuiccChannel>()
|
|
private val channelCache = mutableListOf<EuiccChannel>()
|
|
|
|
|
|
|
|
- private var usbChannel: EuiccChannel? = null
|
|
|
|
|
|
|
+ private var usbChannels = mutableListOf<EuiccChannel>()
|
|
|
|
|
|
|
|
private val lock = Mutex()
|
|
private val lock = Mutex()
|
|
|
|
|
|
|
@@ -51,37 +51,73 @@ open class DefaultEuiccChannelManager(
|
|
|
protected open val uiccCards: Collection<UiccCardInfoCompat>
|
|
protected open val uiccCards: Collection<UiccCardInfoCompat>
|
|
|
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
|
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
|
|
|
|
|
|
|
- private suspend inline fun tryOpenChannelFirstValidAid(openFn: (ByteArray) -> EuiccChannel?): EuiccChannel? {
|
|
|
|
|
- val isdrAidList =
|
|
|
|
|
|
|
+ private suspend inline fun tryOpenChannelWithKnownAids(openFn: (ByteArray, EuiccChannel.SecureElementId) -> EuiccChannel?): List<EuiccChannel> {
|
|
|
|
|
+ var isdrAidList =
|
|
|
parseIsdrAidList(appContainer.preferenceRepository.isdrAidListFlow.first())
|
|
parseIsdrAidList(appContainer.preferenceRepository.isdrAidListFlow.first())
|
|
|
|
|
+ val ret = mutableListOf<EuiccChannel>()
|
|
|
|
|
+ val openedAids = mutableListOf<ByteArray>()
|
|
|
|
|
+ var hasReset = false
|
|
|
|
|
+ var vendorDecider: VendorAidDecider? = null
|
|
|
|
|
+ var seId = 0
|
|
|
|
|
+
|
|
|
|
|
+ outer@ while (true) {
|
|
|
|
|
+ for (aid in isdrAidList) {
|
|
|
|
|
+ if (vendorDecider != null && !vendorDecider.shouldOpenMore(openedAids, aid)) {
|
|
|
|
|
+ break@outer
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return isdrAidList.firstNotNullOfOrNull {
|
|
|
|
|
- Log.i(TAG, "Opening channel, trying ISDR AID ${it.encodeHex()}")
|
|
|
|
|
|
|
+ val channel =
|
|
|
|
|
+ openFn(aid, EuiccChannel.SecureElementId.createFromInt(seId))?.let { channel ->
|
|
|
|
|
+ if (channel.valid) {
|
|
|
|
|
+ seId += 1
|
|
|
|
|
+ channel
|
|
|
|
|
+ } else {
|
|
|
|
|
+ channel.close()
|
|
|
|
|
+ null
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- openFn(it)?.let { channel ->
|
|
|
|
|
- if (channel.valid) {
|
|
|
|
|
- channel
|
|
|
|
|
- } else {
|
|
|
|
|
- channel.close()
|
|
|
|
|
- null
|
|
|
|
|
|
|
+ if (!hasReset) {
|
|
|
|
|
+ val res = channel?.queryVendorAidListTransformation(isdrAidList)
|
|
|
|
|
+ if (res != null) {
|
|
|
|
|
+ // Reset the for loop since we needed to replace the AID list due to vendor-specific code
|
|
|
|
|
+ Log.i(TAG, "AID list replaced, resetting open attempt")
|
|
|
|
|
+ isdrAidList = res.first
|
|
|
|
|
+ vendorDecider = res.second
|
|
|
|
|
+ seId = 0
|
|
|
|
|
+ ret.clear()
|
|
|
|
|
+ openedAids.clear()
|
|
|
|
|
+ channel.close()
|
|
|
|
|
+ hasReset = true // Don't let anything reset again
|
|
|
|
|
+ continue@outer
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (channel != null) {
|
|
|
|
|
+ ret.add(channel)
|
|
|
|
|
+ openedAids.add(aid)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // If we get here we should exit, since the inner loop completed without resetting
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return ret
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
|
|
|
|
|
|
+ private suspend fun tryOpenEuiccChannel(
|
|
|
|
|
+ port: UiccPortInfoCompat,
|
|
|
|
|
+ seId: EuiccChannel.SecureElementId = EuiccChannel.SecureElementId.DEFAULT
|
|
|
|
|
+ ): EuiccChannel? {
|
|
|
lock.withLock {
|
|
lock.withLock {
|
|
|
if (port.card.physicalSlotIndex == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
if (port.card.physicalSlotIndex == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
|
- return if (usbChannel != null && usbChannel!!.valid) {
|
|
|
|
|
- usbChannel
|
|
|
|
|
- } else {
|
|
|
|
|
- usbChannel = null
|
|
|
|
|
- null
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // We only compare seId because we assume we can only open 1 card from USB
|
|
|
|
|
+ return usbChannels.find { it.seId == seId }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
val existing =
|
|
val existing =
|
|
|
- channelCache.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
|
|
|
|
|
|
+ channelCache.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex && it.seId == seId }
|
|
|
if (existing != null) {
|
|
if (existing != null) {
|
|
|
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
|
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
|
|
return existing
|
|
return existing
|
|
@@ -96,12 +132,18 @@ open class DefaultEuiccChannelManager(
|
|
|
return null
|
|
return null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- val channel =
|
|
|
|
|
- tryOpenChannelFirstValidAid { euiccChannelFactory.tryOpenEuiccChannel(port, it) }
|
|
|
|
|
|
|
+ val channels =
|
|
|
|
|
+ tryOpenChannelWithKnownAids { isdrAid, seId ->
|
|
|
|
|
+ euiccChannelFactory.tryOpenEuiccChannel(
|
|
|
|
|
+ port,
|
|
|
|
|
+ isdrAid,
|
|
|
|
|
+ seId
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (channel != null) {
|
|
|
|
|
- channelCache.add(channel)
|
|
|
|
|
- return channel
|
|
|
|
|
|
|
+ if (channels.isNotEmpty()) {
|
|
|
|
|
+ channelCache.addAll(channels)
|
|
|
|
|
+ return channels.find { it.seId == seId }
|
|
|
} else {
|
|
} else {
|
|
|
Log.i(
|
|
Log.i(
|
|
|
TAG,
|
|
TAG,
|
|
@@ -112,10 +154,13 @@ open class DefaultEuiccChannelManager(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- protected suspend fun findEuiccChannelByLogicalSlot(logicalSlotId: Int): EuiccChannel? =
|
|
|
|
|
|
|
+ protected suspend fun findEuiccChannelByLogicalSlot(
|
|
|
|
|
+ logicalSlotId: Int,
|
|
|
|
|
+ seId: EuiccChannel.SecureElementId = EuiccChannel.SecureElementId.DEFAULT
|
|
|
|
|
+ ): EuiccChannel? =
|
|
|
withContext(Dispatchers.IO) {
|
|
withContext(Dispatchers.IO) {
|
|
|
if (logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
if (logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
|
- return@withContext usbChannel
|
|
|
|
|
|
|
+ return@withContext usbChannels.find { it.seId == seId }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (card in uiccCards) {
|
|
for (card in uiccCards) {
|
|
@@ -131,7 +176,7 @@ open class DefaultEuiccChannelManager(
|
|
|
|
|
|
|
|
private suspend fun findAllEuiccChannelsByPhysicalSlot(physicalSlotId: Int): List<EuiccChannel>? {
|
|
private suspend fun findAllEuiccChannelsByPhysicalSlot(physicalSlotId: Int): List<EuiccChannel>? {
|
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
|
- return usbChannel?.let { listOf(it) }
|
|
|
|
|
|
|
+ return usbChannels.ifEmpty { null }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for (card in uiccCards) {
|
|
for (card in uiccCards) {
|
|
@@ -142,14 +187,18 @@ open class DefaultEuiccChannelManager(
|
|
|
return null
|
|
return null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private suspend fun findEuiccChannelByPort(physicalSlotId: Int, portId: Int): EuiccChannel? =
|
|
|
|
|
|
|
+ private suspend fun findEuiccChannelByPort(
|
|
|
|
|
+ physicalSlotId: Int,
|
|
|
|
|
+ portId: Int,
|
|
|
|
|
+ seId: EuiccChannel.SecureElementId = EuiccChannel.SecureElementId.DEFAULT
|
|
|
|
|
+ ): EuiccChannel? =
|
|
|
withContext(Dispatchers.IO) {
|
|
withContext(Dispatchers.IO) {
|
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
|
- return@withContext usbChannel
|
|
|
|
|
|
|
+ return@withContext usbChannels.find { it.seId == seId }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
|
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
|
|
- card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
|
|
|
|
|
|
|
+ card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it, seId) }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -168,15 +217,17 @@ open class DefaultEuiccChannelManager(
|
|
|
return@withContext listOf(0)
|
|
return@withContext listOf(0)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- findAllEuiccChannelsByPhysicalSlot(physicalSlotId)?.map { it.portId } ?: listOf()
|
|
|
|
|
|
|
+ findAllEuiccChannelsByPhysicalSlot(physicalSlotId)?.map { it.portId }?.toSet()?.toList()
|
|
|
|
|
+ ?: listOf()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
override suspend fun <R> withEuiccChannel(
|
|
override suspend fun <R> withEuiccChannel(
|
|
|
physicalSlotId: Int,
|
|
physicalSlotId: Int,
|
|
|
portId: Int,
|
|
portId: Int,
|
|
|
|
|
+ seId: EuiccChannel.SecureElementId,
|
|
|
fn: suspend (EuiccChannel) -> R
|
|
fn: suspend (EuiccChannel) -> R
|
|
|
): R {
|
|
): R {
|
|
|
- val channel = findEuiccChannelByPort(physicalSlotId, portId)
|
|
|
|
|
|
|
+ val channel = findEuiccChannelByPort(physicalSlotId, portId, seId)
|
|
|
?: throw EuiccChannelManager.EuiccChannelNotFoundException()
|
|
?: throw EuiccChannelManager.EuiccChannelNotFoundException()
|
|
|
val wrapper = EuiccChannelWrapper(channel)
|
|
val wrapper = EuiccChannelWrapper(channel)
|
|
|
try {
|
|
try {
|
|
@@ -190,9 +241,10 @@ open class DefaultEuiccChannelManager(
|
|
|
|
|
|
|
|
override suspend fun <R> withEuiccChannel(
|
|
override suspend fun <R> withEuiccChannel(
|
|
|
logicalSlotId: Int,
|
|
logicalSlotId: Int,
|
|
|
|
|
+ seId: EuiccChannel.SecureElementId,
|
|
|
fn: suspend (EuiccChannel) -> R
|
|
fn: suspend (EuiccChannel) -> R
|
|
|
): R {
|
|
): R {
|
|
|
- val channel = findEuiccChannelByLogicalSlot(logicalSlotId)
|
|
|
|
|
|
|
+ val channel = findEuiccChannelByLogicalSlot(logicalSlotId, seId)
|
|
|
?: throw EuiccChannelManager.EuiccChannelNotFoundException()
|
|
?: throw EuiccChannelManager.EuiccChannelNotFoundException()
|
|
|
val wrapper = EuiccChannelWrapper(channel)
|
|
val wrapper = EuiccChannelWrapper(channel)
|
|
|
try {
|
|
try {
|
|
@@ -206,8 +258,8 @@ open class DefaultEuiccChannelManager(
|
|
|
|
|
|
|
|
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
|
|
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
|
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
|
|
- usbChannel?.close()
|
|
|
|
|
- usbChannel = null
|
|
|
|
|
|
|
+ usbChannels.forEach { it.close() }
|
|
|
|
|
+ usbChannels.clear()
|
|
|
} else {
|
|
} else {
|
|
|
// If there is already a valid channel, we close it proactively
|
|
// If there is already a valid channel, we close it proactively
|
|
|
// Sometimes the current channel can linger on for a bit even after it should have become invalid
|
|
// Sometimes the current channel can linger on for a bit even after it should have become invalid
|
|
@@ -223,7 +275,7 @@ open class DefaultEuiccChannelManager(
|
|
|
// tryOpenUsbEuiccChannel() will always try to reopen the channel, even if
|
|
// tryOpenUsbEuiccChannel() will always try to reopen the channel, even if
|
|
|
// a USB channel already exists
|
|
// a USB channel already exists
|
|
|
tryOpenUsbEuiccChannel()
|
|
tryOpenUsbEuiccChannel()
|
|
|
- usbChannel!!
|
|
|
|
|
|
|
+ usbChannels.getOrNull(0)!!
|
|
|
} else {
|
|
} else {
|
|
|
// tryOpenEuiccChannel() will automatically dispose of invalid channels
|
|
// tryOpenEuiccChannel() will automatically dispose of invalid channels
|
|
|
// and recreate when needed
|
|
// and recreate when needed
|
|
@@ -264,6 +316,20 @@ open class DefaultEuiccChannelManager(
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ override fun flowEuiccSecureElements(
|
|
|
|
|
+ slotId: Int,
|
|
|
|
|
+ portId: Int
|
|
|
|
|
+ ): Flow<EuiccChannel.SecureElementId> = flow {
|
|
|
|
|
+ // Emit the "default" channel first
|
|
|
|
|
+ // TODO: This function below should really return a list, not just one SE
|
|
|
|
|
+ findEuiccChannelByPort(slotId, portId, seId = EuiccChannel.SecureElementId.DEFAULT)?.let {
|
|
|
|
|
+ emit(EuiccChannel.SecureElementId.DEFAULT)
|
|
|
|
|
+
|
|
|
|
|
+ channelCache.filter { it.slotId == slotId && it.portId == portId && it.seId != EuiccChannel.SecureElementId.DEFAULT }
|
|
|
|
|
+ .forEach { emit(it.seId) }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
override suspend fun tryOpenUsbEuiccChannel(): Pair<UsbDevice?, Boolean> =
|
|
override suspend fun tryOpenUsbEuiccChannel(): Pair<UsbDevice?, Boolean> =
|
|
|
withContext(Dispatchers.IO) {
|
|
withContext(Dispatchers.IO) {
|
|
|
usbManager.deviceList.values.forEach { device ->
|
|
usbManager.deviceList.values.forEach { device ->
|
|
@@ -277,15 +343,17 @@ open class DefaultEuiccChannelManager(
|
|
|
"Found CCID interface on ${device.deviceId}:${device.vendorId}, and has permission; trying to open channel"
|
|
"Found CCID interface on ${device.deviceId}:${device.vendorId}, and has permission; trying to open channel"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- val ccidCtx = UsbCcidContext.createFromUsbDevice(context, device, iface) ?: return@forEach
|
|
|
|
|
|
|
+ val ccidCtx =
|
|
|
|
|
+ UsbCcidContext.createFromUsbDevice(context, device, iface) ?: return@forEach
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- val channel = tryOpenChannelFirstValidAid {
|
|
|
|
|
- euiccChannelFactory.tryOpenUsbEuiccChannel(ccidCtx, it)
|
|
|
|
|
|
|
+ val channels = tryOpenChannelWithKnownAids { isdrAid, seId ->
|
|
|
|
|
+ euiccChannelFactory.tryOpenUsbEuiccChannel(ccidCtx, isdrAid, seId)
|
|
|
}
|
|
}
|
|
|
- if (channel != null && channel.lpa.valid) {
|
|
|
|
|
|
|
+ if (channels.isNotEmpty() && channels[0].valid) {
|
|
|
ccidCtx.allowDisconnect = true
|
|
ccidCtx.allowDisconnect = true
|
|
|
- usbChannel = channel
|
|
|
|
|
|
|
+ usbChannels.clear()
|
|
|
|
|
+ usbChannels.addAll(channels)
|
|
|
return@withContext Pair(device, true)
|
|
return@withContext Pair(device, true)
|
|
|
}
|
|
}
|
|
|
} catch (e: Exception) {
|
|
} catch (e: Exception) {
|
|
@@ -309,8 +377,8 @@ open class DefaultEuiccChannelManager(
|
|
|
channel.close()
|
|
channel.close()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- usbChannel?.close()
|
|
|
|
|
- usbChannel = null
|
|
|
|
|
|
|
+ usbChannels.forEach { it.close() }
|
|
|
|
|
+ usbChannels.clear()
|
|
|
channelCache.clear()
|
|
channelCache.clear()
|
|
|
euiccChannelFactory.cleanup()
|
|
euiccChannelFactory.cleanup()
|
|
|
}
|
|
}
|