Add the mobile data into new SIMs page

Bug: 329584989
Test: verify UI.
atest TelephonyRepositoryTest

Change-Id: Ia00ac287ffd0d15ba0c9350b731c3afc1a04b7a0
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
index f97822a..80aa13c 100644
--- a/src/com/android/settings/network/SimOnboardingActivity.kt
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -45,15 +45,18 @@
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.LifecycleRegistry
 import com.android.settings.R
 import com.android.settings.SidecarFragment
 import com.android.settings.network.telephony.SubscriptionActionDialogActivity
 import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity
 import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
 import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
+import com.android.settings.wifi.WifiPickerTrackerHelper
 import com.android.settingslib.spa.SpaBaseDialogActivity
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
@@ -73,6 +76,8 @@
 
 class SimOnboardingActivity : SpaBaseDialogActivity() {
     lateinit var scope: CoroutineScope
+    lateinit var wifiPickerTrackerHelper: WifiPickerTrackerHelper
+    lateinit var context: Context
     lateinit var showStartingDialog: MutableState<Boolean>
     lateinit var showError: MutableState<ErrorType>
     lateinit var showProgressDialog: MutableState<Boolean>
@@ -85,6 +90,7 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
         if (!this.userManager.isAdminUser) {
             Log.e(TAG, "It is not the admin user. Unable to toggle subscription.")
             finish()
@@ -151,7 +157,10 @@
 
             CallbackType.CALLBACK_SETUP_PRIMARY_SIM -> {
                 scope.launch {
-                    onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
+                    onboardingService.startSetupPrimarySim(
+                        this@SimOnboardingActivity,
+                        wifiPickerTrackerHelper
+                    )
                 }
             }
 
@@ -183,6 +192,12 @@
         showDsdsProgressDialog = remember { mutableStateOf(false) }
         showRestartDialog = remember { mutableStateOf(false) }
         scope = rememberCoroutineScope()
+        context = LocalContext.current
+        val lifecycleOwner = LocalLifecycleOwner.current
+        wifiPickerTrackerHelper = WifiPickerTrackerHelper(
+            LifecycleRegistry(lifecycleOwner), context,
+            null /* WifiPickerTrackerCallback */
+        )
 
         registerSidecarReceiverFlow()
 
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index b99f18d..b7df754 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -30,6 +30,7 @@
 import com.android.settings.spa.network.setDefaultData
 import com.android.settings.spa.network.setDefaultSms
 import com.android.settings.spa.network.setDefaultVoice
+import com.android.settings.wifi.WifiPickerTrackerHelper
 import com.android.settingslib.utils.ThreadUtils
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -336,14 +337,17 @@
         }
     }
 
-    suspend fun startSetupPrimarySim(context: Context) {
+    suspend fun startSetupPrimarySim(
+        context: Context,
+        wifiPickerTrackerHelper: WifiPickerTrackerHelper
+    ) {
         withContext(Dispatchers.Default) {
                 setDefaultVoice(subscriptionManager, targetPrimarySimCalls)
                 setDefaultSms(subscriptionManager, targetPrimarySimTexts)
                 setDefaultData(
                     context,
                     subscriptionManager,
-                    null,
+                    wifiPickerTrackerHelper,
                     targetPrimarySimMobileData
                 )
                 TelephonyRepository(context).setAutomaticData(
diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt
index 18af621..cc9b53d 100644
--- a/src/com/android/settings/network/telephony/TelephonyRepository.kt
+++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt
@@ -21,6 +21,8 @@
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyManager
 import android.util.Log
+import com.android.settings.network.mobileDataEnabledFlow
+import com.android.settings.wifi.WifiPickerTrackerHelper
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.ProducerScope
@@ -62,6 +64,42 @@
         telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
     }
 
+    fun isDataEnabled(
+        subId: Int,
+    ): Flow<Boolean> {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+
+        Log.d(TAG, "register mobileDataEnabledFlow: [$subId]")
+        return context.mobileDataEnabledFlow(subId)
+            .map {
+                Log.d(TAG, "mobileDataEnabledFlow: receive mobile data [$subId] start")
+                val telephonyManager = context.telephonyManager(subId)
+                telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
+                    .also { Log.d(TAG, "mobileDataEnabledFlow: [$subId] isDataEnabled(): $it") }
+            }
+    }
+
+    fun setMobileData(
+        subId: Int,
+        enabled: Boolean,
+        wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null
+    ) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return
+
+        Log.d(TAG, "setMobileData: $enabled")
+        MobileNetworkUtils.setMobileDataEnabled(
+            context,
+            subId,
+            enabled /* enabled */,
+            true /* disableOtherSubscriptions */
+        )
+
+        if (wifiPickerTrackerHelper != null
+            && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
+        ) {
+            wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
+        }
+    }
     private companion object {
         private const val TAG = "TelephonyRepository"
     }
diff --git a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
new file mode 100644
index 0000000..8c382bd
--- /dev/null
+++ b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.network
+
+import android.telephony.TelephonyManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@Composable
+fun MobileDataSwitchingPreference(
+    isMobileDataEnabled: () -> Boolean?,
+    setMobileDataEnabled: (newEnabled: Boolean) -> Unit,
+) {
+    val mobileDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
+    val coroutineScope = rememberCoroutineScope()
+    SwitchPreference(
+        object : SwitchPreferenceModel {
+            override val title = stringResource(id = R.string.mobile_data_settings_title)
+            override val summary = { mobileDataSummary }
+            override val checked = { isMobileDataEnabled() }
+            override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
+                coroutineScope.launch(Dispatchers.Default) {
+                    setMobileDataEnabled(newEnabled)
+                }
+            }
+            override val changeable:() -> Boolean = {true}
+        }
+    )
+}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 80be970..0ccedeb 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -38,11 +38,12 @@
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settings.R
 import com.android.settings.network.SubscriptionInfoListViewModel
-import com.android.settings.network.telephony.MobileNetworkUtils
 import com.android.settings.network.telephony.TelephonyRepository
 import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
 import com.android.settings.wifi.WifiPickerTrackerHelper
@@ -167,7 +168,7 @@
     defaultVoiceSubId: MutableIntState,
     defaultSmsSubId: MutableIntState,
     defaultDataSubId: MutableIntState,
-    nonDds: MutableIntState
+    nonDds: MutableIntState,
 ) {
     val selectableSubscriptionInfoList by selectableSubscriptionInfoListFlow
         .collectAsStateWithLifecycle(initialValue = emptyList())
@@ -175,22 +176,76 @@
     val stringSims = stringResource(R.string.provider_network_settings_title)
     RegularScaffold(title = stringSims) {
         SimsSection(selectableSubscriptionInfoList)
+        MobileDataSectionImpl(defaultDataSubId,
+            nonDds,
+        )
+
         PrimarySimSectionImpl(
             selectableSubscriptionInfoListFlow,
             defaultVoiceSubId,
             defaultSmsSubId,
             defaultDataSubId,
-            nonDds
         )
     }
 }
 
 @Composable
