Browse Source

Move profile download to the new foreground flow

...and fix race condition introduced by the new flow
Peter Cai 1 year ago
parent
commit
cf5704be42

+ 30 - 6
app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt

@@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.takeWhile
 import kotlinx.coroutines.flow.transformWhile
 import kotlinx.coroutines.launch
@@ -174,12 +175,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
             updateForegroundNotification(title, iconRes)
         }
 
-        // We 've launched the coroutine, now we can self-start
-        // This is required in order to use startForeground()
-        // This will end up calling onStartCommand(), which will emit
-        // into foregroundStarted and unblock the coroutine above
-        startForegroundService(Intent(this, this::class.java))
-
         // We should be the only task running, so we can subscribe to foregroundTaskState
         // until we encounter ForegroundTaskState.Done.
         // Then, we complete the returned flow, but we also set the state back to Idle.
@@ -192,6 +187,15 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
             }
             emit(it)
             it !is ForegroundTaskState.Done
+        }.onStart {
+            // When this Flow is started, we unblock the coroutine launched above by
+            // self-starting as a foreground service.
+            startForegroundService(
+                Intent(
+                    this@EuiccChannelManagerService,
+                    this@EuiccChannelManagerService::class.java
+                )
+            )
         }.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
     }
 
@@ -238,4 +242,24 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
                 preferenceRepository.notificationDownloadFlow.first()
             }
         }
+
+    fun launchProfileRenameTask(
+        slotId: Int,
+        portId: Int,
+        iccid: String,
+        name: String
+    ): Flow<ForegroundTaskState>? =
+        launchForegroundTask(
+            getString(R.string.task_profile_rename),
+            R.drawable.ic_task_rename
+        ) {
+            val res = euiccChannelManager.findEuiccChannelByPort(slotId, portId)!!.lpa.setNickname(
+                iccid,
+                name
+            )
+
+            if (!res) {
+                throw RuntimeException("Profile not renamed")
+            }
+        }
 }

+ 2 - 0
app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt

@@ -213,6 +213,8 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(),
         progress.visibility = View.VISIBLE
 
         lifecycleScope.launch {
+            ensureEuiccChannelManager()
+            euiccChannelManagerService.waitForForegroundTask()
             try {
                 doDownloadProfile(server, code, confirmationCode, imei)
             } catch (e: Exception) {

+ 13 - 20
app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt

@@ -2,7 +2,6 @@ package im.angry.openeuicc.ui
 
 import android.app.Dialog
 import android.os.Bundle
-import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
@@ -13,11 +12,8 @@ import androidx.lifecycle.lifecycleScope
 import com.google.android.material.textfield.TextInputLayout
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.util.*
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import java.lang.Exception
-import java.lang.RuntimeException
 
 class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker {
     companion object {
@@ -97,23 +93,20 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
         progress.visibility = View.VISIBLE
 
         lifecycleScope.launch {
-            try {
-                doRename(name)
-            } catch (e: Exception) {
-                Log.d(TAG, "Failed to rename profile")
-                Log.d(TAG, Log.getStackTraceString(e))
-            } finally {
-                if (parentFragment is EuiccProfilesChangedListener) {
-                    (parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
-                }
-                dismiss()
+            ensureEuiccChannelManager()
+            euiccChannelManagerService.waitForForegroundTask()
+            euiccChannelManagerService.launchProfileRenameTask(
+                slotId,
+                portId,
+                requireArguments().getString("iccid")!!,
+                name
+            )?.collect()
+
+            if (parentFragment is EuiccProfilesChangedListener) {
+                (parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
             }
-        }
-    }
 
-    private suspend fun doRename(name: String) = withContext(Dispatchers.IO) {
-        if (!channel.lpa.setNickname(requireArguments().getString("iccid")!!, name)) {
-            throw RuntimeException("Profile nickname not changed")
+            dismiss()
         }
     }
 }

+ 5 - 0
app-common/src/main/res/drawable/ic_task_rename.xml

@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+    
+</vector>

+ 1 - 0
app-common/src/main/res/values/strings.xml

@@ -35,6 +35,7 @@
 
     <string name="task_notification">Long-running Tasks</string>
     <string name="task_profile_download">Downloading eSIM profile</string>
+    <string name="task_profile_rename">Renaming eSIM profile</string>
 
     <string name="profile_download">New eSIM</string>
     <string name="profile_download_server">Server (RSP / SM-DP+)</string>