Merge changes from topic "RoamingPreferenceController" into main

* changes:
  Fix crash in RoamingPreferenceController
  New CarrierConfigRepository
This commit is contained in:
Chaohui Wang
2024-06-21 08:55:56 +00:00
committed by Android (Google) Code Review
13 changed files with 752 additions and 520 deletions

View File

@@ -85,13 +85,9 @@
android:summary="@string/auto_data_switch_summary"
settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
<com.android.settingslib.RestrictedSwitchPreference
<com.android.settings.spa.preference.ComposePreference
android:key="button_roaming_key"
android:title="@string/roaming"
android:persistent="false"
android:summaryOn="@string/roaming_enable"
android:summaryOff="@string/roaming_disable"
settings:userRestriction="no_data_roaming"
settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
<Preference

View File

@@ -23,10 +23,10 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.network.telephony.CarrierConfigRepository
import com.android.settings.network.telephony.SimSlotRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.safeGetConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -39,7 +39,9 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
class SimStatusDialogRepository @JvmOverloads constructor(
class SimStatusDialogRepository
@JvmOverloads
constructor(
private val context: Context,
private val simSlotRepository: SimSlotRepository = SimSlotRepository(context),
private val signalStrengthRepository: SignalStrengthRepository =
@@ -48,7 +50,7 @@ class SimStatusDialogRepository @JvmOverloads constructor(
ImsMmTelRepositoryImpl(context, subId)
},
) {
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
private val carrierConfigRepository = CarrierConfigRepository(context)
data class SimStatusDialogInfo(
val signalStrength: String? = null,
@@ -73,7 +75,8 @@ class SimStatusDialogRepository @JvmOverloads constructor(
}
private fun simStatusDialogInfoBySlotFlow(simSlotIndex: Int): Flow<SimStatusDialogInfo> =
simSlotRepository.subIdInSimSlotFlow(simSlotIndex)
simSlotRepository
.subIdInSimSlotFlow(simSlotIndex)
.flatMapLatest { subId ->
if (SubscriptionManager.isValidSubscriptionId(subId)) {
simStatusDialogInfoFlow(subId)
@@ -99,22 +102,16 @@ class SimStatusDialogRepository @JvmOverloads constructor(
}
private fun showUpFlow(subId: Int) = flow {
val config = carrierConfigManager.safeGetConfig(
keys = listOf(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
),
subId = subId,
)
val visibility = SimStatusDialogVisibility(
signalStrengthShowUp = config.getBoolean(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
true, // by default we show the signal strength in sim status
),
imsRegisteredShowUp = config.getBoolean(
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL
),
)
val visibility =
carrierConfigRepository.transformConfig(subId) {
SimStatusDialogVisibility(
signalStrengthShowUp =
getBoolean(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL),
imsRegisteredShowUp =
getBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL),
)
}
emit(visibility)
}
}

View File

@@ -24,6 +24,7 @@ import androidx.core.os.persistableBundleOf
/**
* Gets the configuration values of the specified config keys applied.
*/
@Deprecated("Use CarrierConfigRepository instead")
fun CarrierConfigManager.safeGetConfig(
keys: List<String>,
subId: Int = SubscriptionManager.getDefaultSubscriptionId(),

View File

@@ -0,0 +1,217 @@
/*
* 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.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.util.Log
import androidx.annotation.VisibleForTesting
import java.util.concurrent.ConcurrentHashMap
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
class CarrierConfigRepository(private val context: Context) {
private val carrierConfigManager: CarrierConfigManager? =
context.getSystemService(CarrierConfigManager::class.java)
private enum class KeyType {
BOOLEAN,
INT,
STRING
}
interface CarrierConfigAccessor {
fun getBoolean(key: String): Boolean
fun getInt(key: String): Int
fun getString(key: String): String?
}
private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor {
private val keysToRetrieve = mutableMapOf<String, KeyType>()
override fun getBoolean(key: String): Boolean {
check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
val value = cache[key]
return if (value == null) {
keysToRetrieve += key to KeyType.BOOLEAN
DefaultConfig.getBoolean(key)
} else {
check(value is BooleanConfigValue) { "Boolean value type wrong" }
value.value
}
}
override fun getInt(key: String): Int {
check(key.endsWith("_int")) { "Int key should ends with _int" }
val value = cache[key]
return if (value == null) {
keysToRetrieve += key to KeyType.INT
DefaultConfig.getInt(key)
} else {
check(value is IntConfigValue) { "Int value type wrong" }
value.value
}
}
override fun getString(key: String): String? {
check(key.endsWith("_string")) { "String key should ends with _string" }
val value = cache[key]
return if (value == null) {
keysToRetrieve += key to KeyType.STRING
DefaultConfig.getString(key)
} else {
check(value is StringConfigValue) { "String value type wrong" }
value.value
}
}
fun getKeysToRetrieve(): Map<String, KeyType> = keysToRetrieve
}
/**
* Gets the configuration values for the given [subId].
*
* Configuration values could be accessed in [block]. Note: [block] could be called multiple
* times, so it should be pure function without side effort.
*/
fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T {
val perSubCache = getPerSubCache(subId)
val accessor = Accessor(perSubCache)
val result = accessor.block()
val keysToRetrieve = accessor.getKeysToRetrieve()
// If all keys found in the first pass, no need to collect again
if (keysToRetrieve.isEmpty()) return result
perSubCache.update(subId, keysToRetrieve)
return accessor.block()
}
/** Gets the configuration boolean for the given [subId] and [key]. */
fun getBoolean(subId: Int, key: String): Boolean = transformConfig(subId) { getBoolean(key) }
/** Gets the configuration int for the given [subId] and [key]. */
fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) }
/** Gets the configuration string for the given [subId] and [key]. */
fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) }
private fun ConfigCache.update(subId: Int, keysToRetrieve: Map<String, KeyType>) {
val config = safeGetConfig(subId, keysToRetrieve.keys) ?: return
for ((key, type) in keysToRetrieve) {
when (type) {
KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key))
KeyType.INT -> this[key] = IntConfigValue(config.getInt(key))
KeyType.STRING -> this[key] = StringConfigValue(config.getString(key))
}
}
}
/** Gets the configuration values of the specified config keys applied. */
private fun safeGetConfig(subId: Int, keys: Collection<String>): PersistableBundle? {
if (carrierConfigManager == null || !SubscriptionManager.isValidSubscriptionId(subId)) {
return null
}
tryRegisterListener(context)
return try {
carrierConfigManager.getConfigForSubId(subId, *keys.toTypedArray())
} catch (e: Exception) {
Log.e(TAG, "safeGetConfig: exception", e)
// The CarrierConfigLoader (the service implemented the CarrierConfigManager) hasn't
// been initialized yet. This may occurs during very early phase of phone booting up
// or when Phone process has been restarted.
// Settings should not assume Carrier config loader (and any other system services
// as well) are always available. If not available, use default value instead.
null
}
}
companion object {
private const val TAG = "CarrierConfigRepository"
private val DefaultConfig = CarrierConfigManager.getDefaultConfig()
/** Cache of config values for each subscription. */
private val Cache = ConcurrentHashMap<Int, ConfigCache>()
private fun getPerSubCache(subId: Int) =
Cache.computeIfAbsent(subId) { ConcurrentHashMap() }
/** To make sure the registerCarrierConfigChangeListener is only called once. */
private val ListenerRegistered = atomic(false)
private fun tryRegisterListener(context: Context) {
if (ListenerRegistered.compareAndSet(expect = false, update = true)) {
val carrierConfigManager =
context.applicationContext.getSystemService(CarrierConfigManager::class.java)
if (carrierConfigManager != null) {
carrierConfigManager.registerCarrierConfigChangeListener()
} else {
ListenerRegistered.getAndSet(false)
}
}
}
private fun CarrierConfigManager.registerCarrierConfigChangeListener() {
val executor = Dispatchers.Default.asExecutor()
registerCarrierConfigChangeListener(executor) { _, subId, _, _ ->
Log.d(TAG, "[$subId] onCarrierConfigChanged")
Cache.remove(subId)
}
}
@VisibleForTesting
fun resetForTest() {
Cache.clear()
ListenerRegistered.getAndSet(false)
}
@VisibleForTesting
fun setBooleanForTest(subId: Int, key: String, value: Boolean) {
check(key.endsWith("_bool")) { "Boolean key should ends with _bool" }
getPerSubCache(subId)[key] = BooleanConfigValue(value)
}
@VisibleForTesting
fun setIntForTest(subId: Int, key: String, value: Int) {
check(key.endsWith("_int")) { "Int key should ends with _int" }
getPerSubCache(subId)[key] = IntConfigValue(value)
}
@VisibleForTesting
fun setStringForTest(subId: Int, key: String, value: String) {
check(key.endsWith("_string")) { "String key should ends with _string" }
getPerSubCache(subId)[key] = StringConfigValue(value)
}
}
}
private sealed interface ConfigValue
private data class BooleanConfigValue(val value: Boolean) : ConfigValue
private data class IntConfigValue(val value: Int) : ConfigValue
private data class StringConfigValue(val value: String?) : ConfigValue
private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue>

