Browse Source

fix: beginTrackedOperation() should work with channels that get invalidated

Peter Cai 1 year ago
parent
commit
c8d2269efb

+ 9 - 2
app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt

@@ -5,6 +5,8 @@ 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
 
 interface EuiccChannelFragmentMarker: OpenEuiccContextMarker
 
@@ -36,5 +38,10 @@ interface EuiccProfilesChangedListener {
     fun onEuiccProfilesChanged()
 }
 
-suspend fun <T> T.beginTrackedOperation(op: suspend () -> Boolean) where T: Fragment, T: EuiccChannelFragmentMarker =
-    channel.lpa.beginTrackedOperation(op)
+suspend fun <T> T.beginTrackedOperation(op: suspend () -> Boolean) where T: Fragment, T: EuiccChannelFragmentMarker {
+    withContext(Dispatchers.IO) {
+        euiccChannelManager.beginTrackedOperationBlocking(slotId, portId) {
+            op()
+        }
+    }
+}

+ 26 - 9
app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt

@@ -2,6 +2,7 @@ package im.angry.openeuicc.util
 
 import android.util.Log
 import im.angry.openeuicc.core.EuiccChannel
+import im.angry.openeuicc.core.EuiccChannelManager
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
@@ -51,23 +52,39 @@ fun LocalProfileAssistant.disableActiveProfileWithUndo(refreshOnDisable: Boolean
  * 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 fun LocalProfileAssistant.beginTrackedOperation(op: suspend () -> Boolean) =
-    withContext(Dispatchers.IO) {
-        beginTrackedOperationBlocking { op() }
-    }
-
-inline fun LocalProfileAssistant.beginTrackedOperationBlocking(op: () -> Boolean) {
-    val latestSeq = notifications.firstOrNull()?.seqNumber ?: 0
+inline fun EuiccChannelManager.beginTrackedOperationBlocking(
+    slotId: Int,
+    portId: Int,
+    op: () -> Boolean
+) {
+    val latestSeq =
+        findEuiccChannelByPortBlocking(slotId, portId)!!.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"
-            notifications.filter { it.seqNumber > latestSeq }.forEach {
+            findEuiccChannelByPortBlocking(
+                slotId,
+                portId
+            )?.lpa?.notifications?.filter { it.seqNumber > latestSeq }?.forEach {
                 Log.d(TAG, "Handling notification $it")
-                handleNotification(it.seqNumber)
+                findEuiccChannelByPortBlocking(
+                    slotId,
+                    portId
+                )?.lpa?.handleNotification(it.seqNumber)
             }
         } catch (e: Exception) {
             // Ignore any error during notification handling

+ 2 - 2
app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt

@@ -228,7 +228,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
                 }
             }
 
-            channels[0].lpa.beginTrackedOperationBlocking {
+            euiccChannelManager.beginTrackedOperationBlocking(channels[0].slotId, channels[0].portId) {
                 if (channels[0].lpa.deleteProfile(iccid)) {
                     return RESULT_OK
                 }
@@ -291,7 +291,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
                 return RESULT_FIRST_USER
             }
 
-            channel.lpa.beginTrackedOperationBlocking {
+            euiccChannelManager.beginTrackedOperationBlocking(channel.slotId, channel.portId) {
                 if (iccid != null) {
                     // Disable any active profile first if present
                     channel.lpa.disableActiveProfile(false)