Merge "Create VoNrRepository" into main

This commit is contained in:
Chaohui Wang
2024-05-17 02:21:31 +00:00
committed by Android (Google) Code Review
7 changed files with 453 additions and 435 deletions

View File

@@ -295,11 +295,9 @@
settings:controller=
"com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/>
<SwitchPreferenceCompat
<com.android.settings.spa.preference.ComposePreference
android:key="nr_advanced_calling"
android:title="@string/nr_advanced_calling_title"
android:persistent="false"
android:summary="@string/nr_advanced_calling_summary"
settings:keywords="@string/keywords_nr_advanced_calling"
settings:controller="com.android.settings.network.telephony.NrAdvancedCallingPreferenceController"/>
</PreferenceCategory>

View File

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

View File

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

View File

@@ -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<Boolean> = 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"
}
}

View File

@@ -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<CallStateRepository> {
on { isInCallFlow() } doReturn flowOf(false)
}
private val voNrRepository = mock<VoNrRepository>()
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
}
}

View File

@@ -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<TelephonyManager> {
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<CarrierConfigManager> {
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
}
}

View File

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