Compare commits

..

No commits in common. "790a5cf778ed88f8920f1c23330ad238bccf2863" and "df9cece94b6aa91c085b906a7361134bba745c27" have entirely different histories.

4 changed files with 47 additions and 70 deletions

View file

@ -1,10 +1,15 @@
package im.angry.openeuicc.util
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.ui.BaseEuiccAccessActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
private const val TAG = "EuiccChannelFragmentUtils"
interface EuiccChannelFragmentMarker: OpenEuiccContextMarker
@ -32,9 +37,32 @@ val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccChannelFragmentMarker
get() =
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!!
/*
* Begin a "tracked" operation where notifications may be generated by the eSIM
* Automatically handle any newly generated notification during the operation
* if the function "op" returns true.
*/
suspend fun <T> T.beginTrackedOperation(op: suspend () -> Boolean) where T : Fragment, T : EuiccChannelFragmentMarker =
withContext(Dispatchers.IO) {
val latestSeq = channel.lpa.notifications.firstOrNull()?.seqNumber ?: 0
Log.d(TAG, "Latest notification is $latestSeq before operation")
if (op()) {
Log.d(TAG, "Operation has requested notification handling")
try {
// Note that the exact instance of "channel" might have changed here if reconnected;
// so we MUST use the automatic getter for "channel"
channel.lpa.notifications.filter { it.seqNumber > latestSeq }.forEach {
Log.d(TAG, "Handling notification $it")
channel.lpa.handleNotification(it.seqNumber)
}
} catch (e: Exception) {
// Ignore any error during notification handling
e.printStackTrace()
}
}
Log.d(TAG, "Operation complete")
}
interface EuiccProfilesChangedListener {
fun onEuiccProfilesChanged()
}
suspend fun <T> T.beginTrackedOperation(op: suspend () -> Boolean) where T: Fragment, T: EuiccChannelFragmentMarker =
channel.lpa.beginTrackedOperation(op)
}

View file

@ -2,14 +2,9 @@ package im.angry.openeuicc.util
import android.util.Log
import im.angry.openeuicc.core.EuiccChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.LocalProfileInfo
const val TAG = "LPAUtils"
val LocalProfileInfo.displayName: String
get() = nickName.ifEmpty { name }
@ -31,7 +26,7 @@ val List<EuiccChannel>.hasMultipleChips: Boolean
*/
fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean =
profiles.find { it.isEnabled }?.let {
Log.i(TAG, "Disabling active profile ${it.iccid}")
Log.i("LPAUtils", "Disabling active profile ${it.iccid}")
disableProfile(it.iccid, refresh)
} ?: true
@ -45,34 +40,4 @@ fun LocalProfileAssistant.disableActiveProfileWithUndo(refreshOnDisable: Boolean
profiles.find { it.isEnabled }?.let {
disableProfile(it.iccid, refreshOnDisable)
return { enableProfile(it.iccid) }
} ?: { }
/**
* Begin a "tracked" operation where notifications may be generated by the eSIM
* Automatically handle any newly generated notification during the operation
* if the function "op" returns true.
*/
suspend fun LocalProfileAssistant.beginTrackedOperation(op: suspend () -> Boolean) =
withContext(Dispatchers.IO) {
beginTrackedOperationBlocking { op() }
}
inline fun LocalProfileAssistant.beginTrackedOperationBlocking(op: () -> Boolean) {
val latestSeq = notifications.firstOrNull()?.seqNumber ?: 0
Log.d(TAG, "Latest notification is $latestSeq before operation")
if (op()) {
Log.d(TAG, "Operation has requested notification handling")
try {
// Note that the exact instance of "channel" might have changed here if reconnected;
// so we MUST use the automatic getter for "channel"
notifications.filter { it.seqNumber > latestSeq }.forEach {
Log.d(TAG, "Handling notification $it")
handleNotification(it.seqNumber)
}
} catch (e: Exception) {
// Ignore any error during notification handling
e.printStackTrace()
}
}
Log.d(TAG, "Operation complete")
}
} ?: { }

View file

@ -12,8 +12,6 @@ import net.typeblog.lpac_jni.LocalProfileInfo
import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.runBlocking
import java.lang.IllegalStateException
@ -228,17 +226,11 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
}
}
channels[0].lpa.beginTrackedOperationBlocking {
if (channels[0].lpa.deleteProfile(iccid)) {
return RESULT_OK
}
runBlocking {
preferenceRepository.notificationDeleteFlow.first()
}
return if (channels[0].lpa.deleteProfile(iccid)) {
RESULT_OK
} else {
RESULT_FIRST_USER
}
return RESULT_FIRST_USER
} catch (e: Exception) {
return RESULT_FIRST_USER
}
@ -291,22 +283,14 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
return RESULT_FIRST_USER
}
channel.lpa.beginTrackedOperationBlocking {
if (iccid != null) {
// Disable any active profile first if present
channel.lpa.disableActiveProfile(false)
if (!channel.lpa.enableProfile(iccid)) {
return RESULT_FIRST_USER
}
} else {
if (!channel.lpa.disableActiveProfile(true)) {
return RESULT_FIRST_USER
}
}
// Disable any active profile first if present
if (!channel.lpa.disableActiveProfile(false)) {
return RESULT_FIRST_USER
}
runBlocking {
// TODO: The enable / disable operations should really be one
preferenceRepository.notificationEnableFlow.first()
if (iccid != null) {
if (!channel.lpa.enableProfile(iccid)) {
return RESULT_FIRST_USER
}
}

View file

@ -10,8 +10,8 @@ interface LocalProfileAssistant {
// All blocking functions in this class assume that they are executed on non-Main threads
// The IO context in Kotlin's coroutine library is recommended.
fun enableProfile(iccid: String, refresh: Boolean = true): Boolean
fun disableProfile(iccid: String, refresh: Boolean = true): Boolean
fun enableProfile(iccid: String, refresh: Boolean = false): Boolean
fun disableProfile(iccid: String, refresh: Boolean = false): Boolean
fun deleteProfile(iccid: String): Boolean
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,