| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- package im.angry.openeuicc.util
- import android.util.Log
- import im.angry.openeuicc.core.EuiccChannel
- import im.angry.openeuicc.core.EuiccChannelManager
- import net.typeblog.lpac_jni.LocalProfileAssistant
- import net.typeblog.lpac_jni.LocalProfileInfo
- const val TAG = "LPAUtils"
- val LocalProfileInfo.displayName: String
- get() = nickName.ifEmpty { name }
- val LocalProfileInfo.isEnabled: Boolean
- get() = state == LocalProfileInfo.State.Enabled
- val List<LocalProfileInfo>.operational: List<LocalProfileInfo>
- get() = filter { it.profileClass == LocalProfileInfo.Clazz.Operational }
- val List<LocalProfileInfo>.enabled: LocalProfileInfo?
- get() = find { it.isEnabled }
- val List<EuiccChannel>.hasMultipleChips: Boolean
- get() = distinctBy { it.slotId }.size > 1
- fun LocalProfileAssistant.switchProfile(
- iccid: String,
- enable: Boolean = false,
- refresh: Boolean = false
- ): Boolean =
- if (enable) {
- enableProfile(iccid, refresh)
- } else {
- disableProfile(iccid, refresh)
- }
- /**
- * Disable the current active profile if any. If refresh is true, also cause a refresh command.
- * See EuiccManager.waitForReconnect()
- */
- fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean =
- profiles.enabled?.let {
- Log.i(TAG, "Disabling active profile ${it.iccid}")
- disableProfile(it.iccid, refresh)
- } ?: true
- /**
- * Disable the current active profile if any. If refresh is true, also cause a refresh command.
- * See EuiccManager.waitForReconnect()
- *
- * Return the iccid of the profile being disabled, or null if no active profile found or failed to
- * disable.
- */
- fun LocalProfileAssistant.disableActiveProfileKeepIccId(refresh: Boolean): String? =
- profiles.enabled?.let {
- Log.i(TAG, "Disabling active profile ${it.iccid}")
- if (disableProfile(it.iccid, refresh)) {
- it.iccid
- } else {
- null
- }
- }
- /**
- * 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.
- *
- * This requires the EuiccChannelManager object and a slotId / portId instead of
- * just an LPA object, because a LPA might become invalid during an operation
- * that generates notifications. As such, we will end up having to reconnect
- * when this happens.
- *
- * Note that however, if reconnect is required and will not be instant, waiting
- * should be the concern of op() itself, and this function assumes that when
- * op() returns, the slotId and portId will correspond to a valid channel again.
- */
- suspend inline fun EuiccChannelManager.beginTrackedOperation(
- slotId: Int,
- portId: Int,
- op: () -> Boolean
- ) {
- val latestSeq = withEuiccChannel(slotId, portId) { channel ->
- 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;
- // this is why we need to use two distinct calls to withEuiccChannel()
- withEuiccChannel(slotId, portId) { 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")
- }
|