Refactor OMAPI SEService creation

This commit is contained in:
Peter Cai 2024-01-28 17:23:32 -05:00
parent 1e2e3f044c
commit 5926a6601d
3 changed files with 37 additions and 34 deletions

View file

@ -1,8 +1,6 @@
package im.angry.openeuicc.core package im.angry.openeuicc.core
import android.content.Context import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.se.omapi.SEService import android.se.omapi.SEService
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.util.Log import android.util.Log
@ -14,8 +12,6 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
open class EuiccChannelManager(protected val context: Context) { open class EuiccChannelManager(protected val context: Context) {
companion object { companion object {
@ -32,23 +28,12 @@ open class EuiccChannelManager(protected val context: Context) {
(context.applicationContext as OpenEuiccApplication).telephonyManager (context.applicationContext as OpenEuiccApplication).telephonyManager
} }
private val handler = Handler(HandlerThread("BaseEuiccChannelManager").also { it.start() }.looper)
protected open val uiccCards: Collection<UiccCardInfoCompat> protected open val uiccCards: Collection<UiccCardInfoCompat>
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) } get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
private suspend fun connectSEService(): SEService = suspendCoroutine { cont ->
handler.post {
var service: SEService? = null
service = SEService(context, { handler.post(it) }) {
cont.resume(service!!)
}
}
}
private suspend fun ensureSEService() { private suspend fun ensureSEService() {
if (seService == null) { if (seService == null) {
seService = connectSEService() seService = connectSEService(context)
} }
} }

View file

@ -1,9 +1,16 @@
package im.angry.openeuicc.util package im.angry.openeuicc.util
import android.content.Context
import android.os.Build import android.os.Build
import android.se.omapi.Reader import android.se.omapi.Reader
import android.se.omapi.SEService import android.se.omapi.SEService
import android.telephony.TelephonyManager 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 val TelephonyManager.activeModemCountCompat: Int
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 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 * In the privileged version, the EuiccChannelManager should work
* based on real Uicc{Card,Port}Info reported by TelephonyManager. * based on real Uicc{Card,Port}Info reported by TelephonyManager.

View file

@ -3,14 +3,11 @@ package im.angry.openeuicc.util
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.se.omapi.SEService
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import im.angry.easyeuicc.R import im.angry.easyeuicc.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
fun getCompatibilityChecks(context: Context): List<CompatibilityCheck> = fun getCompatibilityChecks(context: Context): List<CompatibilityCheck> =
listOf( listOf(
@ -95,22 +92,8 @@ internal class OmapiConnCheck(private val context: Context): CompatibilityCheck(
override val defaultDescription: String override val defaultDescription: String
get() = context.getString(R.string.compatibility_check_omapi_connectivity_desc) 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 { override suspend fun doCheck(): Boolean {
val seService = getSEService() val seService = connectSEService(context)
if (!seService.isConnected) { if (!seService.isConnected) {
failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail) failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail)
return false return false