diff --git a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt index d324c75d6f9..59c853d3bc2 100644 --- a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt +++ b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt @@ -21,7 +21,7 @@ import android.os.INetworkManagementService import android.os.ServiceManager import android.util.Log import androidx.annotation.OpenForTesting -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settingslib.spaprivileged.framework.common.userManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -36,13 +36,13 @@ open class BillingCycleRepository @JvmOverloads constructor( INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ), - private val telephonyRepository: TelephonyRepository = TelephonyRepository(context), + private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context), ) { private val userManager = context.userManager fun isModifiableFlow(subId: Int): Flow = - telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled -> - isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser + mobileDataRepository.isMobileDataEnabledFlow(subId).map { mobileDataEnabled -> + mobileDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser }.conflate().flowOn(Dispatchers.Default) open fun isBandwidthControlEnabled(): Boolean = try { diff --git a/src/com/android/settings/network/MobileDataEnabledFlow.kt b/src/com/android/settings/network/MobileDataEnabledFlow.kt deleted file mode 100644 index 1f995a94b0a..00000000000 --- a/src/com/android/settings/network/MobileDataEnabledFlow.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 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.network - -import android.content.Context -import android.provider.Settings -import android.telephony.SubscriptionManager -import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.merge - -/** - * Flow for mobile data enabled changed event. - * - * Note: This flow can only notify enabled status changes, cannot provide the latest status. - */ -fun Context.mobileDataEnabledFlow(subId: Int, sendInitialValue: Boolean = true): Flow { - val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA, sendInitialValue) - return when (subId) { - SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow - else -> { - val subIdFlow = settingsGlobalChangeFlow( - name = Settings.Global.MOBILE_DATA + subId, - sendInitialValue = false, - ) - merge(flow, subIdFlow) - } - } -} diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt index f6c6065e40d..ea0b5acc1f2 100644 --- a/src/com/android/settings/network/SimOnboardingService.kt +++ b/src/com/android/settings/network/SimOnboardingService.kt @@ -27,9 +27,8 @@ import android.telephony.UiccCardInfo import android.telephony.UiccSlotInfo import android.util.Log import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.sim.SimActivationNotifier -import com.android.settings.spa.network.setAutomaticData import com.android.settings.spa.network.setDefaultData import com.android.settings.spa.network.setDefaultSms import com.android.settings.spa.network.setDefaultVoice @@ -366,7 +365,7 @@ class SimOnboardingService { wifiPickerTrackerHelper, targetPrimarySimMobileData ) - TelephonyRepository(context).setAutomaticData( + MobileDataRepository(context).setAutoDataSwitch( targetNonDds, targetPrimarySimAutoDataSwitch.value ) diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt index e14d5f82b4b..445597f22d6 100644 --- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt +++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt @@ -22,7 +22,6 @@ import android.telephony.TelephonyManager import android.telephony.data.ApnSetting import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceScreen -import com.android.settings.network.mobileDataEnabledFlow import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.flow.combine @@ -71,7 +70,7 @@ class MmsMessagePreferenceController @JvmOverloads constructor( override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { combine( - mContext.mobileDataEnabledFlow(mSubId), + MobileDataRepository(mContext).mobileDataEnabledChangedFlow(mSubId), mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { preferenceScreen?.let { super.displayPreference(it) } diff --git a/src/com/android/settings/network/telephony/MobileDataRepository.kt b/src/com/android/settings/network/telephony/MobileDataRepository.kt new file mode 100644 index 00000000000..46f814b60da --- /dev/null +++ b/src/com/android/settings/network/telephony/MobileDataRepository.kt @@ -0,0 +1,124 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.provider.Settings +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.telephony.TelephonyManager.MobileDataPolicy +import android.util.Log +import com.android.settings.wifi.WifiPickerTrackerHelper +import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach + +class MobileDataRepository( + private val context: Context, + private val subscriptionsChangedFlow: Flow = context.subscriptionsChangedFlow(), +) { + fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + val telephonyManager = context.telephonyManager(subId) + return subscriptionsChangedFlow + .map { telephonyManager.isMobileDataPolicyEnabled(policy) } + .distinctUntilChanged() + .conflate() + .onEach { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") } + .flowOn(Dispatchers.Default) + } + + fun setMobileDataPolicyEnabled(subId: Int, @MobileDataPolicy policy: Int, enabled: Boolean) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return + Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled") + context.telephonyManager(subId).setMobileDataPolicyEnabled(policy, enabled) + } + + fun setAutoDataSwitch(subId: Int, newEnabled: Boolean) { + setMobileDataPolicyEnabled( + subId = subId, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + enabled = newEnabled, + ) + } + + /** + * Flow for mobile data enabled changed event. + * + * Note: This flow can only notify enabled status changes, cannot provide the latest status. + */ + fun mobileDataEnabledChangedFlow(subId: Int, sendInitialValue: Boolean = true): Flow = + mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue) + + private fun mobileSettingsGlobalChangedFlow( + name: String, + subId: Int, + sendInitialValue: Boolean = true, + ): Flow { + val flow = context.settingsGlobalChangeFlow(name, sendInitialValue) + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flow + val subIdFlow = + context.settingsGlobalChangeFlow(name = name + subId, sendInitialValue = false) + return merge(flow, subIdFlow) + } + + fun isMobileDataEnabledFlow(subId: Int): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + val telephonyManager = context.telephonyManager(subId) + return mobileDataEnabledChangedFlow(subId) + .map { + telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) + } + .catch { e -> + Log.w(TAG, "[$subId] isMobileDataEnabledFlow: exception", e) + emit(false) + } + .distinctUntilChanged() + .conflate() + .onEach { Log.d(TAG, "[$subId] isMobileDataEnabledFlow: $it") } + .flowOn(Dispatchers.Default) + } + + fun setMobileDataEnabled( + subId: Int, + enabled: Boolean, + wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null, + ) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return + + Log.d(TAG, "setMobileDataEnabled: $enabled") + MobileNetworkUtils.setMobileDataEnabled( + context, subId, enabled, /* disableOtherSubscriptions= */ true) + + if (wifiPickerTrackerHelper != null && + !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)) { + wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled) + } + } + + private companion object { + private const val TAG = "MobileDataRepository" + } +} diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt index 7c334ee44db..3317c71b333 100644 --- a/src/com/android/settings/network/telephony/TelephonyRepository.kt +++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt @@ -17,98 +17,16 @@ package com.android.settings.network.telephony import android.content.Context -import android.telephony.SubscriptionManager 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 import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach - -class TelephonyRepository( - private val context: Context, - private val subscriptionsChangedFlow: Flow = context.subscriptionsChangedFlow(), -) { - fun isMobileDataPolicyEnabledFlow( - subId: Int, - @TelephonyManager.MobileDataPolicy policy: Int, - ): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - - val telephonyManager = context.telephonyManager(subId) - - return subscriptionsChangedFlow.map { - telephonyManager.isMobileDataPolicyEnabled(policy) - .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") } - }.conflate().flowOn(Dispatchers.Default) - } - - fun setMobileDataPolicyEnabled( - subId: Int, - @TelephonyManager.MobileDataPolicy policy: Int, - enabled: Boolean, - ) { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return - - val telephonyManager = context.telephonyManager(subId) - Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled") - telephonyManager.setMobileDataPolicyEnabled(policy, enabled) - } - - fun isDataEnabledFlow(subId: Int): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - - return context.mobileDataEnabledFlow(subId) - .map { - val telephonyManager = context.telephonyManager(subId) - telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) - } - .catch { - Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it) - emit(false) - } - .onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") } - .conflate() - .flowOn(Dispatchers.Default) - } - - 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" - } -} /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */ fun Context.telephonyCallbackFlow( diff --git a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt index 5dcac1ebaac..fb0bd82a7d4 100644 --- a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt +++ b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt @@ -24,7 +24,7 @@ import android.telephony.TelephonyManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.android.settings.R -import com.android.settings.network.mobileDataEnabledFlow +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl import com.android.settings.network.telephony.requireSubscriptionManager import com.android.settings.network.telephony.safeGetConfig @@ -54,6 +54,7 @@ class CrossSimCallingViewModel( private val scope = viewModelScope + Dispatchers.Default private val metricsFeatureProvider = featureFactory.metricsFeatureProvider private val updateChannel = Channel() + private val mobileDataRepository = MobileDataRepository(application) init { val resources = application.resources @@ -81,7 +82,7 @@ class CrossSimCallingViewModel( } private fun List.anyMobileDataEnableChangedFlow() = map { subId -> - application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false) + mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false) }.merge() private suspend fun updateCrossSimCalling(activeSubIds: List, newEnabled: Boolean) { diff --git a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt index e79be4a9cc6..e7cc18fb101 100644 --- a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt +++ b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt @@ -16,13 +16,11 @@ 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 androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R -import com.android.settings.network.telephony.TelephonyRepository import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel @@ -51,11 +49,3 @@ fun AutomaticDataSwitchingPreference( } ) } - -fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) { - setMobileDataPolicyEnabled( - subId = subId, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - enabled = newEnabled, - ) -} diff --git a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt index 0d40bca4612..4b95d448b5f 100644 --- a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt +++ b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt @@ -16,12 +16,10 @@ 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 diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt index 4b9fcf45764..05a8f6a27de 100644 --- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt +++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt @@ -48,13 +48,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.telephony.DataSubscriptionRepository -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo import com.android.settings.wifi.WifiPickerTrackerHelper import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.rememberContext import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel @@ -202,21 +203,18 @@ fun MobileDataSectionImpl( ) { val context = LocalContext.current val localLifecycleOwner = LocalLifecycleOwner.current - val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner) - - val subscriptionManager: SubscriptionManager? = - context.getSystemService(SubscriptionManager::class.java) + val mobileDataRepository = rememberContext(::MobileDataRepository) Category(title = stringResource(id = R.string.mobile_data_settings_title)) { val isAutoDataEnabled by remember(nonDds.intValue) { - TelephonyRepository(context).isMobileDataPolicyEnabledFlow( + mobileDataRepository.isMobileDataPolicyEnabledFlow( subId = nonDds.intValue, policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH ) }.collectAsStateWithLifecycle(initialValue = null) val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) { - TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue) + mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue) }.collectAsStateWithLifecycle(initialValue = false) val coroutineScope = rememberCoroutineScope() @@ -226,8 +224,8 @@ fun MobileDataSectionImpl( coroutineScope.launch { setMobileData( context, - subscriptionManager, - wifiPickerTrackerHelper, + context.getSystemService(SubscriptionManager::class.java), + getWifiPickerTrackerHelper(context, localLifecycleOwner), mobileDataSelectedId.intValue, newEnabled ) @@ -238,7 +236,7 @@ fun MobileDataSectionImpl( AutomaticDataSwitchingPreference( isAutoDataEnabled = { isAutoDataEnabled }, setAutoDataEnabled = { newEnabled -> - TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled) + mobileDataRepository.setAutoDataSwitch(nonDds.intValue, newEnabled) }, ) } @@ -426,6 +424,6 @@ suspend fun setMobileData( Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]") subscriptionManager?.setDefaultDataSubId(targetSubId) } - TelephonyRepository(context) - .setMobileData(targetSubId, enabled, wifiPickerTrackerHelper) + MobileDataRepository(context) + .setMobileDataEnabled(targetSubId, enabled, wifiPickerTrackerHelper) } \ No newline at end of file diff --git a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt deleted file mode 100644 index c4611ac6544..00000000000 --- a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 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.network - -import android.content.Context -import android.provider.Settings -import android.telephony.SubscriptionManager -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull -import com.android.settingslib.spa.testutils.toListWithTimeout -import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class MobileDataEnabledFlowTest { - private val context: Context = ApplicationProvider.getApplicationContext() - - @Test - fun mobileDataEnabledFlow_notified(): Unit = runBlocking { - val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) - - assertThat(flow.firstWithTimeoutOrNull()).isNotNull() - } - - @Test - fun mobileDataEnabledFlow_changed_notified(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - - val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) - mobileDataEnabled = true - - assertThat(flow.firstWithTimeoutOrNull()).isNotNull() - } - - @Test - fun mobileDataEnabledFlow_forSubIdNotChanged(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - var mobileDataEnabledForSubId - by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) - mobileDataEnabledForSubId = false - - val listDeferred = async { - context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout() - } - - assertThat(listDeferred.await()).hasSize(1) - } - - @Test - fun mobileDataEnabledFlow_forSubIdChanged(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - var mobileDataEnabledForSubId - by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) - mobileDataEnabledForSubId = false - - val listDeferred = async { - context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout() - } - delay(100) - mobileDataEnabledForSubId = true - - assertThat(listDeferred.await().size).isAtLeast(2) - } - - private companion object { - const val SUB_ID = 123 - } -} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt new file mode 100644 index 00000000000..268be570048 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt @@ -0,0 +1,168 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.provider.Settings +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class MobileDataRepositoryTest { + private val mockTelephonyManager = + mock { on { createForSubscriptionId(SUB_ID) } doReturn mock } + + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager + } + + private val repository = MobileDataRepository(context, flowOf(Unit)) + + @Test + fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking { + val flow = + repository.isMobileDataPolicyEnabledFlow( + subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + ) + + assertThat(flow.firstWithTimeoutOrNull()).isFalse() + } + + @Test + fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking { + mockTelephonyManager.stub { + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true + } + + val flow = + repository.isMobileDataPolicyEnabledFlow( + subId = SUB_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + ) + + assertThat(flow.firstWithTimeoutOrNull()).isTrue() + } + + @Test + fun setMobileDataPolicyEnabled() = runBlocking { + repository.setMobileDataPolicyEnabled( + subId = SUB_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + enabled = true) + + verify(mockTelephonyManager) + .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true) + } + + @Test + fun mobileDataEnabledChangedFlow_notified(): Unit = runBlocking { + val flow = + repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + + assertThat(flow.firstWithTimeoutOrNull()).isNotNull() + } + + @Test + fun mobileDataEnabledChangedFlow_changed_notified(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + + val flow = + repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + mobileDataEnabled = true + + assertThat(flow.firstWithTimeoutOrNull()).isNotNull() + } + + @Test + fun mobileDataEnabledChangedFlow_forSubIdNotChanged(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + var mobileDataEnabledForSubId by + context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) + mobileDataEnabledForSubId = false + + val listDeferred = async { + repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout() + } + + assertThat(listDeferred.await()).hasSize(1) + } + + @Test + fun mobileDataEnabledChangedFlow_forSubIdChanged(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + var mobileDataEnabledForSubId by + context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) + mobileDataEnabledForSubId = false + + val listDeferred = async { + repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout() + } + delay(100) + mobileDataEnabledForSubId = true + + assertThat(listDeferred.await().size).isAtLeast(2) + } + + @Test + fun isMobileDataEnabledFlow_invalidSub_returnFalse() = runBlocking { + val state = + repository.isMobileDataEnabledFlow( + subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, + ) + + assertThat(state.firstWithTimeoutOrNull()).isFalse() + } + + @Test + fun isMobileDataEnabledFlow_validSub_returnPolicyState() = runBlocking { + mockTelephonyManager.stub { + on { isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) } doReturn true + } + + val state = repository.isMobileDataEnabledFlow(subId = SUB_ID) + + assertThat(state.firstWithTimeoutOrNull()).isTrue() + } + + private companion object { + const val SUB_ID = 123 + } +} 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 65e8c47023d..12791b8e9c1 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 @@ -17,14 +17,12 @@ package com.android.settings.network.telephony import android.content.Context -import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith @@ -33,91 +31,29 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy -import org.mockito.kotlin.stub import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class TelephonyRepositoryTest { private var telephonyCallback: TelephonyCallback? = null - private val mockTelephonyManager = mock { - on { createForSubscriptionId(SUB_ID) } doReturn mock - on { registerTelephonyCallback(any(), any()) } doAnswer { - telephonyCallback = it.arguments[1] as TelephonyCallback - } - } - - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { - on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager - } - - private val repository = TelephonyRepository(context, flowOf(Unit)) - - @Test - fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking { - val flow = repository.isMobileDataPolicyEnabledFlow( - subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - ) - - assertThat(flow.firstWithTimeoutOrNull()).isFalse() - } - - @Test - fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking { - mockTelephonyManager.stub { - on { - isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) - } doReturn true + private val mockTelephonyManager = + mock { + on { createForSubscriptionId(SUB_ID) } doReturn mock + on { registerTelephonyCallback(any(), any()) } doAnswer + { + telephonyCallback = it.arguments[1] as TelephonyCallback + } } - val flow = repository.isMobileDataPolicyEnabledFlow( - subId = SUB_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - ) - - assertThat(flow.firstWithTimeoutOrNull()).isTrue() - } - - @Test - fun setMobileDataPolicyEnabled() = runBlocking { - repository.setMobileDataPolicyEnabled( - subId = SUB_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - enabled = true - ) - - verify(mockTelephonyManager) - .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true) - } - - @Test - fun isDataEnabled_invalidSub_returnFalse() = runBlocking { - val state = repository.isDataEnabledFlow( - 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 + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager } - val state = repository.isDataEnabledFlow(subId = SUB_ID) - - assertThat(state.firstWithTimeoutOrNull()).isTrue() - } - @Test fun telephonyCallbackFlow_callbackRegistered() = runBlocking { - val flow = context.telephonyCallbackFlow(SUB_ID) { - object : TelephonyCallback() {} - } + val flow = context.telephonyCallbackFlow(SUB_ID) { object : TelephonyCallback() {} } flow.firstWithTimeoutOrNull() @@ -126,9 +62,7 @@ class TelephonyRepositoryTest { @Test fun telephonyCallbackFlow_callbackUnregistered() = runBlocking { - val flow = context.telephonyCallbackFlow(SUB_ID) { - object : TelephonyCallback() {} - } + val flow = context.telephonyCallbackFlow(SUB_ID) { object : TelephonyCallback() {} } flow.firstWithTimeoutOrNull()