Move mobile data logic into MobileDataRepository

Bug: 347224962
Flag: EXEMPT refactor
Test: manual on Mobile Settings
Test: unit
Change-Id: I2b54f9e6c4addafd31d7d96f2a44870cd54fe185
This commit is contained in:
Chaohui Wang
2024-06-20 11:38:16 +08:00
parent 2345b63c68
commit ec59cd0c8b
13 changed files with 324 additions and 329 deletions

View File

@@ -21,7 +21,7 @@ import android.os.INetworkManagementService
import android.os.ServiceManager import android.os.ServiceManager
import android.util.Log import android.util.Log
import androidx.annotation.OpenForTesting 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 com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -36,13 +36,13 @@ open class BillingCycleRepository @JvmOverloads constructor(
INetworkManagementService.Stub.asInterface( INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
), ),
private val telephonyRepository: TelephonyRepository = TelephonyRepository(context), private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
) { ) {
private val userManager = context.userManager private val userManager = context.userManager
fun isModifiableFlow(subId: Int): Flow<Boolean> = fun isModifiableFlow(subId: Int): Flow<Boolean> =
telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled -> mobileDataRepository.isMobileDataEnabledFlow(subId).map { mobileDataEnabled ->
isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser mobileDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
}.conflate().flowOn(Dispatchers.Default) }.conflate().flowOn(Dispatchers.Default)
open fun isBandwidthControlEnabled(): Boolean = try { open fun isBandwidthControlEnabled(): Boolean = try {

View File

@@ -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<Unit> {
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)
}
}
}

View File

@@ -27,9 +27,8 @@ import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo import android.telephony.UiccSlotInfo
import android.util.Log import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType 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.sim.SimActivationNotifier
import com.android.settings.spa.network.setAutomaticData
import com.android.settings.spa.network.setDefaultData import com.android.settings.spa.network.setDefaultData
import com.android.settings.spa.network.setDefaultSms import com.android.settings.spa.network.setDefaultSms
import com.android.settings.spa.network.setDefaultVoice import com.android.settings.spa.network.setDefaultVoice
@@ -366,7 +365,7 @@ class SimOnboardingService {
wifiPickerTrackerHelper, wifiPickerTrackerHelper,
targetPrimarySimMobileData targetPrimarySimMobileData
) )
TelephonyRepository(context).setAutomaticData( MobileDataRepository(context).setAutoDataSwitch(
targetNonDds, targetNonDds,
targetPrimarySimAutoDataSwitch.value targetPrimarySimAutoDataSwitch.value
) )

View File

