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.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<Boolean> =
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 {

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.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
)

View File

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

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
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<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]. */
fun <T> Context.telephonyCallbackFlow(

View File

@@ -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<Unit>()
private val mobileDataRepository = MobileDataRepository(application)
init {
val resources = application.resources
@@ -81,7 +82,7 @@ class CrossSimCallingViewModel(
}
private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false)
mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false)
}.merge()
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {

View File

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

View File

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

View File

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

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
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<TelephonyManager> {
private val mockTelephonyManager =
mock<TelephonyManager> {
on { createForSubscriptionId(SUB_ID) } doReturn mock
on { registerTelephonyCallback(any(), any()) } doAnswer {
on { registerTelephonyCallback(any(), any()) } doAnswer
{
telephonyCallback = it.arguments[1] as TelephonyCallback
}
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
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(
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
}
val state = repository.isDataEnabledFlow(subId = SUB_ID)
assertThat(state.firstWithTimeoutOrNull()).isTrue()
}
@Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
object : TelephonyCallback() {}
}
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
flow.firstWithTimeoutOrNull()
@@ -126,9 +62,7 @@ class TelephonyRepositoryTest {
@Test
fun telephonyCallbackFlow_callbackUnregistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
object : TelephonyCallback() {}
}
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
flow.firstWithTimeoutOrNull()