refactor: Remove all usage of knownChannels
enumerateEuiccChannels() should return all discovered channels on its own. Outside classes should never access the cached open channels directly.
This commit is contained in:
parent
5785fe2e7c
commit
1a22854d05
|
@ -21,7 +21,7 @@ open class DefaultEuiccChannelManager(
|
||||||
const val TAG = "EuiccChannelManager"
|
const val TAG = "EuiccChannelManager"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val channels = mutableListOf<EuiccChannel>()
|
private val channelCache = mutableListOf<EuiccChannel>()
|
||||||
|
|
||||||
private val lock = Mutex()
|
private val lock = Mutex()
|
||||||
|
|
||||||
|
@ -39,13 +39,13 @@ open class DefaultEuiccChannelManager(
|
||||||
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
val existing =
|
val existing =
|
||||||
channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
channelCache.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
|
||||||
} else {
|
} else {
|
||||||
existing.close()
|
existing.close()
|
||||||
channels.remove(existing)
|
channelCache.remove(existing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ open class DefaultEuiccChannelManager(
|
||||||
val channel = euiccChannelFactory.tryOpenEuiccChannel(port) ?: return null
|
val channel = euiccChannelFactory.tryOpenEuiccChannel(port) ?: return null
|
||||||
|
|
||||||
if (channel.valid) {
|
if (channel.valid) {
|
||||||
channels.add(channel)
|
channelCache.add(channel)
|
||||||
return channel
|
return channel
|
||||||
} else {
|
} else {
|
||||||
Log.i(
|
Log.i(
|
||||||
|
@ -128,7 +128,7 @@ open class DefaultEuiccChannelManager(
|
||||||
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
|
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
|
||||||
// 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
|
||||||
channels.find { it.slotId == physicalSlotId && it.portId == portId }?.apply {
|
channelCache.find { it.slotId == physicalSlotId && it.portId == portId }?.apply {
|
||||||
if (valid) close()
|
if (valid) close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,30 +148,26 @@ open class DefaultEuiccChannelManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun enumerateEuiccChannels() {
|
override suspend fun enumerateEuiccChannels(): List<EuiccChannel> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
for (uiccInfo in uiccCards) {
|
uiccCards.flatMap { info ->
|
||||||
for (port in uiccInfo.ports) {
|
info.ports.mapNotNull { port ->
|
||||||
if (tryOpenEuiccChannel(port) != null) {
|
tryOpenEuiccChannel(port)?.also {
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}"
|
"Found eUICC on slot ${info.physicalSlotIndex} port ${port.portIndex}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val knownChannels: List<EuiccChannel>
|
|
||||||
get() = channels.toList()
|
|
||||||
|
|
||||||
override fun invalidate() {
|
override fun invalidate() {
|
||||||
for (channel in channels) {
|
for (channel in channelCache) {
|
||||||
channel.close()
|
channel.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
channels.clear()
|
channelCache.clear()
|
||||||
euiccChannelFactory.cleanup()
|
euiccChannelFactory.cleanup()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,22 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EuiccChannelManager holds references to, and manages the lifecycles of, individual
|
||||||
|
* APDU channels to SIM cards. The find* methods will create channels when needed, and
|
||||||
|
* all opened channels will be held in an internal cache until invalidate() is called
|
||||||
|
* or when this instance is destroyed.
|
||||||
|
*
|
||||||
|
* To precisely control the lifecycle of this object itself (and thus its cached channels),
|
||||||
|
* all other compoents must access EuiccChannelManager objects through EuiccChannelManagerService.
|
||||||
|
* Holding references independent of EuiccChannelManagerService is unsupported.
|
||||||
|
*/
|
||||||
interface EuiccChannelManager {
|
interface EuiccChannelManager {
|
||||||
val knownChannels: List<EuiccChannel>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan all possible sources for EuiccChannels and have them cached for future use
|
* Scan all possible 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()
|
suspend fun enumerateEuiccChannels(): List<EuiccChannel>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for a slot + port to reconnect (i.e. become valid again)
|
* Wait for a slot + port to reconnect (i.e. become valid again)
|
||||||
|
@ -41,7 +51,7 @@ interface EuiccChannelManager {
|
||||||
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel?
|
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate all EuiccChannels previously known by this Manager
|
* Invalidate all EuiccChannels previously cached by this Manager
|
||||||
*/
|
*/
|
||||||
fun invalidate()
|
fun invalidate()
|
||||||
|
|
||||||
|
|
|
@ -9,23 +9,23 @@ import kotlinx.coroutines.withContext
|
||||||
class DirectProfileDownloadActivity : BaseEuiccAccessActivity(), SlotSelectFragment.SlotSelectedListener, OpenEuiccContextMarker {
|
class DirectProfileDownloadActivity : BaseEuiccAccessActivity(), SlotSelectFragment.SlotSelectedListener, OpenEuiccContextMarker {
|
||||||
override fun onInit() {
|
override fun onInit() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
val knownChannels = withContext(Dispatchers.IO) {
|
||||||
euiccChannelManager.enumerateEuiccChannels()
|
euiccChannelManager.enumerateEuiccChannels()
|
||||||
}
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
euiccChannelManager.knownChannels.isEmpty() -> {
|
knownChannels.isEmpty() -> {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
euiccChannelManager.knownChannels.hasMultipleChips -> {
|
knownChannels.hasMultipleChips -> {
|
||||||
SlotSelectFragment.newInstance()
|
SlotSelectFragment.newInstance(knownChannels.sortedBy { it.logicalSlotId })
|
||||||
.show(supportFragmentManager, SlotSelectFragment.TAG)
|
.show(supportFragmentManager, SlotSelectFragment.TAG)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// If the device has only one eSIM "chip" (but may be mapped to multiple slots),
|
// If the device has only one eSIM "chip" (but may be mapped to multiple slots),
|
||||||
// we can skip the slot selection dialog since there is only one chip to save to.
|
// we can skip the slot selection dialog since there is only one chip to save to.
|
||||||
onSlotSelected(euiccChannelManager.knownChannels[0].slotId,
|
onSlotSelected(knownChannels[0].slotId,
|
||||||
euiccChannelManager.knownChannels[0].portId)
|
knownChannels[0].portId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,8 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun init() {
|
private suspend fun init() {
|
||||||
withContext(Dispatchers.IO) {
|
val knownChannels = withContext(Dispatchers.IO) {
|
||||||
euiccChannelManager.enumerateEuiccChannels()
|
euiccChannelManager.enumerateEuiccChannels().onEach {
|
||||||
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
|
||||||
|
@ -108,7 +107,7 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel ->
|
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(appContainer.uiComponentFactory.createEuiccManagementFragment(channel))
|
fragments.add(appContainer.uiComponentFactory.createEuiccManagementFragment(channel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,14 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SlotSelectFragment"
|
const val TAG = "SlotSelectFragment"
|
||||||
|
|
||||||
fun newInstance(): SlotSelectFragment {
|
fun newInstance(knownChannels: List<EuiccChannel>): SlotSelectFragment {
|
||||||
return SlotSelectFragment()
|
return SlotSelectFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putIntArray("slotIds", knownChannels.map { it.slotId }.toIntArray())
|
||||||
|
putIntArray("logicalSlotIds", knownChannels.map { it.logicalSlotId }.toIntArray())
|
||||||
|
putIntArray("portIds", knownChannels.map { it.portId }.toIntArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,10 +34,10 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker
|
||||||
|
|
||||||
private lateinit var toolbar: Toolbar
|
private lateinit var toolbar: Toolbar
|
||||||
private lateinit var spinner: Spinner
|
private lateinit var spinner: Spinner
|
||||||
private val channels: List<EuiccChannel> by lazy {
|
private lateinit var adapter: ArrayAdapter<String>
|
||||||
(requireActivity() as BaseEuiccAccessActivity).euiccChannelManager
|
private lateinit var slotIds: IntArray
|
||||||
.knownChannels.sortedBy { it.logicalSlotId }
|
private lateinit var logicalSlotIds: IntArray
|
||||||
}
|
private lateinit var portIds: IntArray
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -44,26 +50,35 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker
|
||||||
toolbar.setTitle(R.string.slot_select)
|
toolbar.setTitle(R.string.slot_select)
|
||||||
toolbar.inflateMenu(R.menu.fragment_slot_select)
|
toolbar.inflateMenu(R.menu.fragment_slot_select)
|
||||||
|
|
||||||
val adapter = ArrayAdapter<String>(inflater.context, R.layout.spinner_item)
|
adapter = ArrayAdapter<String>(inflater.context, R.layout.spinner_item)
|
||||||
|
|
||||||
spinner = view.requireViewById(R.id.spinner)
|
spinner = view.requireViewById(R.id.spinner)
|
||||||
spinner.adapter = adapter
|
spinner.adapter = adapter
|
||||||
|
|
||||||
channels.forEach { channel ->
|
return view
|
||||||
adapter.add(getString(R.string.channel_name_format, channel.logicalSlotId))
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
|
||||||
|
slotIds = requireArguments().getIntArray("slotIds")!!
|
||||||
|
logicalSlotIds = requireArguments().getIntArray("logicalSlotIds")!!
|
||||||
|
portIds = requireArguments().getIntArray("portIds")!!
|
||||||
|
|
||||||
|
logicalSlotIds.forEach { id ->
|
||||||
|
adapter.add(getString(R.string.channel_name_format, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar.setNavigationOnClickListener {
|
toolbar.setNavigationOnClickListener {
|
||||||
(requireActivity() as SlotSelectedListener).onSlotSelectCancelled()
|
(requireActivity() as SlotSelectedListener).onSlotSelectCancelled()
|
||||||
}
|
}
|
||||||
toolbar.setOnMenuItemClickListener {
|
toolbar.setOnMenuItemClickListener {
|
||||||
val channel = channels[spinner.selectedItemPosition]
|
val slotId = slotIds[spinner.selectedItemPosition]
|
||||||
(requireActivity() as SlotSelectedListener).onSlotSelected(channel.slotId, channel.portId)
|
val portId = portIds[spinner.selectedItemPosition]
|
||||||
|
(requireActivity() as SlotSelectedListener).onSlotSelected(slotId, portId)
|
||||||
dismiss()
|
dismiss()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
|
@ -15,12 +15,12 @@ val TelephonyManager.dsdsEnabled: Boolean
|
||||||
get() = activeModemCount >= 2
|
get() = activeModemCount >= 2
|
||||||
|
|
||||||
fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
|
fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
|
||||||
runBlocking {
|
val knownChannels = runBlocking {
|
||||||
euiccManager.enumerateEuiccChannels()
|
euiccManager.enumerateEuiccChannels()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs)
|
// Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs)
|
||||||
euiccManager.knownChannels.forEach {
|
knownChannels.forEach {
|
||||||
if (!it.removable) {
|
if (!it.removable) {
|
||||||
it.lpa.disableActiveProfileWithUndo()
|
it.lpa.disableActiveProfileWithUndo()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue