Fix crash in RoamingPreferenceController

With new MobileDataRepository.isDataRoamingEnabledFlow() to provide
the data instead of MobileNetworkRepository.

Fix: 347224962
Flag: EXEMPT bug fix
Test: manual on Mobile Settings
Test: unit test
Change-Id: I2a994cb11c93296fb46558f566d6d4467ba4c846
This commit is contained in:
Chaohui Wang
2024-06-19 11:59:27 +08:00
parent ef12f1ddb5
commit cfd401b04e
8 changed files with 333 additions and 459 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

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

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