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
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<UiccCardInfoCompat>
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() {
if (seService == null) {
seService = connectSEService()
seService = connectSEService(context)
}
}

View file

@ -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.

View file

@ -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<CompatibilityCheck> =
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