From cfd401b04e64e3f665aa4b695b132006f18f900b Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 19 Jun 2024 11:59:27 +0800 Subject: [PATCH] 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 --- res/xml/mobile_network_settings.xml | 6 +- .../network/telephony/MobileDataRepository.kt | 12 + .../telephony/MobileNetworkSettings.java | 6 +- .../RoamingPreferenceController.java | 215 ---------------- .../telephony/RoamingPreferenceController.kt | 105 ++++++++ .../telephony/MobileDataRepositoryTest.kt | 20 ++ .../RoamingPreferenceControllerTest.kt | 194 +++++++++++++++ .../RoamingPreferenceControllerTest.java | 234 ------------------ 8 files changed, 333 insertions(+), 459 deletions(-) delete mode 100644 src/com/android/settings/network/telephony/RoamingPreferenceController.java create mode 100644 src/com/android/settings/network/telephony/RoamingPreferenceController.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt delete mode 100644 tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index eb9f4427533..51cbbe6b86f 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -85,13 +85,9 @@ android:summary="@string/auto_data_switch_summary" settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/> - { + 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" } diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 8b927a9f1ca..34d2fbd5436 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -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); diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.java b/src/com/android/settings/network/telephony/RoamingPreferenceController.java deleted file mode 100644 index bf02308be39..00000000000 --- a/src/com/android/settings/network/telephony/RoamingPreferenceController.java +++ /dev/null @@ -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 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 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(); - } -} diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt new file mode 100644 index 00000000000..2529d41324e --- /dev/null +++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt @@ -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" + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt index 268be570048..fc762fa894a 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt @@ -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 } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt new file mode 100644 index 00000000000..ee4cff684ca --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt @@ -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 { + 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(), SUB_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(AVAILABLE) + } + + @Test + fun getAvailabilityStatus_invalidSubId_returnConditionallyUnavailable() { + controller.init(mock(), 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(), 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(), SUB_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(AVAILABLE) + } + + @Test + fun title_displayed() { + controller.init(mock(), 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(), 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(), 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(), 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(), 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(), 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 + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java deleted file mode 100644 index d221280ae48..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java +++ /dev/null @@ -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); - } -}