refactor: Extract an interface from EuiccChannelManager
All checks were successful
/ build-debug (push) Successful in 4m45s
All checks were successful
/ build-debug (push) Successful in 4m45s
Eventually, we would like EuiccChannelManager to become a Service instead of just any random class.
This commit is contained in:
parent
e48a919335
commit
770083523d
|
@ -5,6 +5,7 @@ import android.telephony.SubscriptionManager
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.EuiccChannelManager
|
||||||
|
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||||
import im.angry.openeuicc.util.PreferenceRepository
|
import im.angry.openeuicc.util.PreferenceRepository
|
||||||
|
|
||||||
open class OpenEuiccApplication : Application() {
|
open class OpenEuiccApplication : Application() {
|
||||||
|
@ -19,7 +20,7 @@ open class OpenEuiccApplication : Application() {
|
||||||
getSystemService(TelephonyManager::class.java)!!
|
getSystemService(TelephonyManager::class.java)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
open val euiccChannelManager: EuiccChannelManager by lazy {
|
open val euiccChannelManager: IEuiccChannelManager by lazy {
|
||||||
EuiccChannelManager(this)
|
EuiccChannelManager(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
open class EuiccChannelManager(protected val context: Context) {
|
open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "EuiccChannelManager"
|
const val TAG = "EuiccChannelManager"
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,9 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
||||||
|
|
||||||
private suspend fun ensureSEService() {
|
private suspend fun ensureSEService() {
|
||||||
if (seService == null) {
|
if (seService == null) {
|
||||||
seService = connectSEService(context)
|
seService = connectSEService(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
||||||
|
@ -52,7 +52,10 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
return OmapiChannel(seService!!, port)
|
return OmapiChannel(seService!!, port)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// Failed
|
// Failed
|
||||||
Log.w(TAG, "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}.")
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -61,7 +64,8 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
ensureSEService()
|
ensureSEService()
|
||||||
val existing = channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
val existing =
|
||||||
|
channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
||||||
return existing
|
return existing
|
||||||
|
@ -90,7 +94,7 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
|
override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
|
||||||
runBlocking {
|
runBlocking {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
for (card in uiccCards) {
|
for (card in uiccCards) {
|
||||||
|
@ -105,54 +109,60 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? = runBlocking {
|
override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? =
|
||||||
withContext(Dispatchers.IO) {
|
runBlocking {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
for (card in uiccCards) {
|
||||||
|
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||||
|
for (port in card.ports) {
|
||||||
|
tryOpenEuiccChannel(port)?.let { return@withContext it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? =
|
||||||
|
runBlocking {
|
||||||
for (card in uiccCards) {
|
for (card in uiccCards) {
|
||||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||||
for (port in card.ports) {
|
return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
|
||||||
tryOpenEuiccChannel(port)?.let { return@withContext it }
|
.ifEmpty { null }
|
||||||
|
}
|
||||||
|
return@runBlocking null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? =
|
||||||
|
runBlocking {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
||||||
|
card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? = runBlocking {
|
override suspend fun enumerateEuiccChannels() {
|
||||||
for (card in uiccCards) {
|
|
||||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
|
||||||
return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
|
|
||||||
.ifEmpty { null }
|
|
||||||
}
|
|
||||||
return@runBlocking null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = runBlocking {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
|
||||||
card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun enumerateEuiccChannels() {
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
ensureSEService()
|
ensureSEService()
|
||||||
|
|
||||||
for (uiccInfo in uiccCards) {
|
for (uiccInfo in uiccCards) {
|
||||||
for (port in uiccInfo.ports) {
|
for (port in uiccInfo.ports) {
|
||||||
if (tryOpenEuiccChannel(port) != null) {
|
if (tryOpenEuiccChannel(port) != null) {
|
||||||
Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}")
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val knownChannels: List<EuiccChannel>
|
override val knownChannels: List<EuiccChannel>
|
||||||
get() = channels.toList()
|
get() = channels.toList()
|
||||||
|
|
||||||
fun invalidate() {
|
override fun invalidate() {
|
||||||
for (channel in channels) {
|
for (channel in channels) {
|
||||||
channel.close()
|
channel.close()
|
||||||
}
|
}
|
||||||
|
@ -161,8 +171,4 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
seService?.shutdown()
|
seService?.shutdown()
|
||||||
seService = null
|
seService = null
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
|
|
||||||
// No-op for unprivileged
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
|
interface IEuiccChannelManager {
|
||||||
|
val knownChannels: List<EuiccChannel>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan all possible sources for EuiccChannels and have them cached for future use
|
||||||
|
*/
|
||||||
|
suspend fun enumerateEuiccChannels()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the EuiccChannel corresponding to a **logical** slot
|
||||||
|
*/
|
||||||
|
fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first EuiccChannel corresponding to a **physical** slot
|
||||||
|
* If the physical slot supports MEP and has multiple ports, it is undefined
|
||||||
|
* which of the two channels will be returned.
|
||||||
|
*/
|
||||||
|
fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all EuiccChannels corresponding to a **physical** slot
|
||||||
|
* Multiple channels are possible in the case of MEP
|
||||||
|
*/
|
||||||
|
fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the EuiccChannel corresponding to a **physical** slot and a port ID
|
||||||
|
*/
|
||||||
|
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate all EuiccChannels previously known by this Manager
|
||||||
|
*/
|
||||||
|
fun invalidate()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If possible, trigger the system to update the cached list of profiles
|
||||||
|
* This is only expected to be implemented when the application is privileged
|
||||||
|
* TODO: Remove this from the common interface
|
||||||
|
*/
|
||||||
|
fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
|
||||||
|
// no-op by default
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -25,8 +24,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
const val TAG = "MainActivity"
|
const val TAG = "MainActivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
protected lateinit var manager: EuiccChannelManager
|
|
||||||
|
|
||||||
private lateinit var spinnerAdapter: ArrayAdapter<String>
|
private lateinit var spinnerAdapter: ArrayAdapter<String>
|
||||||
private lateinit var spinner: Spinner
|
private lateinit var spinner: Spinner
|
||||||
|
|
||||||
|
@ -45,8 +42,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
|
|
||||||
tm = telephonyManager
|
tm = telephonyManager
|
||||||
|
|
||||||
manager = euiccChannelManager
|
|
||||||
|
|
||||||
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
|
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
@ -99,19 +94,19 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
|
|
||||||
private suspend fun init() {
|
private suspend fun init() {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
manager.enumerateEuiccChannels()
|
euiccChannelManager.enumerateEuiccChannels()
|
||||||
manager.knownChannels.forEach {
|
euiccChannelManager.knownChannels.forEach {
|
||||||
Log.d(TAG, "slot ${it.slotId} port ${it.portId}")
|
Log.d(TAG, "slot ${it.slotId} port ${it.portId}")
|
||||||
Log.d(TAG, it.lpa.eID)
|
Log.d(TAG, it.lpa.eID)
|
||||||
// Request the system to refresh the list of profiles every time we start
|
// Request the system to refresh the list of profiles every time we start
|
||||||
// Note that this is currently supposed to be no-op when unprivileged,
|
// Note that this is currently supposed to be no-op when unprivileged,
|
||||||
// but it could change in the future
|
// but it could change in the future
|
||||||
manager.notifyEuiccProfilesChanged(it.logicalSlotId)
|
euiccChannelManager.notifyEuiccProfilesChanged(it.logicalSlotId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
manager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel ->
|
euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel ->
|
||||||
spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId))
|
spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId))
|
||||||
fragments.add(createEuiccManagementFragment(channel))
|
fragments.add(createEuiccManagementFragment(channel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.telephony.TelephonyManager
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.angry.openeuicc.OpenEuiccApplication
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
@ -48,7 +48,7 @@ interface OpenEuiccContextMarker {
|
||||||
val openEuiccApplication: OpenEuiccApplication
|
val openEuiccApplication: OpenEuiccApplication
|
||||||
get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
|
get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
|
||||||
|
|
||||||
val euiccChannelManager: EuiccChannelManager
|
val euiccChannelManager: IEuiccChannelManager
|
||||||
get() = openEuiccApplication.euiccChannelManager
|
get() = openEuiccApplication.euiccChannelManager
|
||||||
|
|
||||||
val telephonyManager: TelephonyManager
|
val telephonyManager: TelephonyManager
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package im.angry.openeuicc
|
package im.angry.openeuicc
|
||||||
|
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||||
import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
|
import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
|
||||||
|
|
||||||
class PrivilegedOpenEuiccApplication: OpenEuiccApplication() {
|
class PrivilegedOpenEuiccApplication: OpenEuiccApplication() {
|
||||||
override val euiccChannelManager: EuiccChannelManager by lazy {
|
override val euiccChannelManager: IEuiccChannelManager by lazy {
|
||||||
PrivilegedEuiccChannelManager(this)
|
PrivilegedEuiccChannelManager(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.telephony.SubscriptionManager
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import android.telephony.UiccSlotMapping
|
import android.telephony.UiccSlotMapping
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean
|
||||||
val TelephonyManager.dsdsEnabled: Boolean
|
val TelephonyManager.dsdsEnabled: Boolean
|
||||||
get() = activeModemCount >= 2
|
get() = activeModemCount >= 2
|
||||||
|
|
||||||
fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
|
fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
euiccManager.enumerateEuiccChannels()
|
euiccManager.enumerateEuiccChannels()
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled:
|
||||||
// Disable eSIM profiles before switching the slot mapping
|
// Disable eSIM profiles before switching the slot mapping
|
||||||
// This ensures that unmapped eSIM ports never have "ghost" profiles enabled
|
// This ensures that unmapped eSIM ports never have "ghost" profiles enabled
|
||||||
fun TelephonyManager.updateSimSlotMapping(
|
fun TelephonyManager.updateSimSlotMapping(
|
||||||
euiccManager: EuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
|
euiccManager: IEuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
|
||||||
currentMapping: Collection<UiccSlotMapping> = simSlotMapping
|
currentMapping: Collection<UiccSlotMapping> = simSlotMapping
|
||||||
) {
|
) {
|
||||||
val unmapped = currentMapping.filterNot { mapping ->
|
val unmapped = currentMapping.filterNot { mapping ->
|
||||||
|
|
Loading…
Reference in a new issue