Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Jul 15, 2024
2 parents debdcf1 + 27627fd commit 29a4008
Show file tree
Hide file tree
Showing 48 changed files with 2,506 additions and 515 deletions.
899 changes: 617 additions & 282 deletions LICENSE

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{Open,Easy}EUICC
---
<img src="https://gitea.angry.im/PeterCxy/OpenEUICC/media/branch/master/art/OpenEUICCBG.svg" width="512" height="300">

A fully free and open-source Local Profile Assistant implementation for Android devices.

Expand Down Expand Up @@ -78,14 +77,14 @@ FAQs
Copyright
===

Everything except `libs/lpac-jni`:
Everything except `libs/lpac-jni` and `art/`:

```
Copyright 2022-2024 OpenEUICC contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, version 2.
as published by the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
Expand Down Expand Up @@ -114,4 +113,6 @@ Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
```
```

`art/`: Courtesy of [Aikoyori](https://github.com/Aikoyori), CC NC-SA 4.0.
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package im.angry.openeuicc.core

import android.content.Context
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbInterface
import android.hardware.usb.UsbManager
import android.se.omapi.SEService
import android.util.Log
import im.angry.openeuicc.core.usb.UsbApduInterface
import im.angry.openeuicc.core.usb.getIoEndpoints
import im.angry.openeuicc.util.*
import java.lang.IllegalArgumentException

open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccChannelFactory {
private var seService: SEService? = null

private val usbManager by lazy {
context.getSystemService(Context.USB_SERVICE) as UsbManager
}

private suspend fun ensureSEService() {
if (seService == null || !seService!!.isConnected) {
seService = connectSEService(context)
Expand Down Expand Up @@ -36,6 +45,17 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha
return null
}

override fun tryOpenUsbEuiccChannel(usbDevice: UsbDevice, usbInterface: UsbInterface): EuiccChannel? {
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
return EuiccChannel(
FakeUiccPortInfoCompat(FakeUiccCardInfoCompat(EuiccChannelManager.USB_CHANNEL_ID)),
UsbApduInterface(conn, bulkIn, bulkOut)
)
}

override fun cleanup() {
seService?.shutdown()
seService = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package im.angry.openeuicc.core

import android.content.Context
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.di.AppContainer
import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
Expand All @@ -23,12 +26,18 @@ open class DefaultEuiccChannelManager(

private val channelCache = mutableListOf<EuiccChannel>()

private var usbChannel: EuiccChannel? = null

private val lock = Mutex()

protected val tm by lazy {
appContainer.telephonyManager
}

private val usbManager by lazy {
context.getSystemService(Context.USB_SERVICE) as UsbManager
}

private val euiccChannelFactory by lazy {
appContainer.euiccChannelFactory
}
Expand All @@ -38,6 +47,15 @@ open class DefaultEuiccChannelManager(

private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
lock.withLock {
if (port.card.physicalSlotIndex == EuiccChannelManager.USB_CHANNEL_ID) {
return if (usbChannel != null && usbChannel!!.valid) {
usbChannel
} else {
usbChannel = null
null
}
}

val existing =
channelCache.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
if (existing != null) {
Expand Down Expand Up @@ -73,6 +91,10 @@ open class DefaultEuiccChannelManager(
override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
runBlocking {
withContext(Dispatchers.IO) {
if (logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
return@withContext usbChannel
}

for (card in uiccCards) {
for (port in card.ports) {
if (port.logicalSlotIndex == logicalSlotId) {
Expand All @@ -88,6 +110,10 @@ open class DefaultEuiccChannelManager(
override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? =
runBlocking {
withContext(Dispatchers.IO) {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
return@withContext usbChannel
}

for (card in uiccCards) {
if (card.physicalSlotIndex != physicalSlotId) continue
for (port in card.ports) {
Expand All @@ -100,6 +126,10 @@ open class DefaultEuiccChannelManager(
}

override suspend fun findAllEuiccChannelsByPhysicalSlot(physicalSlotId: Int): List<EuiccChannel>? {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
return usbChannel?.let { listOf(it) }
}

for (card in uiccCards) {
if (card.physicalSlotIndex != physicalSlotId) continue
return card.ports.mapNotNull { tryOpenEuiccChannel(it) }
Expand All @@ -115,6 +145,10 @@ open class DefaultEuiccChannelManager(

override suspend fun findEuiccChannelByPort(physicalSlotId: Int, portId: Int): EuiccChannel? =
withContext(Dispatchers.IO) {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
return@withContext usbChannel
}

uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
}
Expand All @@ -126,6 +160,8 @@ open class DefaultEuiccChannelManager(
}

override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) return

// 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
channelCache.find { it.slotId == physicalSlotId && it.portId == portId }?.apply {
Expand Down Expand Up @@ -162,11 +198,37 @@ open class DefaultEuiccChannelManager(
}
}

override suspend fun enumerateUsbEuiccChannel(): Pair<UsbDevice?, EuiccChannel?> =
withContext(Dispatchers.IO) {
usbManager.deviceList.values.forEach { device ->
Log.i(TAG, "Scanning USB device ${device.deviceId}:${device.vendorId}")
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, null)
Log.i(TAG, "Found CCID interface on ${device.deviceId}:${device.vendorId}, and has permission; trying to open channel")
try {
val channel = euiccChannelFactory.tryOpenUsbEuiccChannel(device, iface)
if (channel != null && channel.lpa.valid) {
usbChannel = channel
return@withContext Pair(device, channel)
}
} catch (e: Exception) {
// Ignored -- skip forward
e.printStackTrace()
}
Log.i(TAG, "No valid eUICC channel found on USB device ${device.deviceId}:${device.vendorId}")
}
return@withContext Pair(null, null)
}

override fun invalidate() {
for (channel in channelCache) {
channel.close()
}

usbChannel?.close()
usbChannel = null
channelCache.clear()
euiccChannelFactory.cleanup()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package im.angry.openeuicc.core

import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbInterface
import im.angry.openeuicc.util.*

// This class is here instead of inside DI because it contains a bit more logic than just
// "dumb" dependency injection.
interface EuiccChannelFactory {
suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel?

fun tryOpenUsbEuiccChannel(usbDevice: UsbDevice, usbInterface: UsbInterface): EuiccChannel?

/**
* Release all resources used by this EuiccChannelFactory
* Note that the same instance may be reused; any resources allocated must be automatically
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package im.angry.openeuicc.core

import android.hardware.usb.UsbDevice

/**
* EuiccChannelManager holds references to, and manages the lifecycles of, individual
* APDU channels to SIM cards. The find* methods will create channels when needed, and
Expand All @@ -11,13 +13,25 @@ package im.angry.openeuicc.core
* Holding references independent of EuiccChannelManagerService is unsupported.
*/
interface EuiccChannelManager {
companion object {
const val USB_CHANNEL_ID = 99
}

/**
* Scan all possible sources for EuiccChannels, return them and have all
* Scan all possible _device internal_ sources for EuiccChannels, return them and have all
* scanned channels cached; these channels will remain open for the entire lifetime of
* this EuiccChannelManager object, unless disconnected externally or invalidate()'d
*/
suspend fun enumerateEuiccChannels(): List<EuiccChannel>

/**
* Scan all possible USB devices for CCID readers that may contain eUICC cards.
* If found, try to open it for access, and add it to the internal EuiccChannel cache
* as a "port" with id 99. When user interaction is required to obtain permission
* to interact with the device, the second return value (EuiccChannel) will be null.
*/
suspend fun enumerateUsbEuiccChannel(): Pair<UsbDevice?, EuiccChannel?>

/**
* Wait for a slot + port to reconnect (i.e. become valid again)
* If the port is currently valid, this function will return immediately.
Expand Down
Loading

0 comments on commit 29a4008

Please sign in to comment.