From 64c1fa93d33861a88578d429f582342c150aa7aa Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 4 Feb 2024 16:10:25 -0500 Subject: [PATCH 1/2] DirectProfileDownloadActivity: Handle more potential cases e.g. we can skip the slot selection dialog if there is only one chip in the device. --- .../ui/DirectProfileDownloadActivity.kt | 19 ++++++++++++++++--- .../java/im/angry/openeuicc/util/Utils.kt | 6 +++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt index a7983a5..9c4011a 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt @@ -3,7 +3,7 @@ package im.angry.openeuicc.ui import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import im.angry.openeuicc.util.openEuiccApplication +import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -16,8 +16,21 @@ class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.Sl openEuiccApplication.euiccChannelManager.enumerateEuiccChannels() } - SlotSelectFragment.newInstance() - .show(supportFragmentManager, SlotSelectFragment.TAG) + val knownChannels = openEuiccApplication.euiccChannelManager.knownChannels + when { + knownChannels.isEmpty() -> { + finish() + } + knownChannels.hasMultipleChips -> { + SlotSelectFragment.newInstance() + .show(supportFragmentManager, SlotSelectFragment.TAG) + } + else -> { + // 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. + onSlotSelected(knownChannels[0].slotId, knownChannels[0].portId) + } + } } } diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt index 6e6f162..cc89df3 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt @@ -2,6 +2,7 @@ package im.angry.openeuicc.util import android.content.Context import android.content.pm.PackageManager +import im.angry.openeuicc.core.EuiccChannel import net.typeblog.lpac_jni.LocalProfileInfo import java.lang.RuntimeException @@ -15,4 +16,7 @@ val Context.selfAppVersion: String } val LocalProfileInfo.isEnabled: Boolean - get() = state == LocalProfileInfo.State.Enabled \ No newline at end of file + get() = state == LocalProfileInfo.State.Enabled + +val List.hasMultipleChips: Boolean + get() = distinctBy { it.slotId }.size > 1 \ No newline at end of file From 7c0be54fd77784053d470789aa79381f47073a11 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 4 Feb 2024 16:13:14 -0500 Subject: [PATCH 2/2] cleanup: Move connectSEService to Utils instead of TelephonyCompat --- .../angry/openeuicc/util/TelephonyCompat.kt | 28 -------------- .../java/im/angry/openeuicc/util/Utils.kt | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt b/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt index 6636bc1..5c7d217 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt @@ -27,34 +27,6 @@ fun SEService.getUiccReaderCompat(slotNumber: Int): Reader { } } -// Create an instance of OMAPI SEService in a manner that "makes sense" without unpredictable callbacks -suspend fun connectSEService(context: Context): SEService = suspendCoroutine { cont -> - // Use a Mutex to make sure the continuation is run *after* the "service" variable is assigned - val lock = Mutex() - var service: SEService? = null - val callback = { - runBlocking { - lock.withLock { - cont.resume(service!!) - } - } - } - - runBlocking { - // If this were not protected by a Mutex, callback might be run before service is even assigned - // Yes, we are on Android, we could have used something like a Handler, but we cannot really - // assume the coroutine is run on a thread that has a Handler. We either use our own HandlerThread - // (and then cleanup becomes an issue), or we use a lock - lock.withLock { - try { - service = SEService(context, { it.run() }, callback) - } catch (e: Exception) { - cont.resumeWithException(e) - } - } - } -} - /* * In the privileged version, the EuiccChannelManager should work * based on real Uicc{Card,Port}Info reported by TelephonyManager. diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt index cc89df3..5d4221e 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt @@ -2,9 +2,16 @@ package im.angry.openeuicc.util import android.content.Context import android.content.pm.PackageManager +import android.se.omapi.SEService import im.angry.openeuicc.core.EuiccChannel +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import net.typeblog.lpac_jni.LocalProfileInfo import java.lang.RuntimeException +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine val Context.selfAppVersion: String get() = @@ -19,4 +26,32 @@ val LocalProfileInfo.isEnabled: Boolean get() = state == LocalProfileInfo.State.Enabled val List.hasMultipleChips: Boolean - get() = distinctBy { it.slotId }.size > 1 \ No newline at end of file + get() = distinctBy { it.slotId }.size > 1 + +// Create an instance of OMAPI SEService in a manner that "makes sense" without unpredictable callbacks +suspend fun connectSEService(context: Context): SEService = suspendCoroutine { cont -> + // Use a Mutex to make sure the continuation is run *after* the "service" variable is assigned + val lock = Mutex() + var service: SEService? = null + val callback = { + runBlocking { + lock.withLock { + cont.resume(service!!) + } + } + } + + runBlocking { + // If this were not protected by a Mutex, callback might be run before service is even assigned + // Yes, we are on Android, we could have used something like a Handler, but we cannot really + // assume the coroutine is run on a thread that has a Handler. We either use our own HandlerThread + // (and then cleanup becomes an issue), or we use a lock + lock.withLock { + try { + service = SEService(context, { it.run() }, callback) + } catch (e: Exception) { + cont.resumeWithException(e) + } + } + } +} \ No newline at end of file