+fun MobileDataSectionImpl(
+    mobileDataSelectedId: MutableIntState,
+    nonDds: MutableIntState,
+) {
+    val context = LocalContext.current
+    val localLifecycleOwner = LocalLifecycleOwner.current
+    val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
+
+    val subscriptionManager: SubscriptionManager? =
+            context.getSystemService(SubscriptionManager::class.java)
+
+    Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
+        val isAutoDataEnabled by remember(nonDds.intValue) {
+            TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
+                subId = nonDds.intValue,
+                policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
+            )
+        }.collectAsStateWithLifecycle(initialValue = null)
+
+        val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
+            TelephonyRepository(context).isDataEnabled(mobileDataSelectedId.intValue)
+        }.collectAsStateWithLifecycle(initialValue = false)
+        val coroutineScope = rememberCoroutineScope()
+
+        MobileDataSwitchingPreference(
+            isMobileDataEnabled = { mobileDataStateChanged },
+            setMobileDataEnabled = { newEnabled ->
+                coroutineScope.launch {
+                    setMobileData(
+                        context,
+                        subscriptionManager,
+                        wifiPickerTrackerHelper,
+                        mobileDataSelectedId.intValue,
+                        newEnabled
+                    )
+                }
+           },
+        )
+        if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            AutomaticDataSwitchingPreference(
+                isAutoDataEnabled = { isAutoDataEnabled },
+                setAutoDataEnabled = { newEnabled ->
+                    TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
+                },
+            )
+        }
+    }
+}
+
+@Composable
 fun PrimarySimImpl(
     primarySimInfo: PrimarySimInfo,
     callsSelectedId: MutableIntState,
     textsSelectedId: MutableIntState,
     mobileDataSelectedId: MutableIntState,
+    wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null,
     subscriptionManager: SubscriptionManager? =
         LocalContext.current.getSystemService(SubscriptionManager::class.java),
     coroutineScope: CoroutineScope = rememberCoroutineScope(),
@@ -208,20 +263,15 @@
         }
     },
     actionSetMobileData: (Int) -> Unit = {
-        mobileDataSelectedId.intValue = it
         coroutineScope.launch {
-            // TODO: to fix the WifiPickerTracker crash when create
-            //       the wifiPickerTrackerHelper
             setDefaultData(
                 context,
                 subscriptionManager,
-                null/*wifiPickerTrackerHelper*/,
+                wifiPickerTrackerHelper,
                 it
             )
         }
     },
-    isAutoDataEnabled: () -> Boolean?,
-    setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
 ) {
     CreatePrimarySimListPreference(
         stringResource(id = R.string.primary_sim_calls_title),
@@ -244,8 +294,6 @@
         Icons.Outlined.DataUsage,
         actionSetMobileData
     )
-
-    AutomaticDataSwitchingPreference(isAutoDataEnabled, setAutoDataEnabled)
 }
 
 @Composable
