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 1c58c8e..23c21b9 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 @@ -1,8 +1,6 @@ package im.angry.openeuicc.core import android.content.Context -import android.os.Handler -import android.os.HandlerThread import android.se.omapi.SEService import android.telephony.SubscriptionManager import android.util.Log @@ -14,8 +12,6 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.lang.IllegalArgumentException -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine open class EuiccChannelManager(protected val context: Context) { companion object { @@ -32,23 +28,12 @@ open class EuiccChannelManager(protected val context: Context) { (context.applicationContext as OpenEuiccApplication).telephonyManager } - private val handler = Handler(HandlerThread("BaseEuiccChannelManager").also { it.start() }.looper) - protected open val uiccCards: Collection get() = (0.. - handler.post { - var service: SEService? = null - service = SEService(context, { handler.post(it) }) { - cont.resume(service!!) - } - } - } - private suspend fun ensureSEService() { if (seService == null) { - seService = connectSEService() + seService = connectSEService(context) } } 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 614efdf..6636bc1 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 @@ -1,9 +1,16 @@ package im.angry.openeuicc.util +import android.content.Context import android.os.Build import android.se.omapi.Reader import android.se.omapi.SEService import android.telephony.TelephonyManager +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine val TelephonyManager.activeModemCountCompat: Int get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -20,6 +27,34 @@ 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-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt index bccef18..b2a069b 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt @@ -3,14 +3,11 @@ package im.angry.openeuicc.util import android.content.Context import android.content.pm.PackageManager import android.os.Build -import android.se.omapi.SEService import android.telephony.TelephonyManager import im.angry.easyeuicc.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine fun getCompatibilityChecks(context: Context): List = listOf( @@ -95,22 +92,8 @@ internal class OmapiConnCheck(private val context: Context): CompatibilityCheck( override val defaultDescription: String get() = context.getString(R.string.compatibility_check_omapi_connectivity_desc) - private suspend fun getSEService(): SEService = suspendCoroutine { cont -> - var service: SEService? = null - var resumed = false - val resume = { - if (!resumed && service != null) { - cont.resume(service!!) - resumed = true - } - } - service = SEService(context, { it.run() }, { resume() }) - Thread.sleep(1000) - resume() - } - override suspend fun doCheck(): Boolean { - val seService = getSEService() + val seService = connectSEService(context) if (!seService.isConnected) { failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail) return false