View File

@@ -118,6 +118,18 @@ class MobileDataRepository(
}
}
/** Creates an instance of a cold Flow for whether data roaming is enabled of given [subId]. */
fun isDataRoamingEnabledFlow(subId: Int): Flow<Boolean> {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
val telephonyManager = context.telephonyManager(subId)
return mobileSettingsGlobalChangedFlow(Settings.Global.DATA_ROAMING, subId)
.map { telephonyManager.isDataRoamingEnabled }
.distinctUntilChanged()
.conflate()
.onEach { Log.d(TAG, "[$subId] isDataRoamingEnabledFlow: $it") }
.flowOn(Dispatchers.Default)
}
private companion object {
private const val TAG = "MobileDataRepository"
}

View File

@@ -79,7 +79,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
@VisibleForTesting
static final String KEY_CLICKED_PREF = "key_clicked_pref";
private static final String KEY_ROAMING_PREF = "button_roaming_key";
private static final String KEY_CALLS_PREF = "calls_preference";
private static final String KEY_SMS_PREF = "sms_preference";
private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable";
@@ -178,8 +177,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
return Arrays.asList(
new DataUsageSummaryPreferenceController(context, mSubId),
new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
this, mSubId),
new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,
getSettingsLifecycle(), this),
new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(),
@@ -263,8 +260,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
final RoamingPreferenceController roamingPreferenceController =
use(RoamingPreferenceController.class);
if (roamingPreferenceController != null) {
roamingPreferenceController.init(getFragmentManager(), mSubId,
mMobileNetworkInfoEntity);
roamingPreferenceController.init(getParentFragmentManager(), mSubId);
}
final SatelliteSettingPreferenceController satelliteSettingPreferenceController = use(
SatelliteSettingPreferenceController.class);

