Peter Cai
c0d1c29b7f
All checks were successful
/ build-debug (push) Successful in 3m45s
...however, don't do this in privileged mode because OpenEuiccService is supposed to be background, and we don't want to just randomly show up when things go wrong.
90 lines
3.2 KiB
Kotlin
90 lines
3.2 KiB
Kotlin
package im.angry.openeuicc.util
|
|
|
|
import android.content.Context
|
|
import android.content.pm.PackageManager
|
|
import android.se.omapi.SEService
|
|
import android.telephony.TelephonyManager
|
|
import androidx.fragment.app.Fragment
|
|
import im.angry.openeuicc.OpenEuiccApplication
|
|
import im.angry.openeuicc.core.EuiccChannel
|
|
import im.angry.openeuicc.core.EuiccChannelManager
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.runBlocking
|
|
import kotlinx.coroutines.sync.Mutex
|
|
import kotlinx.coroutines.sync.withLock
|
|
import kotlinx.coroutines.withContext
|
|
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() =
|
|
try {
|
|
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
|
pInfo.versionName
|
|
} catch (e: PackageManager.NameNotFoundException) {
|
|
throw RuntimeException(e)
|
|
}
|
|
|
|
suspend fun readSelfLog(lines: Int = 2048): String = withContext(Dispatchers.IO) {
|
|
try {
|
|
Runtime.getRuntime().exec("logcat -t $lines").inputStream.readBytes()
|
|
.decodeToString()
|
|
} catch (_: Exception) {
|
|
""
|
|
}
|
|
}
|
|
|
|
interface OpenEuiccContextMarker {
|
|
val openEuiccMarkerContext: Context
|
|
get() = when (this) {
|
|
is Context -> this
|
|
is Fragment -> requireContext()
|
|
else -> throw RuntimeException("OpenEuiccUIContextMarker shall only be used on Fragments or UI types that derive from Context")
|
|
}
|
|
|
|
val openEuiccApplication: OpenEuiccApplication
|
|
get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
|
|
|
|
val euiccChannelManager: EuiccChannelManager
|
|
get() = openEuiccApplication.euiccChannelManager
|
|
|
|
val telephonyManager: TelephonyManager
|
|
get() = openEuiccApplication.telephonyManager
|
|
}
|
|
|
|
val LocalProfileInfo.isEnabled: Boolean
|
|
get() = state == LocalProfileInfo.State.Enabled
|
|
|
|
val List<EuiccChannel>.hasMultipleChips: Boolean
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
} |