@@ -254,9 +302,11 @@
     callsSelectedId: MutableIntState,
     textsSelectedId: MutableIntState,
     mobileDataSelectedId: MutableIntState,
-    nonDds: MutableIntState,
 ) {
     val context = LocalContext.current
+    val localLifecycleOwner = LocalLifecycleOwner.current
+    val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
+
     val primarySimInfo = remember(subscriptionInfoListFlow) {
         subscriptionInfoListFlow
             .map { subscriptionInfoList ->
@@ -267,25 +317,25 @@
     }.collectAsStateWithLifecycle(initialValue = null).value ?: return
 
     Category(title = stringResource(id = R.string.primary_sim_title)) {
-        val isAutoDataEnabled by remember(nonDds.intValue) {
-            TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
-                subId = nonDds.intValue,
-                policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
-            )
-        }.collectAsStateWithLifecycle(initialValue = null)
         PrimarySimImpl(
             primarySimInfo,
             callsSelectedId,
             textsSelectedId,
             mobileDataSelectedId,
-            isAutoDataEnabled = { isAutoDataEnabled },
-            setAutoDataEnabled = { newEnabled ->
-                TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
-            },
+            wifiPickerTrackerHelper
         )
     }
 }
 
+private fun getWifiPickerTrackerHelper(
+    context: Context,
+    lifecycleOwner: LifecycleOwner
+): WifiPickerTrackerHelper {
+    return WifiPickerTrackerHelper(
+        LifecycleRegistry(lifecycleOwner), context,
+        null /* WifiPickerTrackerCallback */
+    )
+}
 private fun Context.defaultVoiceSubscriptionFlow(): Flow<Int> =
         merge(
                 flowOf(null), // kick an initial value
@@ -335,18 +385,27 @@
     wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
     subId: Int
 ): Unit =
+    setMobileData(
+        context,
+        subscriptionManager,
+        wifiPickerTrackerHelper,
+        subId,
+        true
+    )
+
+suspend fun setMobileData(
+    context: Context,
+    subscriptionManager: SubscriptionManager?,
+    wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
+    subId: Int,
+    enabled: Boolean,
+): Unit =
     withContext(Dispatchers.Default) {
-        subscriptionManager?.setDefaultDataSubId(subId)
-        Log.d(NetworkCellularGroupProvider.name, "setMobileDataEnabled: true")
-        MobileNetworkUtils.setMobileDataEnabled(
-            context,
-            subId,
-            true /* enabled */,
-            true /* disableOtherSubscriptions */
-        )
-        if (wifiPickerTrackerHelper != null
-            && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
-        ) {
-            wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
+        Log.d(NetworkCellularGroupProvider.name, "setMobileData: $enabled")
+        if (enabled) {
+            Log.d(NetworkCellularGroupProvider.name, "setDefaultData: [$subId]")
+            subscriptionManager?.setDefaultDataSubId(subId)
         }
-    }
+        TelephonyRepository(context)
+            .setMobileData(subId, enabled, wifiPickerTrackerHelper)
+    }
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index 0306aad..1c96979 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -102,18 +102,21 @@
             mobileDataSelectedId = mobileDataSelectedId,
             actionSetCalls = {
                 callsSelectedId.intValue = it
-                onboardingService.targetPrimarySimCalls = it},
+                onboardingService.targetPrimarySimCalls = it
+            },
             actionSetTexts = {
                 textsSelectedId.intValue = it
-                onboardingService.targetPrimarySimTexts = it},
+                onboardingService.targetPrimarySimTexts = it
+            },
             actionSetMobileData = {
                 mobileDataSelectedId.intValue = it
-                onboardingService.targetPrimarySimMobileData = it},
-            isAutoDataEnabled = { isAutoDataEnabled },
+                onboardingService.targetPrimarySimMobileData = it
+            }
+        )
+        AutomaticDataSwitchingPreference(isAutoDataEnabled = { isAutoDataEnabled },
             setAutoDataEnabled = { newEnabled ->
                 onboardingService.targetPrimarySimAutoDataSwitch.value = newEnabled
-            },
-        )
+            })
     }
 }
 
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
index ce27ed4..6058935 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
@@ -92,6 +92,30 @@
     }
 
     @Test
+    fun isDataEnabled_invalidSub_returnFalse() = runBlocking {
+        val state = repository.isDataEnabled(
+            subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+        )
+
+        assertThat(state.firstWithTimeoutOrNull()).isFalse()
+    }
+
+    @Test
+    fun isDataEnabled_validSub_returnPolicyState() = runBlocking {
+        mockTelephonyManager.stub {
+            on {
+                isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
+            } doReturn true
+        }
+
+        val state = repository.isDataEnabled(
+            subId = SUB_ID,
+        )
+
+        assertThat(state.firstWithTimeoutOrNull()).isTrue()
+    }
+
+    @Test
     fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
         val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
             object : TelephonyCallback() {}