View File

@@ -1,215 +0,0 @@
/*
* Copyright (C) 2018 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 static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.content.Context;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.network.MobileNetworkRepository;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import java.util.ArrayList;
import java.util.List;
/**
* Preference controller for "Roaming"
*/
public class RoamingPreferenceController extends TelephonyTogglePreferenceController implements
LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback {
private static final String TAG = "RoamingController";
private static final String DIALOG_TAG = "MobileDataDialog";
private RestrictedSwitchPreference mSwitchPreference;
private TelephonyManager mTelephonyManager;
private CarrierConfigManager mCarrierConfigManager;
protected MobileNetworkRepository mMobileNetworkRepository;
protected LifecycleOwner mLifecycleOwner;
private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
@VisibleForTesting
FragmentManager mFragmentManager;
MobileNetworkInfoEntity mMobileNetworkInfoEntity;
public RoamingPreferenceController(Context context, String key, Lifecycle lifecycle,
LifecycleOwner lifecycleOwner, int subId) {
this(context, key);
mSubId = subId;
mLifecycleOwner = lifecycleOwner;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
public RoamingPreferenceController(Context context, String key) {
super(context, key);
mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
mMobileNetworkRepository = MobileNetworkRepository.getInstance(context);
}
@Override
public int getAvailabilityStatus() {
final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
if (carrierConfig != null && carrierConfig.getBoolean(
CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)) {
return CONDITIONALLY_UNAVAILABLE;
}
return AVAILABLE;
}
@OnLifecycleEvent(ON_START)
public void onStart() {
mMobileNetworkRepository.addRegister(mLifecycleOwner, this, mSubId);
mMobileNetworkRepository.updateEntity();
}
@OnLifecycleEvent(ON_STOP)
public void onStop() {
mMobileNetworkRepository.removeRegister(this);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mSwitchPreference = screen.findPreference(getPreferenceKey());
}
@Override
public int getAvailabilityStatus(int subId) {
return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
? AVAILABLE
: AVAILABLE_UNSEARCHABLE;
}
@Override
public boolean setChecked(boolean isChecked) {
if (isDialogNeeded()) {
showDialog();
} else {
// Update data directly if we don't need dialog
mTelephonyManager.setDataRoamingEnabled(isChecked);
return true;
}
return false;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mSwitchPreference = (RestrictedSwitchPreference) preference;
update();
}
private void update() {
if (mSwitchPreference == null) {
return;
}
if (!mSwitchPreference.isDisabledByAdmin()) {
mSwitchPreference.setEnabled(mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mSwitchPreference.setChecked(isChecked());
}
}
@VisibleForTesting
boolean isDialogNeeded() {
final boolean isRoamingEnabled = mMobileNetworkInfoEntity == null ? false
: mMobileNetworkInfoEntity.isDataRoamingEnabled;
final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
mSubId);
// Need dialog if we need to turn on roaming and the roaming charge indication is allowed
if (!isRoamingEnabled && (carrierConfig == null || !carrierConfig.getBoolean(
CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL))) {
return true;
}
return false;
}
@Override
public boolean isChecked() {
return mMobileNetworkInfoEntity == null ? false
: mMobileNetworkInfoEntity.isDataRoamingEnabled;
}
public void init(FragmentManager fragmentManager, int subId, MobileNetworkInfoEntity entity) {
mFragmentManager = fragmentManager;
mSubId = subId;
mMobileNetworkInfoEntity = entity;
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return;
}
final TelephonyManager telephonyManager = mTelephonyManager
.createForSubscriptionId(mSubId);
if (telephonyManager == null) {
Log.w(TAG, "fail to init in sub" + mSubId);
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
return;
}
mTelephonyManager = telephonyManager;
}
private void showDialog() {
final RoamingDialogFragment dialogFragment = RoamingDialogFragment.newInstance(mSubId);
dialogFragment.show(mFragmentManager, DIALOG_TAG);
}
@VisibleForTesting
public void setMobileNetworkInfoEntity(MobileNetworkInfoEntity mobileNetworkInfoEntity) {
mMobileNetworkInfoEntity = mobileNetworkInfoEntity;
}
@Override
public void onAllMobileNetworkInfoChanged(
List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList;
mMobileNetworkInfoEntityList.forEach(entity -> {
if (Integer.parseInt(entity.subId) == mSubId) {
mMobileNetworkInfoEntity = entity;
update();
refreshSummary(mSwitchPreference);
return;
}
});
}
@Override
public void onDataRoamingChanged(int subId, boolean enabled) {
if (subId != mSubId) {
Log.d(TAG, "onDataRoamingChanged - wrong subId : " + subId + " / " + enabled);
return;
}
update();
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.os.UserManager
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
/** Preference controller for "Roaming" */
class RoamingPreferenceController
@JvmOverloads
constructor(
context: Context,
key: String,
private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
) : ComposePreferenceController(context, key) {
@VisibleForTesting var fragmentManager: FragmentManager? = null
private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
private val carrierConfigRepository = CarrierConfigRepository(context)
fun init(fragmentManager: FragmentManager, subId: Int) {
this.fragmentManager = fragmentManager
this.subId = subId
telephonyManager = telephonyManager.createForSubscriptionId(subId)
}
override fun getAvailabilityStatus(): Int {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return CONDITIONALLY_UNAVAILABLE
val isForceHomeNetwork =
carrierConfigRepository.getBoolean(
subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)
return if (isForceHomeNetwork) CONDITIONALLY_UNAVAILABLE else AVAILABLE
}
@Composable
override fun Content() {
val summary = stringResource(R.string.roaming_enable)
val isDataRoamingEnabled by
remember { mobileDataRepository.isDataRoamingEnabledFlow(subId) }
.collectAsStateWithLifecycle(null)
RestrictedSwitchPreference(
model =
object : SwitchPreferenceModel {
override val title = stringResource(R.string.roaming)
override val summary = { summary }
override val checked = { isDataRoamingEnabled }
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
if (newChecked && isDialogNeeded()) {
showDialog()
} else {
// Update data directly if we don't need dialog
telephonyManager.isDataRoamingEnabled = newChecked
}
}
},
restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_DATA_ROAMING)),
)
}
@VisibleForTesting
fun isDialogNeeded(): Boolean {
// Need dialog if we need to turn on roaming and the roaming charge indication is allowed
return !carrierConfigRepository.getBoolean(
subId, CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)
}
private fun showDialog() {
fragmentManager?.let { RoamingDialogFragment.newInstance(subId).show(it, DIALOG_TAG) }
}
companion object {
private const val DIALOG_TAG = "MobileDataDialog"
}
}

