From 65c9a7dc39bc88e23c9a6b8b4c7bd6091f30f5ac Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 23 Oct 2024 04:17:48 +0200 Subject: [PATCH 001/186] fix: refer url (#53) Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/53 Co-authored-by: septs Co-committed-by: septs --- .../main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt index 13848b63..5167da55 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt @@ -35,7 +35,7 @@ internal fun keyIdToKeystore(keyIds: Array): KeyStore { return ret } -// ref: +// ref: internal val KNOWN_CI_CERTS = hashMapOf( // GSM Association - RSP2 Root CI1 (CA: DigiCert) // Specs: SGP.21 and SGP.22 version 2 and version 3 From 7cb872a664eabc25ee9ccd176154aa2fae2d0bad Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 15:23:49 -0400 Subject: [PATCH 002/186] Add new withEuiccChannel() method to EuiccChannelManager --- .../angry/openeuicc/core/DefaultEuiccChannelManager.kt | 10 ++++++++++ .../im/angry/openeuicc/core/EuiccChannelManager.kt | 10 ++++++++++ 2 files changed, 20 insertions(+) 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 55fb53d5..af69c120 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 @@ -159,6 +159,16 @@ open class DefaultEuiccChannelManager( findEuiccChannelByPort(physicalSlotId, portId) } + override suspend fun withEuiccChannel( + physicalSlotId: Int, + portId: Int, + fn: (EuiccChannel) -> R + ): R { + val channel = findEuiccChannelByPortBlocking(physicalSlotId, portId) + ?: throw EuiccChannelManager.EuiccChannelNotFoundException() + return fn(channel) + } + override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) { if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) return diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index b21ccf61..304269dd 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -64,6 +64,16 @@ interface EuiccChannelManager { suspend fun findEuiccChannelByPort(physicalSlotId: Int, portId: Int): EuiccChannel? fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? + class EuiccChannelNotFoundException: Exception("EuiccChannel not found") + + /** + * Find a EuiccChannel by its slot and port, then run a callback with a reference to it. + * The reference is not supposed to be held outside of the callback. + * + * If a channel for that slot / port is not found, EuiccChannelNotFoundException is thrown + */ + suspend fun withEuiccChannel(physicalSlotId: Int, portId: Int, fn: (EuiccChannel) -> R): R + /** * Invalidate all EuiccChannels previously cached by this Manager */ From d54fcf2589c4e94dc041b4d5d857a56a8e7dbd80 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 15:31:52 -0400 Subject: [PATCH 003/186] refactor: Make EuiccChannel abstract This allows wrapping to control reference lifetime outside of EuiccChannelManager. --- .../core/DefaultEuiccChannelFactory.kt | 4 +-- .../im/angry/openeuicc/core/EuiccChannel.kt | 26 +++++++------------ .../angry/openeuicc/core/EuiccChannelImpl.kt | 26 +++++++++++++++++++ .../core/PrivilegedEuiccChannelFactory.kt | 2 +- 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt 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 7f3abb0b..2365b071 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 @@ -33,7 +33,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}") try { - return EuiccChannel( + return EuiccChannelImpl( port, OmapiApduInterface( seService!!, @@ -61,7 +61,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha if (bulkIn == null || bulkOut == null) return null val conn = usbManager.openDevice(usbDevice) ?: return null if (!conn.claimInterface(usbInterface, true)) return null - return EuiccChannel( + return EuiccChannelImpl( FakeUiccPortInfoCompat(FakeUiccCardInfoCompat(EuiccChannelManager.USB_CHANNEL_ID)), UsbApduInterface( conn, 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 e1065350..11ac7b04 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,26 +1,18 @@ package im.angry.openeuicc.core import im.angry.openeuicc.util.* -import kotlinx.coroutines.flow.Flow -import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant -import net.typeblog.lpac_jni.impl.HttpInterfaceImpl -import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl -class EuiccChannel( - val port: UiccPortInfoCompat, - apduInterface: ApduInterface, - verboseLoggingFlow: Flow -) { - val slotId = port.card.physicalSlotIndex // PHYSICAL slot - val logicalSlotId = port.logicalSlotIndex - val portId = port.portIndex +interface EuiccChannel { + val port: UiccPortInfoCompat - val lpa: LocalProfileAssistant = - LocalProfileAssistantImpl(apduInterface, HttpInterfaceImpl(verboseLoggingFlow)) + val slotId: Int // PHYSICAL slot + val logicalSlotId: Int + val portId: Int + + val lpa: LocalProfileAssistant val valid: Boolean - get() = lpa.valid - fun close() = lpa.close() -} + 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 new file mode 100644 index 00000000..0c0b00ee --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt @@ -0,0 +1,26 @@ +package im.angry.openeuicc.core + +import im.angry.openeuicc.util.* +import kotlinx.coroutines.flow.Flow +import net.typeblog.lpac_jni.ApduInterface +import net.typeblog.lpac_jni.LocalProfileAssistant +import net.typeblog.lpac_jni.impl.HttpInterfaceImpl +import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl + +class EuiccChannelImpl( + override val port: UiccPortInfoCompat, + apduInterface: ApduInterface, + verboseLoggingFlow: Flow +) : EuiccChannel { + override val slotId = port.card.physicalSlotIndex + override val logicalSlotId = port.logicalSlotIndex + override val portId = port.portIndex + + override val lpa: LocalProfileAssistant = + LocalProfileAssistantImpl(apduInterface, HttpInterfaceImpl(verboseLoggingFlow)) + + override val valid: Boolean + get() = lpa.valid + + override fun close() = lpa.close() +} diff --git a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt index ce57fb80..14e00200 100644 --- a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt +++ b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt @@ -26,7 +26,7 @@ class PrivilegedEuiccChannelFactory(context: Context) : DefaultEuiccChannelFacto "Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}" ) try { - return EuiccChannel( + return EuiccChannelImpl( port, TelephonyManagerApduInterface( port, From 76e8fbd56b1561965d616e7998cd3f2172811c33 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 15:46:43 -0400 Subject: [PATCH 004/186] Use wrappers to enforce that withEuiccChannel can't leak references --- .../core/DefaultEuiccChannelManager.kt | 7 +- .../openeuicc/core/EuiccChannelManager.kt | 3 +- .../openeuicc/core/EuiccChannelWrapper.kt | 41 ++++++++++++ .../core/LocalProfileAssistantWrapper.kt | 64 +++++++++++++++++++ 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt 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 af69c120..45855257 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 @@ -166,7 +166,12 @@ open class DefaultEuiccChannelManager( ): R { val channel = findEuiccChannelByPortBlocking(physicalSlotId, portId) ?: throw EuiccChannelManager.EuiccChannelNotFoundException() - return fn(channel) + val wrapper = EuiccChannelWrapper(channel) + try { + return fn(wrapper) + } finally { + wrapper.invalidateWrapper() + } } override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) { diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 304269dd..171c2157 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -68,7 +68,8 @@ interface EuiccChannelManager { /** * Find a EuiccChannel by its slot and port, then run a callback with a reference to it. - * The reference is not supposed to be held outside of the callback. + * The reference is not supposed to be held outside of the callback. This is enforced via + * a wrapper object. * * If a channel for that slot / port is not found, EuiccChannelNotFoundException is thrown */ 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 new file mode 100644 index 00000000..826ba41d --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt @@ -0,0 +1,41 @@ +package im.angry.openeuicc.core + +import im.angry.openeuicc.util.* +import net.typeblog.lpac_jni.LocalProfileAssistant + +class EuiccChannelWrapper(private val _inner: EuiccChannel) : EuiccChannel { + private var wrapperInvalidated = false + + private val channel: EuiccChannel + get() { + if (wrapperInvalidated) { + throw IllegalStateException("This wrapper has been invalidated") + } + + return _inner + } + override val port: UiccPortInfoCompat + get() = channel.port + override val slotId: Int + get() = channel.slotId + override val logicalSlotId: Int + get() = channel.logicalSlotId + override val portId: Int + get() = channel.portId + private val lpaDelegate = lazy { + LocalProfileAssistantWrapper(_inner.lpa) + } + override val lpa: LocalProfileAssistant by lpaDelegate + override val valid: Boolean + get() = channel.valid + + override fun close() = channel.close() + + fun invalidateWrapper() { + wrapperInvalidated = true + + if (lpaDelegate.isInitialized()) { + (lpa as LocalProfileAssistantWrapper).invalidateWrapper() + } + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt new file mode 100644 index 00000000..660dbc2b --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt @@ -0,0 +1,64 @@ +package im.angry.openeuicc.core + +import net.typeblog.lpac_jni.EuiccInfo2 +import net.typeblog.lpac_jni.LocalProfileAssistant +import net.typeblog.lpac_jni.LocalProfileInfo +import net.typeblog.lpac_jni.LocalProfileNotification +import net.typeblog.lpac_jni.ProfileDownloadCallback + +class LocalProfileAssistantWrapper(private val _inner: LocalProfileAssistant) : + LocalProfileAssistant { + + private var wrapperInvalidated = false + + private val lpa: LocalProfileAssistant + get() { + if (wrapperInvalidated) { + throw IllegalStateException("This wrapper has been invalidated") + } + + return _inner + } + + override val valid: Boolean + get() = lpa.valid + override val profiles: List + get() = lpa.profiles + override val notifications: List + get() = lpa.notifications + override val eID: String + get() = lpa.eID + override val euiccInfo2: EuiccInfo2? + get() = lpa.euiccInfo2 + + override fun setEs10xMss(mss: Byte) = lpa.setEs10xMss(mss) + + override fun enableProfile(iccid: String, refresh: Boolean): Boolean = + lpa.enableProfile(iccid, refresh) + + override fun disableProfile(iccid: String, refresh: Boolean): Boolean = + lpa.disableProfile(iccid, refresh) + + override fun deleteProfile(iccid: String): Boolean = lpa.deleteProfile(iccid) + + override fun downloadProfile( + smdp: String, + matchingId: String?, + imei: String?, + confirmationCode: String?, + callback: ProfileDownloadCallback + ): Boolean = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback) + + override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber) + + override fun handleNotification(seqNumber: Long): Boolean = lpa.handleNotification(seqNumber) + + override fun setNickname(iccid: String, nickname: String): Boolean = + lpa.setNickname(iccid, nickname) + + override fun close() = lpa.close() + + fun invalidateWrapper() { + wrapperInvalidated = true + } +} \ No newline at end of file From ef622740573d3d2fea0cac410a3e0f9991b096e5 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 15:49:38 -0400 Subject: [PATCH 005/186] Wrappers shouldn't hold references indefinitely --- .../im/angry/openeuicc/core/EuiccChannelWrapper.kt | 12 ++++++------ .../openeuicc/core/LocalProfileAssistantWrapper.kt | 11 +++++------ 2 files changed, 11 insertions(+), 12 deletions(-) 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 826ba41d..04888135 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 @@ -3,16 +3,16 @@ package im.angry.openeuicc.core import im.angry.openeuicc.util.* import net.typeblog.lpac_jni.LocalProfileAssistant -class EuiccChannelWrapper(private val _inner: EuiccChannel) : EuiccChannel { - private var wrapperInvalidated = false +class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { + private var _inner: EuiccChannel? = orig private val channel: EuiccChannel get() { - if (wrapperInvalidated) { + if (_inner == null) { throw IllegalStateException("This wrapper has been invalidated") } - return _inner + return _inner!! } override val port: UiccPortInfoCompat get() = channel.port @@ -23,7 +23,7 @@ class EuiccChannelWrapper(private val _inner: EuiccChannel) : EuiccChannel { override val portId: Int get() = channel.portId private val lpaDelegate = lazy { - LocalProfileAssistantWrapper(_inner.lpa) + LocalProfileAssistantWrapper(channel.lpa) } override val lpa: LocalProfileAssistant by lpaDelegate override val valid: Boolean @@ -32,7 +32,7 @@ class EuiccChannelWrapper(private val _inner: EuiccChannel) : EuiccChannel { override fun close() = channel.close() fun invalidateWrapper() { - wrapperInvalidated = true + _inner = null if (lpaDelegate.isInitialized()) { (lpa as LocalProfileAssistantWrapper).invalidateWrapper() diff --git a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt index 660dbc2b..e6a648af 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt @@ -6,18 +6,17 @@ import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.ProfileDownloadCallback -class LocalProfileAssistantWrapper(private val _inner: LocalProfileAssistant) : +class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : LocalProfileAssistant { - - private var wrapperInvalidated = false + private var _inner: LocalProfileAssistant? = orig private val lpa: LocalProfileAssistant get() { - if (wrapperInvalidated) { + if (_inner == null) { throw IllegalStateException("This wrapper has been invalidated") } - return _inner + return _inner!! } override val valid: Boolean @@ -59,6 +58,6 @@ class LocalProfileAssistantWrapper(private val _inner: LocalProfileAssistant) : override fun close() = lpa.close() fun invalidateWrapper() { - wrapperInvalidated = true + _inner = null } } \ No newline at end of file From 95b24e61514002065d8333fb2a8fda22568c7bbc Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 21:29:09 -0400 Subject: [PATCH 006/186] Add withEuiccChannel helper for EuiccChannelFragment --- .../im/angry/openeuicc/core/DefaultEuiccChannelManager.kt | 6 ++++-- .../java/im/angry/openeuicc/core/EuiccChannelManager.kt | 8 +++++++- .../im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt | 5 +++++ 3 files changed, 16 insertions(+), 3 deletions(-) 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 45855257..4bf634f3 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 @@ -162,13 +162,15 @@ open class DefaultEuiccChannelManager( override suspend fun withEuiccChannel( physicalSlotId: Int, portId: Int, - fn: (EuiccChannel) -> R + fn: suspend (EuiccChannel) -> R ): R { val channel = findEuiccChannelByPortBlocking(physicalSlotId, portId) ?: throw EuiccChannelManager.EuiccChannelNotFoundException() val wrapper = EuiccChannelWrapper(channel) try { - return fn(wrapper) + return withContext(Dispatchers.IO) { + fn(wrapper) + } } finally { wrapper.invalidateWrapper() } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 171c2157..0626c80c 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -71,9 +71,15 @@ interface EuiccChannelManager { * The reference is not supposed to be held outside of the callback. This is enforced via * a wrapper object. * + * The callback is run on Dispatchers.IO by default. + * * If a channel for that slot / port is not found, EuiccChannelNotFoundException is thrown */ - suspend fun withEuiccChannel(physicalSlotId: Int, portId: Int, fn: (EuiccChannel) -> R): R + suspend fun withEuiccChannel( + physicalSlotId: Int, + portId: Int, + fn: suspend (EuiccChannel) -> R + ): R /** * Invalidate all EuiccChannels previously cached by this Manager diff --git a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt index e92be408..3a74fbb0 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt @@ -40,6 +40,11 @@ val T.channel: EuiccChannel where T: Fragment, T: EuiccChannelFragmentMarker get() = euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!! +suspend fun T.withEuiccChannel(fn: suspend (EuiccChannel) -> R): R where T : Fragment, T : EuiccChannelFragmentMarker { + ensureEuiccChannelManager() + return euiccChannelManager.withEuiccChannel(slotId, portId, fn) +} + suspend fun T.ensureEuiccChannelManager() where T: Fragment, T: EuiccChannelFragmentMarker = (requireActivity() as BaseEuiccAccessActivity).euiccChannelManagerLoaded.await() From 3b868e4f9abeb8e4746f1be83e2a66bfdfd46da0 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 21:41:56 -0400 Subject: [PATCH 007/186] Move some fragments to withEuiccChannel() --- .../openeuicc/ui/EuiccManagementFragment.kt | 13 +++++--- .../openeuicc/ui/ProfileDownloadFragment.kt | 32 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index da291230..7d47ae1d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -6,7 +6,6 @@ import android.content.ClipboardManager import android.content.Intent import android.os.Bundle import android.text.method.PasswordTransformationMethod -import android.util.Log import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -52,6 +51,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var fab: FloatingActionButton private lateinit var profileList: RecyclerView + private var logicalSlotId: Int = -1 private val adapter = EuiccProfileAdapter() @@ -127,9 +127,11 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.show_notifications -> { - Intent(requireContext(), NotificationsActivity::class.java).apply { - putExtra("logicalSlotId", channel.logicalSlotId) - startActivity(this) + if (logicalSlotId != -1) { + Intent(requireContext(), NotificationsActivity::class.java).apply { + putExtra("logicalSlotId", logicalSlotId) + startActivity(this) + } } true } @@ -162,7 +164,8 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, preferenceRepository.disableSafeguardFlow.stateIn(lifecycleScope) } - val profiles = withContext(Dispatchers.IO) { + val profiles = withEuiccChannel { channel -> + logicalSlotId = channel.logicalSlotId euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId) channel.lpa.profiles.operational } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index 843c9e5f..9aa55068 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -159,22 +159,26 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), return@launch } - val imei = try { - telephonyManager.getImei(channel.logicalSlotId) ?: "" - } catch (e: Exception) { - "" - } + withEuiccChannel { channel -> + val imei = try { + telephonyManager.getImei(channel.logicalSlotId) ?: "" + } catch (e: Exception) { + "" + } - // Fetch remaining NVRAM - val str = channel.lpa.euiccInfo2?.freeNvram?.also { - freeNvram = it - }?.let { formatFreeSpace(it) } + // Fetch remaining NVRAM + val str = channel.lpa.euiccInfo2?.freeNvram?.also { + freeNvram = it + }?.let { formatFreeSpace(it) } - withContext(Dispatchers.Main) { - profileDownloadFreeSpace.text = getString(R.string.profile_download_free_space, - str ?: getText(R.string.unknown)) - profileDownloadIMEI.editText!!.text = - Editable.Factory.getInstance().newEditable(imei) + withContext(Dispatchers.Main) { + profileDownloadFreeSpace.text = getString( + R.string.profile_download_free_space, + str ?: getText(R.string.unknown) + ) + profileDownloadIMEI.editText!!.text = + Editable.Factory.getInstance().newEditable(imei) + } } } } From 0961ef70f400825cf061bec0957dc71378ee3dba Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 21:45:14 -0400 Subject: [PATCH 008/186] New withEuiccChannel() variant with logical slot ID --- .../openeuicc/core/DefaultEuiccChannelManager.kt | 16 ++++++++++++++++ .../angry/openeuicc/core/EuiccChannelManager.kt | 8 ++++++++ 2 files changed, 24 insertions(+) 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 4bf634f3..53418b72 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 @@ -176,6 +176,22 @@ open class DefaultEuiccChannelManager( } } + override suspend fun withEuiccChannel( + logicalSlotId: Int, + fn: suspend (EuiccChannel) -> R + ): R { + val channel = findEuiccChannelBySlotBlocking(logicalSlotId) + ?: throw EuiccChannelManager.EuiccChannelNotFoundException() + val wrapper = EuiccChannelWrapper(channel) + try { + return withContext(Dispatchers.IO) { + fn(wrapper) + } + } finally { + wrapper.invalidateWrapper() + } + } + override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) { if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) return diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 0626c80c..a4968c78 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -81,6 +81,14 @@ interface EuiccChannelManager { fn: suspend (EuiccChannel) -> R ): R + /** + * Same as withEuiccChannel(Int, Int, (EuiccChannel) -> R) but instead uses logical slot ID + */ + suspend fun withEuiccChannel( + logicalSlotId: Int, + fn: suspend (EuiccChannel) -> R + ): R + /** * Invalidate all EuiccChannels previously cached by this Manager */ From 8ac46bd77890f0243edb190910e0452fa56fc5fd Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 21:46:13 -0400 Subject: [PATCH 009/186] Move findEuiccChannelBySlot to non-blocking --- .../core/DefaultEuiccChannelManager.kt | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) 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 53418b72..91a859df 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 @@ -88,23 +88,26 @@ open class DefaultEuiccChannelManager( } } - override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? = - runBlocking { - withContext(Dispatchers.IO) { - if (logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) { - return@withContext usbChannel - } + private suspend fun findEuiccChannelBySlot(logicalSlotId: Int): EuiccChannel? = + 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) { - return@withContext tryOpenEuiccChannel(port) - } + for (card in uiccCards) { + for (port in card.ports) { + if (port.logicalSlotIndex == logicalSlotId) { + return@withContext tryOpenEuiccChannel(port) } } - - null } + + null + } + + override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? = + runBlocking { + findEuiccChannelBySlot(logicalSlotId) } override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? = @@ -164,7 +167,7 @@ open class DefaultEuiccChannelManager( portId: Int, fn: suspend (EuiccChannel) -> R ): R { - val channel = findEuiccChannelByPortBlocking(physicalSlotId, portId) + val channel = findEuiccChannelByPort(physicalSlotId, portId) ?: throw EuiccChannelManager.EuiccChannelNotFoundException() val wrapper = EuiccChannelWrapper(channel) try { @@ -180,7 +183,7 @@ open class DefaultEuiccChannelManager( logicalSlotId: Int, fn: suspend (EuiccChannel) -> R ): R { - val channel = findEuiccChannelBySlotBlocking(logicalSlotId) + val channel = findEuiccChannelBySlot(logicalSlotId) ?: throw EuiccChannelManager.EuiccChannelNotFoundException() val wrapper = EuiccChannelWrapper(channel) try { From 6a2d4d66dd8547053ae6d9d1624cf460cdaa850c Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 21:52:33 -0400 Subject: [PATCH 010/186] Move EuiccChannelManagerService to withEuiccChannel() --- .../service/EuiccChannelManagerService.kt | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt index 8db3bbe5..fce3d305 100644 --- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt +++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt @@ -287,19 +287,20 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { R.drawable.ic_task_sim_card_download ) { euiccChannelManager.beginTrackedOperation(slotId, portId) { - val channel = euiccChannelManager.findEuiccChannelByPort(slotId, portId) - val res = channel!!.lpa.downloadProfile( - smdp, - matchingId, - imei, - confirmationCode, - object : ProfileDownloadCallback { - override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) { - if (state.progress == 0) return - foregroundTaskState.value = - ForegroundTaskState.InProgress(state.progress) - } - }) + val res = euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> + channel.lpa.downloadProfile( + smdp, + matchingId, + imei, + confirmationCode, + object : ProfileDownloadCallback { + override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) { + if (state.progress == 0) return + foregroundTaskState.value = + ForegroundTaskState.InProgress(state.progress) + } + }) + } if (!res) { // TODO: Provide more details on the error @@ -321,10 +322,12 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { getString(R.string.task_profile_rename_failure), R.drawable.ic_task_rename ) { - val res = euiccChannelManager.findEuiccChannelByPort(slotId, portId)!!.lpa.setNickname( - iccid, - name - ) + val res = euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> + channel.lpa.setNickname( + iccid, + name + ) + } if (!res) { throw RuntimeException("Profile not renamed") @@ -342,10 +345,9 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { R.drawable.ic_task_delete ) { euiccChannelManager.beginTrackedOperation(slotId, portId) { - euiccChannelManager.findEuiccChannelByPort( - slotId, - portId - )!!.lpa.deleteProfile(iccid) + euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> + channel.lpa.deleteProfile(iccid) + } preferenceRepository.notificationDeleteFlow.first() } @@ -366,8 +368,10 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { R.drawable.ic_task_switch ) { euiccChannelManager.beginTrackedOperation(slotId, portId) { - val channel = euiccChannelManager.findEuiccChannelByPort(slotId, portId)!! - val (res, refreshed) = + val (res, refreshed) = euiccChannelManager.withEuiccChannel( + slotId, + portId + ) { channel -> if (!channel.lpa.switchProfile(iccid, enable, refresh = true)) { // Sometimes, we *can* enable or disable the profile, but we cannot // send the refresh command to the modem because the profile somehow @@ -378,6 +382,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { } else { Pair(true, true) } + } if (!res) { throw RuntimeException("Could not switch profile") From 3d4704e77b6e882a9a74c113aa47d316990149fb Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 26 Oct 2024 22:15:02 -0400 Subject: [PATCH 011/186] Remove more EuiccChannel usage in PrivilegedEuiccManagementFragment --- .../openeuicc/ui/EuiccManagementFragment.kt | 40 ++++++++++--------- .../ui/PrivilegedEuiccManagementFragment.kt | 19 ++++++--- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index 7d47ae1d..0d61581e 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -150,32 +150,36 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, listOf() } - @SuppressLint("NotifyDataSetChanged") private fun refresh() { if (invalid) return swipeRefresh.isRefreshing = true lifecycleScope.launch { - ensureEuiccChannelManager() - euiccChannelManagerService.waitForForegroundTask() + doRefresh() + } + } - if (!this@EuiccManagementFragment::disableSafeguardFlow.isInitialized) { - disableSafeguardFlow = - preferenceRepository.disableSafeguardFlow.stateIn(lifecycleScope) - } + @SuppressLint("NotifyDataSetChanged") + protected open suspend fun doRefresh() { + ensureEuiccChannelManager() + euiccChannelManagerService.waitForForegroundTask() - val profiles = withEuiccChannel { channel -> - logicalSlotId = channel.logicalSlotId - euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId) - channel.lpa.profiles.operational - } + if (!this@EuiccManagementFragment::disableSafeguardFlow.isInitialized) { + disableSafeguardFlow = + preferenceRepository.disableSafeguardFlow.stateIn(lifecycleScope) + } - withContext(Dispatchers.Main) { - adapter.profiles = profiles - adapter.footerViews = onCreateFooterViews(profileList, profiles) - adapter.notifyDataSetChanged() - swipeRefresh.isRefreshing = false - } + val profiles = withEuiccChannel { channel -> + logicalSlotId = channel.logicalSlotId + euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId) + channel.lpa.profiles.operational + } + + withContext(Dispatchers.Main) { + adapter.profiles = profiles + adapter.footerViews = onCreateFooterViews(profileList, profiles) + adapter.notifyDataSetChanged() + swipeRefresh.isRefreshing = false } } diff --git a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt index 7d055f47..6a8fede9 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt @@ -6,8 +6,6 @@ import android.widget.Button import android.widget.PopupMenu import im.angry.openeuicc.R import im.angry.openeuicc.util.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import net.typeblog.lpac_jni.LocalProfileInfo class PrivilegedEuiccManagementFragment: EuiccManagementFragment() { @@ -16,14 +14,23 @@ class PrivilegedEuiccManagementFragment: EuiccManagementFragment() { newInstanceEuicc(PrivilegedEuiccManagementFragment::class.java, slotId, portId) } + private var isMEP = false + private var isRemovable = false + + override suspend fun doRefresh() { + super.doRefresh() + withEuiccChannel { channel -> + isMEP = channel.isMEP + isRemovable = channel.removable + } + } + override suspend fun onCreateFooterViews( parent: ViewGroup, profiles: List ): List = super.onCreateFooterViews(parent, profiles).let { footers -> - // isMEP can map to a slow operation (UiccCardInfo.isMultipleEnabledProfilesSupported()) - // so let's do it in the IO context - if (withContext(Dispatchers.IO) { channel.isMEP }) { + if (isMEP) { val view = layoutInflater.inflate(R.layout.footer_mep, parent, false) view.requireViewById