@@ -22,7 +22,6 @@ import android.telephony.TelephonyManager
import android.telephony.data.ApnSetting import android.telephony.data.ApnSetting
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.android.settings.network.mobileDataEnabledFlow
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@@ -71,7 +70,7 @@ class MmsMessagePreferenceController @JvmOverloads constructor(
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
combine( combine(
mContext.mobileDataEnabledFlow(mSubId), MobileDataRepository(mContext).mobileDataEnabledChangedFlow(mSubId),
mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes
) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) {
preferenceScreen?.let { super.displayPreference(it) } preferenceScreen?.let { super.displayPreference(it) }

View File

@@ -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<Unit> = context.subscriptionsChangedFlow(),
) {
fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow<Boolean> {
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<Unit> =
mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue)
private fun mobileSettingsGlobalChangedFlow(
name: String,
subId: Int,
sendInitialValue: Boolean = true,
): Flow<Unit> {
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<Boolean> {
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"
}
}

View File

@@ -17,98 +17,16 @@
package com.android.settings.network.telephony package com.android.settings.network.telephony
import android.content.Context import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager 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.Dispatchers
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn 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<Unit> = context.subscriptionsChangedFlow(),
) {
fun isMobileDataPolicyEnabledFlow(
subId: Int,
@TelephonyManager.MobileDataPolicy policy: Int,
): Flow<Boolean> {
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<Boolean> {
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]. */ /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
fun <T> Context.telephonyCallbackFlow( fun <T> Context.telephonyCallbackFlow(

View File

@@ -24,7 +24,7 @@ import android.telephony.TelephonyManager
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.R 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.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.requireSubscriptionManager import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settings.network.telephony.safeGetConfig import com.android.settings.network.telephony.safeGetConfig
@@ -54,6 +54,7 @@ class CrossSimCallingViewModel(
private val scope = viewModelScope + Dispatchers.Default private val scope = viewModelScope + Dispatchers.Default
private val metricsFeatureProvider = featureFactory.metricsFeatureProvider private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
private val updateChannel = Channel<Unit>() private val updateChannel = Channel<Unit>()
private val mobileDataRepository = MobileDataRepository(application)
init { init {
val resources = application.resources val resources = application.resources
@@ -81,7 +82,7 @@ class CrossSimCallingViewModel(
} }
private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId -> private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false) mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false)
}.merge() }.merge()
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) { private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {

View File

@@ -16,13 +16,11 @@
package com.android.settings.spa.network package com.android.settings.spa.network
import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel
import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel 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,
)
}

View File

@@ -16,12 +16,10 @@
package com.android.settings.spa.network package com.android.settings.spa.network
import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.android.settings.R 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.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@@ -48,13 +48,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.telephony.DataSubscriptionRepository 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.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.wifi.WifiPickerTrackerHelper import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator 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.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -202,21 +203,18 @@ fun MobileDataSectionImpl(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val localLifecycleOwner = LocalLifecycleOwner.current val localLifecycleOwner = LocalLifecycleOwner.current
val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner) val mobileDataRepository = rememberContext(::MobileDataRepository)
val subscriptionManager: SubscriptionManager? =
context.getSystemService(SubscriptionManager::class.java)
Category(title = stringResource(id = R.string.mobile_data_settings_title)) { Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
val isAutoDataEnabled by remember(nonDds.intValue) { val isAutoDataEnabled by remember(nonDds.intValue) {
TelephonyRepository(context).isMobileDataPolicyEnabledFlow( mobileDataRepository.isMobileDataPolicyEnabledFlow(
subId = nonDds.intValue, subId = nonDds.intValue,
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
) )
}.collectAsStateWithLifecycle(initialValue = null) }.collectAsStateWithLifecycle(initialValue = null)
val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) { val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue) mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue)
}.collectAsStateWithLifecycle(initialValue = false) }.collectAsStateWithLifecycle(initialValue = false)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@@ -226,8 +224,8 @@ fun MobileDataSectionImpl(
coroutineScope.launch { coroutineScope.launch {
setMobileData( setMobileData(
context, context,
subscriptionManager, context.getSystemService(SubscriptionManager::class.java),
wifiPickerTrackerHelper, getWifiPickerTrackerHelper(context, localLifecycleOwner),
mobileDataSelectedId.intValue, mobileDataSelectedId.intValue,
newEnabled newEnabled
) )
@@ -238,7 +236,7 @@ fun MobileDataSectionImpl(
AutomaticDataSwitchingPreference( AutomaticDataSwitchingPreference(
isAutoDataEnabled = { isAutoDataEnabled }, isAutoDataEnabled = { isAutoDataEnabled },
setAutoDataEnabled = { newEnabled -> 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]") Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]")
subscriptionManager?.setDefaultDataSubId(targetSubId) subscriptionManager?.setDefaultDataSubId(targetSubId)
} }
TelephonyRepository(context) MobileDataRepository(context)
.setMobileData(targetSubId, enabled, wifiPickerTrackerHelper) .setMobileDataEnabled(targetSubId, enabled, wifiPickerTrackerHelper)
} }

View File

@@ -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
}
}

View File

@@ -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<TelephonyManager> { 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
}
}

View File

@@ -17,14 +17,12 @@
package com.android.settings.network.telephony package com.android.settings.network.telephony
import android.content.Context import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -33,91 +31,29 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.spy import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class TelephonyRepositoryTest { class TelephonyRepositoryTest {
private var telephonyCallback: TelephonyCallback? = null private var telephonyCallback: TelephonyCallback? = null
private val mockTelephonyManager = mock<TelephonyManager> { private val mockTelephonyManager =
on { createForSubscriptionId(SUB_ID) } doReturn mock mock<TelephonyManager> {
on { registerTelephonyCallback(any(), any()) } doAnswer { on { createForSubscriptionId(SUB_ID) } doReturn mock
telephonyCallback = it.arguments[1] as TelephonyCallback 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
} }
val flow = repository.isMobileDataPolicyEnabledFlow( private val context: Context =
subId = SUB_ID, spy(ApplicationProvider.getApplicationContext()) {
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
)
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
} }
val state = repository.isDataEnabledFlow(subId = SUB_ID)
assertThat(state.firstWithTimeoutOrNull()).isTrue()
}
@Test @Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking { fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull() flow.firstWithTimeoutOrNull()
@@ -126,9 +62,7 @@ class TelephonyRepositoryTest {
@Test @Test
fun telephonyCallbackFlow_callbackUnregistered() = runBlocking { fun telephonyCallbackFlow_callbackUnregistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull() flow.firstWithTimeoutOrNull()