View File

@@ -17,65 +17,65 @@
package com.android.settings.deviceinfo.simstatus
import android.content.Context
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.deviceinfo.simstatus.SimStatusDialogRepository.SimStatusDialogInfo
import com.android.settings.network.telephony.CarrierConfigRepository
import com.android.settings.network.telephony.SimSlotRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.anyVararg
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@RunWith(AndroidJUnit4::class)
class SimStatusDialogRepositoryTest {
private val carrierConfig = PersistableBundle().apply {
putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true)
}
private val context: Context = ApplicationProvider.getApplicationContext()
private val mockCarrierConfigManager = mock<CarrierConfigManager> {
on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig
}
private val mockSimSlotRepository =
mock<SimSlotRepository> {
on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
}
private val mockSignalStrengthRepository =
mock<SignalStrengthRepository> {
on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH)
}
private val mockSimSlotRepository = mock<SimSlotRepository> {
on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID)
}
private val mockImsMmTelRepository =
mock<ImsMmTelRepository> { on { imsRegisteredFlow() } doReturn flowOf(true) }
private val mockSignalStrengthRepository = mock<SignalStrengthRepository> {
on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH)
}
private val controller =
SimStatusDialogRepository(
context = context,
simSlotRepository = mockSimSlotRepository,
signalStrengthRepository = mockSignalStrengthRepository,
imsMmTelRepositoryFactory = { subId ->
assertThat(subId).isEqualTo(SUB_ID)
mockImsMmTelRepository
},
)
private val mockImsMmTelRepository = mock<ImsMmTelRepository> {
on { imsRegisteredFlow() } doReturn flowOf(true)
@Before
fun setUp() {
CarrierConfigRepository.resetForTest()
}
private val controller = SimStatusDialogRepository(
context = context,
simSlotRepository = mockSimSlotRepository,
signalStrengthRepository = mockSignalStrengthRepository,
imsMmTelRepositoryFactory = { subId ->
assertThat(subId).isEqualTo(SUB_ID)
mockImsMmTelRepository
},
)
@Test
fun collectSimStatusDialogInfo() = runBlocking {
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
value = true,
)
var simStatusDialogInfo = SimStatusDialogInfo()
controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {
@@ -83,19 +83,20 @@ class SimStatusDialogRepositoryTest {
}
delay(100)
assertThat(simStatusDialogInfo).isEqualTo(
SimStatusDialogInfo(
signalStrength = SIGNAL_STRENGTH,
imsRegistered = true,
)
)
assertThat(simStatusDialogInfo)
.isEqualTo(
SimStatusDialogInfo(
signalStrength = SIGNAL_STRENGTH,
imsRegistered = true,
))
}
@Test
fun collectSimStatusDialogInfo_doNotShowSignalStrength() = runBlocking {
carrierConfig.putBoolean(
CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
false
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL,
value = false,
)
var simStatusDialogInfo = SimStatusDialogInfo()
@@ -109,7 +110,11 @@ class SimStatusDialogRepositoryTest {
@Test
fun collectSimStatusDialogInfo_doNotShowImsRegistration() = runBlocking {
carrierConfig.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false)
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL,
value = false,
)
var simStatusDialogInfo = SimStatusDialogInfo()
controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) {

View File

@@ -0,0 +1,138 @@
/*
* 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.telephony.CarrierConfigManager
import androidx.core.os.persistableBundleOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyVararg
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class CarrierConfigRepositoryTest {
private val mockCarrierConfigManager = mock<CarrierConfigManager>()
private val context =
mock<Context> {
on { applicationContext } doReturn mock
on { getSystemService(CarrierConfigManager::class.java) } doReturn
mockCarrierConfigManager
}
private val repository = CarrierConfigRepository(context)
@Before
fun setUp() {
CarrierConfigRepository.resetForTest()
}
@Test
fun getBoolean_returnValue() {
val key = CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to true)
}
val value = repository.getBoolean(SUB_ID, key)
assertThat(value).isTrue()
}
@Test
fun getInt_returnValue() {
val key = CarrierConfigManager.KEY_GBA_MODE_INT
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to 99)
}
val value = repository.getInt(SUB_ID, key)
assertThat(value).isEqualTo(99)
}
@Test
fun getString_returnValue() {
val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(key)) } doReturn
persistableBundleOf(key to STRING_VALUE)
}
val value = repository.getString(SUB_ID, key)
assertThat(value).isEqualTo(STRING_VALUE)
}
@Test
fun transformConfig_managerThrowIllegalStateException_returnDefaultValue() {
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), anyVararg()) } doThrow IllegalStateException()
}
val carrierName =
repository.transformConfig(SUB_ID) {
getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT)
}
assertThat(carrierName)
.isEqualTo(
CarrierConfigManager.getDefaultConfig()
.getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT))
}
@Test
fun transformConfig_getValueTwice_cached() {
val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
mockCarrierConfigManager.stub {
on { getConfigForSubId(any(), eq(key)) } doReturn
persistableBundleOf(key to STRING_VALUE)
}
repository.transformConfig(SUB_ID) { getString(key) }
repository.transformConfig(SUB_ID) { getString(key) }
verify(mockCarrierConfigManager, times(1)).getConfigForSubId(any(), anyVararg())
}
@Test
fun transformConfig_registerCarrierConfigChangeListener() {
val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
repository.transformConfig(SUB_ID) { getString(key) }
repository.transformConfig(SUB_ID) { getString(key) }
verify(mockCarrierConfigManager, times(1)).registerCarrierConfigChangeListener(any(), any())
}
private companion object {
const val SUB_ID = 123
const val STRING_VALUE = "value"
}
}

View File

@@ -162,6 +162,26 @@ class MobileDataRepositoryTest {
assertThat(state.firstWithTimeoutOrNull()).isTrue()
}
@Test
fun isDataRoamingEnabledFlow_invalidSub_returnFalse() = runBlocking {
val isDataRoamingEnabled =
repository
.isDataRoamingEnabledFlow(subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.firstWithTimeoutOrNull()
assertThat(isDataRoamingEnabled).isFalse()
}
@Test
fun isDataRoamingEnabledFlow_validSub_returnCurrentValue() = runBlocking {
mockTelephonyManager.stub { on { isDataRoamingEnabled } doReturn true }
val isDataRoamingEnabled =
repository.isDataRoamingEnabledFlow(subId = SUB_ID).firstWithTimeoutOrNull()
assertThat(isDataRoamingEnabled).isTrue()
}
private companion object {
const val SUB_ID = 123
}

View File

@@ -0,0 +1,194 @@
/*
* 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.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.fragment.app.FragmentManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.core.BasePreferenceController.AVAILABLE
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class RoamingPreferenceControllerTest {
@get:Rule val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private val mockMobileDataRepository =
mock<MobileDataRepository> {
on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false)
}
private val controller =
RoamingPreferenceController(context, TEST_KEY, mockMobileDataRepository)
@Before
fun setUp() {
CarrierConfigRepository.resetForTest()
}
@Test
fun getAvailabilityStatus_validSubId_returnAvailable() {
controller.init(mock<FragmentManager>(), SUB_ID)
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun getAvailabilityStatus_invalidSubId_returnConditionallyUnavailable() {
controller.init(mock<FragmentManager>(), SubscriptionManager.INVALID_SUBSCRIPTION_ID)
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_forceHomeNetworkIsTrue_returnConditionallyUnavailable() {
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL,
value = true,
)
controller.init(mock<FragmentManager>(), SUB_ID)
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
}
@Test
fun getAvailabilityStatus_forceHomeNetworkIsFalse_returnAvailable() {
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL,
value = false,
)
controller.init(mock<FragmentManager>(), SUB_ID)
val availabilityStatus = controller.getAvailabilityStatus()
assertThat(availabilityStatus).isEqualTo(AVAILABLE)
}
@Test
fun title_displayed() {
controller.init(mock<FragmentManager>(), SUB_ID)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { controller.Content() }
}
composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsDisplayed()
}
@Test
fun summary_displayed() {
controller.init(mock<FragmentManager>(), SUB_ID)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { controller.Content() }
}
composeTestRule
.onNodeWithText(context.getString(R.string.roaming_enable))
.assertIsDisplayed()
}
@Test
fun isDialogNeeded_enableChargeIndication_returnTrue() {
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL,
value = false,
)
controller.init(mock<FragmentManager>(), SUB_ID)
val isDialogNeeded = controller.isDialogNeeded()
assertThat(isDialogNeeded).isTrue()
}
@Test
fun isDialogNeeded_disableChargeIndication_returnFalse() {
CarrierConfigRepository.setBooleanForTest(
subId = SUB_ID,
key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL,
value = true,
)
controller.init(mock<FragmentManager>(), SUB_ID)
val isDialogNeeded = controller.isDialogNeeded()
assertThat(isDialogNeeded).isFalse()
}
@Test
fun checked_roamingEnabled_isOn() {
mockMobileDataRepository.stub {
on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(true)
}
controller.init(mock<FragmentManager>(), SUB_ID)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { controller.Content() }
}
composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOn()
}
@Test
fun checked_roamingDisabled_isOff() {
mockMobileDataRepository.stub {
on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false)
}
controller.init(mock<FragmentManager>(), SUB_ID)
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) { controller.Content() }
}
composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOff()
}
private companion object {
const val TEST_KEY = "test_key"
const val SUB_ID = 2
}
}

View File

@@ -1,234 +0,0 @@
/*
* Copyright (C) 2020 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 static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class RoamingPreferenceControllerTest {
private static final int SUB_ID = 2;
@Mock
private FragmentManager mFragmentManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private TelephonyManager mInvalidTelephonyManager;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private FragmentTransaction mFragmentTransaction;
@Mock
private CarrierConfigManager mCarrierConfigManager;
@Mock
private Lifecycle mLifecycle;
@Mock
private LifecycleOwner mLifecycleOwner;
private LifecycleRegistry mLifecycleRegistry;
private RoamingPreferenceController mController;
private RestrictedSwitchPreference mPreference;
private Context mContext;
private MobileNetworkInfoEntity mMobileNetworkInfoEntity;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
if (Looper.myLooper() == null) {
Looper.prepare();
}
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
doReturn(mSubscriptionManager).when(mContext).getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
doReturn(mCarrierConfigManager).when(mContext).getSystemService(
Context.CARRIER_CONFIG_SERVICE);
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction();
mPreference = spy(new RestrictedSwitchPreference(mContext));
mController = spy(
new RoamingPreferenceController(mContext, "roaming", mLifecycle, mLifecycleOwner,
SUB_ID));
mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
mController.init(mFragmentManager, SUB_ID, mMobileNetworkInfoEntity);
mPreference.setKey(mController.getPreferenceKey());
}
private MobileNetworkInfoEntity setupMobileNetworkInfoEntity(String subId,
boolean isDataRoaming) {
return new MobileNetworkInfoEntity(subId, false, false, true, false, false, false, false,
false, false, false, isDataRoaming);
}
@Test
public void getAvailabilityStatus_validSubId_returnAvailable() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(
AVAILABLE);
}
@Test
public void getAvailabilityStatus_invalidSubId_returnUnsearchable() {
mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
mMobileNetworkInfoEntity);
assertThat(mController.getAvailabilityStatus(
SubscriptionManager.INVALID_SUBSCRIPTION_ID)).isEqualTo(
BasePreferenceController.AVAILABLE_UNSEARCHABLE);
}
@Test
public void isDialogNeeded_roamingDisabledWithoutFlag_returnTrue() {
final PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
assertThat(mController.isDialogNeeded()).isTrue();
}
@Test
public void isDialogNeeded_roamingEnabled_returnFalse() {
mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
assertThat(mController.isDialogNeeded()).isFalse();
}
@Test
@UiThreadTest
public void setChecked_needDialog_showDialog() {
mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false);
mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
mController.setChecked(true);
verify(mFragmentManager).beginTransaction();
}
@Test
public void updateState_invalidSubId_disabled() {
mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(
String.valueOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID), false);
mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
mMobileNetworkInfoEntity);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void updateState_validSubId_enabled() {
mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true);
mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void updateState_isNotDisabledByAdmin_shouldInvokeSetEnabled() {
when(mPreference.isDisabledByAdmin()).thenReturn(false);
mController.updateState(mPreference);
verify(mPreference).setEnabled(anyBoolean());
}
@Test
public void updateState_isDisabledByAdmin_shouldNotInvokeSetEnabled() {
when(mPreference.isDisabledByAdmin()).thenReturn(true);
mController.updateState(mPreference);
verify(mPreference, never()).setEnabled(anyBoolean());
}
@Test
public void getAvailabilityStatus_carrierConfigIsNull_shouldReturnAvailable() {
doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_forceHomeNetworkIsFalse_shouldReturnAvailable() {
final PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, false);
doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_forceHomeNetworkIsTrue_shouldReturnConditionallyAvailable() {
final PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, true);
doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
}