diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 3c011098a9e..eb9f4427533 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -295,11 +295,9 @@ settings:controller= "com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/> - diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java deleted file mode 100644 index ecf01cb9484..00000000000 --- a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2021 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.Handler; -import android.os.Looper; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyCallback; -import android.telephony.TelephonyManager; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import androidx.preference.TwoStatePreference; - -import com.android.internal.telephony.flags.Flags; -import com.android.internal.telephony.util.ArrayUtils; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnStart; -import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.utils.ThreadUtils; - -/** - * Preference controller for "Voice over NR". - */ -public class NrAdvancedCallingPreferenceController extends TelephonyTogglePreferenceController - implements LifecycleObserver, OnStart, OnStop { - - private static final String TAG = "VoNrSettings"; - - @VisibleForTesting - Preference mPreference; - private TelephonyManager mTelephonyManager; - private PhoneCallStateTelephonyCallback mTelephonyCallback; - private boolean mIsVonrEnabledFromCarrierConfig = false; - private boolean mIsVonrVisibleFromCarrierConfig = false; - private boolean mIsNrEnableFromCarrierConfig = false; - private boolean mHas5gCapability = false; - private boolean mIsVoNrEnabled = false; - private Integer mCallState; - - private Handler mHandler = new Handler(Looper.getMainLooper()); - - public NrAdvancedCallingPreferenceController(Context context, String key) { - super(context, key); - mTelephonyManager = context.getSystemService(TelephonyManager.class); - } - - /** - * Initial this PreferenceController. - * @param subId The subscription Id. - * @return This PreferenceController. - */ - public NrAdvancedCallingPreferenceController init(int subId) { - Log.d(TAG, "init: "); - if (mTelephonyCallback == null) { - mTelephonyCallback = new PhoneCallStateTelephonyCallback(); - } - - mSubId = subId; - - if (mTelephonyManager == null) { - mTelephonyManager = mContext.getSystemService(TelephonyManager.class); - } - if (SubscriptionManager.isValidSubscriptionId(subId)) { - mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); - } - long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily(); - mHas5gCapability = - (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0; - - PersistableBundle carrierConfig = getCarrierConfigForSubId(subId); - if (carrierConfig == null) { - return this; - } - mIsVonrEnabledFromCarrierConfig = carrierConfig.getBoolean( - CarrierConfigManager.KEY_VONR_ENABLED_BOOL); - - mIsVonrVisibleFromCarrierConfig = carrierConfig.getBoolean( - CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL); - - int[] nrAvailabilities = carrierConfig.getIntArray( - CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); - mIsNrEnableFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities); - - updateVoNrState(); - - Log.d(TAG, "mHas5gCapability: " + mHas5gCapability - + ",mIsNrEnabledFromCarrierConfig: " + mIsNrEnableFromCarrierConfig - + ",mIsVonrEnabledFromCarrierConfig: " + mIsVonrEnabledFromCarrierConfig - + ",mIsVonrVisibleFromCarrierConfig: " + mIsVonrVisibleFromCarrierConfig); - return this; - } - - @Override - public int getAvailabilityStatus(int subId) { - init(subId); - - if (mHas5gCapability - && mIsNrEnableFromCarrierConfig - && mIsVonrEnabledFromCarrierConfig - && mIsVonrVisibleFromCarrierConfig) { - return AVAILABLE; - } - return CONDITIONALLY_UNAVAILABLE; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mPreference = screen.findPreference(getPreferenceKey()); - } - - @Override - public void onStart() { - if (mTelephonyCallback == null) { - return; - } - mTelephonyCallback.register(mTelephonyManager); - } - - @Override - public void onStop() { - if (mTelephonyCallback == null) { - return; - } - mTelephonyCallback.unregister(); - } - - @Override - public void updateState(Preference preference) { - super.updateState(preference); - if (preference == null) { - return; - } - final TwoStatePreference switchPreference = (TwoStatePreference) preference; - switchPreference.setEnabled(isUserControlAllowed()); - } - - @Override - public boolean setChecked(boolean isChecked) { - if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { - return false; - } - Log.d(TAG, "setChecked: " + isChecked); - int result = mTelephonyManager.setVoNrEnabled(isChecked); - if (result == TelephonyManager.ENABLE_VONR_SUCCESS) { - return true; - } - Log.d(TAG, "Fail to set VoNR result= " + result + ". subId=" + mSubId); - return false; - } - - @Override - public boolean isChecked() { - return mIsVoNrEnabled; - } - - @VisibleForTesting - protected boolean isCallStateIdle() { - return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE); - } - - private boolean isUserControlAllowed() { - return isCallStateIdle(); - } - - private void updateVoNrState() { - ThreadUtils.postOnBackgroundThread(() -> { - boolean result = mTelephonyManager.isVoNrEnabled(); - if (result != mIsVoNrEnabled) { - Log.i(TAG, "VoNr state : " + result); - mIsVoNrEnabled = result; - mHandler.post(() -> { - updateState(mPreference); - }); - } - }); - } - - private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements - TelephonyCallback.CallStateListener { - - private TelephonyManager mLocalTelephonyManager; - - @Override - public void onCallStateChanged(int state) { - mCallState = state; - updateState(mPreference); - } - - public void register(TelephonyManager telephonyManager) { - mLocalTelephonyManager = telephonyManager; - - // assign current call state so that it helps to show correct preference state even - // before first onCallStateChanged() by initial registration. - if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { - try { - mCallState = mLocalTelephonyManager.getCallState(); - } catch (UnsupportedOperationException e) { - // Device doesn't support FEATURE_TELEPHONY_CALLING - mCallState = TelephonyManager.CALL_STATE_IDLE; - } - } else { - mCallState = mLocalTelephonyManager.getCallState(); - } - mLocalTelephonyManager.registerTelephonyCallback( - mContext.getMainExecutor(), mTelephonyCallback); - } - - public void unregister() { - mCallState = null; - if (mLocalTelephonyManager != null) { - mLocalTelephonyManager.unregisterTelephonyCallback(this); - } - } - } -} diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt new file mode 100644 index 00000000000..cf47c1f0cd3 --- /dev/null +++ b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt @@ -0,0 +1,76 @@ +/* + * 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.SubscriptionManager +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.settings.R +import com.android.settings.spa.preference.ComposePreferenceController +import com.android.settingslib.spa.widget.preference.SwitchPreference +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch + +/** + * Preference controller for "Voice over NR". + */ +class NrAdvancedCallingPreferenceController @JvmOverloads constructor( + context: Context, + key: String, + private val callStateRepository : CallStateRepository = CallStateRepository(context), +) : ComposePreferenceController(context, key) { + private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID + private var repository: VoNrRepository? = null + + /** Initial this PreferenceController. */ + @JvmOverloads + fun init(subId: Int, repository: VoNrRepository = VoNrRepository(mContext, subId)) { + this.subId = subId + this.repository = repository + } + + override fun getAvailabilityStatus() = + if (repository?.isVoNrAvailable() == true) AVAILABLE else CONDITIONALLY_UNAVAILABLE + + @Composable + override fun Content() { + val summary = stringResource(R.string.nr_advanced_calling_summary) + val isInCall by remember { callStateRepository.isInCallFlow() } + .collectAsStateWithLifecycle(initialValue = false) + val isEnabled by remember { + repository?.isVoNrEnabledFlow() ?: flowOf(false) + }.collectAsStateWithLifecycle(initialValue = false) + val coroutineScope = rememberCoroutineScope() + SwitchPreference(object : SwitchPreferenceModel { + override val title = stringResource(R.string.nr_advanced_calling_title) + override val summary = { summary } + override val changeable = { !isInCall } + override val checked = { isEnabled } + override val onCheckedChange: (Boolean) -> Unit = { newChecked -> + coroutineScope.launch { + repository?.setVoNrEnabled(newChecked) + } + } + }) + } +} diff --git a/src/com/android/settings/network/telephony/VoNrRepository.kt b/src/com/android/settings/network/telephony/VoNrRepository.kt new file mode 100644 index 00000000000..35af2844609 --- /dev/null +++ b/src/com/android/settings/network/telephony/VoNrRepository.kt @@ -0,0 +1,72 @@ +/* + * 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 android.telephony.TelephonyManager +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.withContext + +class VoNrRepository(private val context: Context, private val subId: Int) { + private val telephonyManager = context.telephonyManager(subId) + private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! + + fun isVoNrAvailable(): Boolean { + if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability()) return false + val carrierConfig = carrierConfigManager.safeGetConfig( + keys = listOf( + CarrierConfigManager.KEY_VONR_ENABLED_BOOL, + CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, + CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, + ), + subId = subId, + ) + return carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL) && + carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL) && + (carrierConfig.getIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY) + ?.isNotEmpty() ?: false) + } + + private fun has5gCapability() = + ((telephonyManager.supportedRadioAccessFamily and + TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0) + .also { Log.d(TAG, "[$subId] has5gCapability: $it") } + + fun isVoNrEnabledFlow(): Flow = context.subscriptionsChangedFlow() + .map { telephonyManager.isVoNrEnabled } + .conflate() + .onEach { Log.d(TAG, "[$subId] isVoNrEnabled: $it") } + .flowOn(Dispatchers.Default) + + suspend fun setVoNrEnabled(enabled: Boolean) = withContext(Dispatchers.Default) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return@withContext + val result = telephonyManager.setVoNrEnabled(enabled) + Log.d(TAG, "[$subId] setVoNrEnabled: $enabled, result: $result") + } + + private companion object { + private const val TAG = "VoNrRepository" + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.kt new file mode 100644 index 00000000000..418a00b057f --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.kt @@ -0,0 +1,135 @@ +/* + * 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 androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled +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.compose.ui.test.onRoot +import androidx.compose.ui.test.performClick +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.R +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +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.spy +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class NrAdvancedCallingPreferenceControllerTest { + @get:Rule + val composeTestRule = createComposeRule() + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) {} + + private val callStateRepository = mock { + on { isInCallFlow() } doReturn flowOf(false) + } + + private val voNrRepository = mock() + + private val controller = NrAdvancedCallingPreferenceController( + context = context, + key = TEST_KEY, + callStateRepository = callStateRepository, + ).apply { init(SUB_ID, voNrRepository) } + + @Test + fun isChecked_voNrEnabled_on() { + voNrRepository.stub { + on { isVoNrEnabledFlow() } doReturn flowOf(true) + } + + composeTestRule.setContent { + controller.Content() + } + + composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title)) + .assertIsOn() + } + + @Test + fun isChecked_voNrDisabled_off() { + voNrRepository.stub { + on { isVoNrEnabledFlow() } doReturn flowOf(false) + } + + composeTestRule.setContent { + controller.Content() + } + + composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title)) + .assertIsOff() + } + + @Test + fun isEnabled_notInCall_enabled() { + callStateRepository.stub { + on { isInCallFlow() } doReturn flowOf(false) + } + + composeTestRule.setContent { + controller.Content() + } + + composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title)) + .assertIsEnabled() + } + + @Test + fun isEnabled_inCall_notEnabled() { + callStateRepository.stub { + on { isInCallFlow() } doReturn flowOf(true) + } + + composeTestRule.setContent { + controller.Content() + } + + composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title)) + .assertIsNotEnabled() + } + + @Test + fun onClick_setVoNrEnabled(): Unit = runBlocking { + voNrRepository.stub { + on { isVoNrEnabledFlow() } doReturn flowOf(false) + } + + composeTestRule.setContent { + controller.Content() + } + composeTestRule.onRoot().performClick() + + verify(voNrRepository).setVoNrEnabled(true) + } + + private companion object { + const val TEST_KEY = "test_key" + const val SUB_ID = 2 + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/VoNrRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/VoNrRepositoryTest.kt new file mode 100644 index 00000000000..9c20afe5e85 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/VoNrRepositoryTest.kt @@ -0,0 +1,169 @@ +/* + * 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.TelephonyManager +import androidx.core.os.persistableBundleOf +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.runBlocking +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 +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class VoNrRepositoryTest { + + private val mockTelephonyManager = mock { + on { createForSubscriptionId(SUB_ID) } doReturn mock + on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR + } + + private val carrierConfig = persistableBundleOf( + CarrierConfigManager.KEY_VONR_ENABLED_BOOL to true, + CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL to true, + CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY to intArrayOf(1, 2), + ) + + private val mockCarrierConfigManager = mock { + on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager + on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager + } + + private val repository = VoNrRepository(context, SUB_ID) + + @Test + fun isVoNrAvailable_visibleDisable_returnFalse() { + carrierConfig.apply { + putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false) + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isFalse() + } + + @Test + fun isVoNrAvailable_voNrDisabled_returnFalse() { + carrierConfig.apply { + putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, false) + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isFalse() + } + + @Test + fun isVoNrAvailable_allEnabled_returnTrue() { + mockTelephonyManager.stub { + on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR + } + carrierConfig.apply { + putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, true) + putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, true) + putIntArray( + CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, + intArrayOf(1, 2), + ) + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isTrue() + } + + @Test + fun isVoNrAvailable_deviceNoNr_returnFalse() { + mockTelephonyManager.stub { + on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_LTE + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isFalse() + } + + @Test + fun isVoNrAvailable_carrierNoNr_returnFalse() { + carrierConfig.apply { + putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, intArrayOf()) + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isFalse() + } + + @Test + fun isVoNrAvailable_carrierConfigNrIsNull_returnFalse() { + carrierConfig.apply { + putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, null) + } + + val available = repository.isVoNrAvailable() + + assertThat(available).isFalse() + } + + @Test + fun isVoNrEnabledFlow_voNrDisabled() = runBlocking { + mockTelephonyManager.stub { + on { isVoNrEnabled } doReturn false + } + + val isVoNrEnabled = repository.isVoNrEnabledFlow().firstWithTimeoutOrNull() + + assertThat(isVoNrEnabled).isFalse() + } + + @Test + fun isVoNrEnabledFlow_voNrEnabled() = runBlocking { + mockTelephonyManager.stub { + on { isVoNrEnabled } doReturn true + } + + val isVoNrEnabled = repository.isVoNrEnabledFlow().firstWithTimeoutOrNull() + + assertThat(isVoNrEnabled).isTrue() + } + + @Test + fun setVoNrEnabled(): Unit = runBlocking { + repository.setVoNrEnabled(true) + + verify(mockTelephonyManager).setVoNrEnabled(true) + } + + private companion object { + const val SUB_ID = 1 + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java deleted file mode 100644 index e4c486f6487..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2021 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.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; - -import androidx.preference.TwoStatePreference; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.core.BasePreferenceController; -import com.android.settings.network.CarrierConfigCache; -import com.android.settingslib.RestrictedSwitchPreference; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class NrAdvancedCallingPreferenceControllerTest { - private static final int SUB_ID = 2; - - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private TelephonyManager mInvalidTelephonyManager; - @Mock - private SubscriptionManager mSubscriptionManager; - @Mock - private CarrierConfigCache mCarrierConfigCache; - - private NrAdvancedCallingPreferenceController mController; - private TwoStatePreference mPreference; - private PersistableBundle mCarrierConfig; - private Context mContext; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); - when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); - CarrierConfigCache.setTestInstance(mContext, mCarrierConfigCache); - - doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID); - doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId( - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - doReturn(TelephonyManager.NETWORK_TYPE_BITMASK_NR).when( - mTelephonyManager).getSupportedRadioAccessFamily(); - doReturn(false).when(mTelephonyManager).isVoNrEnabled(); - doReturn(TelephonyManager.ENABLE_VONR_REQUEST_NOT_SUPPORTED).when( - mTelephonyManager).setVoNrEnabled(anyBoolean()); - mCarrierConfig = new PersistableBundle(); - doReturn(mCarrierConfig).when(mCarrierConfigCache).getConfigForSubId(SUB_ID); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, false); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, true); - mCarrierConfig.putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, - new int[]{1, 2}); - - mPreference = new RestrictedSwitchPreference(mContext); - mController = spy(new NrAdvancedCallingPreferenceController(mContext, "VoNr")); - mController.init(SUB_ID); - doReturn(true).when(mController).isCallStateIdle(); - mPreference.setKey(mController.getPreferenceKey()); - } - - @Test - public void getAvailabilityStatus_vonrEnabledAndVisibleDisable_returnUnavailable() { - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_vonrDisabledAndVisibleDisable_returnUnavailable() { - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, false); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_vonrDisabledAndVisibleEnable_returnUnavailable() { - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, false); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, true); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_vonrEnabledAndVisibleEnable_returnAvailable() { - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, true); - mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, true); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.AVAILABLE); - } - - @Test - public void getAvailabilityStatus_deviceNoNr_returnUnavailable() { - doReturn(TelephonyManager.NETWORK_TYPE_BITMASK_LTE).when( - mTelephonyManager).getSupportedRadioAccessFamily(); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_carrierNoNr_returnUnavailable() { - mCarrierConfig.putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, - new int[0]); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void getAvailabilityStatus_carrierConfigNrIsNull_returnUnavailable() { - mCarrierConfig.putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, - null); - - mController.init(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo( - BasePreferenceController.CONDITIONALLY_UNAVAILABLE); - } - - @Test - public void updateState_callStateNotIdle_prefDisabled() { - doReturn(false).when(mController).isCallStateIdle(); - mPreference.setEnabled(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - @Ignore("b/339542743") - public void updateState_configOn_prefChecked() { - doReturn(TelephonyManager.ENABLE_VONR_SUCCESS).when( - mTelephonyManager).setVoNrEnabled(anyBoolean()); - doReturn(true).when(mTelephonyManager).isVoNrEnabled(); - mPreference.setChecked(false); - - mController.init(SUB_ID); - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } -}