[VoNR] VoNR settings

Allow user to enable or disable voice over NR.

Bug: 191203577
Test: atest
com.android.settings.network.telephony.NrAdvancedCallingPreferenceControllerTest

Change-Id: I9be4716d0cdac5d698c2058be65bfa541495d012
This commit is contained in:
SongFerngWang
2021-09-01 19:18:08 +08:00
parent 1a5b010e20
commit eaa71ac6de
5 changed files with 382 additions and 2 deletions

View File

@@ -7950,9 +7950,13 @@
<!-- Enhaced 4G LTE Mode title for carriers who want to show 4G Calling. [CHAR LIMIT=50] --> <!-- Enhaced 4G LTE Mode title for carriers who want to show 4G Calling. [CHAR LIMIT=50] -->
<string name="enhanced_4g_lte_mode_title_4g_calling">4G Calling</string> <string name="enhanced_4g_lte_mode_title_4g_calling">4G Calling</string>
<!-- Enhaced 4G LTE Mode summary. [CHAR LIMIT=100] --> <!-- Enhaced 4G LTE Mode summary. [CHAR LIMIT=100] -->
<string name="enhanced_4g_lte_mode_summary">Use LTE services to improve voice and other communications (recommended)</string> <string name="enhanced_4g_lte_mode_summary">Use LTE services to improve voice calls (recommended)</string>
<!-- Enhaced 4G LTE Mode summary for 4g calling. [CHAR LIMIT=100] --> <!-- Enhaced 4G LTE Mode summary for 4g calling. [CHAR LIMIT=100] -->
<string name="enhanced_4g_lte_mode_summary_4g_calling">Use 4G services to improve voice and other communications (recommended)</string> <string name="enhanced_4g_lte_mode_summary_4g_calling">Use 4G services to improve voice calls (recommended)</string>
<!-- NR advanced calling(VoNR or Vo5G) title. [CHAR LIMIT=50] -->
<string name="nr_advanced_calling_title">Vo5G</string>
<!-- NR advanced calling(VoNR or Vo5G) summary. [CHAR LIMIT=NONE] -->
<string name="nr_advanced_calling_summary">Use 5G for voice calls</string>
<!-- Title of a preference determining whether or not the user has allowed the device to share <!-- Title of a preference determining whether or not the user has allowed the device to share
their contacts' phone numbers with their carrier in order to implement contact discovery, their contacts' phone numbers with their carrier in order to implement contact discovery,
which is a service that exchanges contacts with the carrier to determine if your which is a service that exchanges contacts with the carrier to determine if your
@@ -8347,6 +8351,9 @@
<!-- List of synonyms for the enhance 4G LTE titles, used to match in settings search [CHAR LIMIT=NONE] --> <!-- List of synonyms for the enhance 4G LTE titles, used to match in settings search [CHAR LIMIT=NONE] -->
<string name="keywords_enhance_4g_lte">volte, advanced calling, 4g calling</string> <string name="keywords_enhance_4g_lte">volte, advanced calling, 4g calling</string>
<!-- List of synonyms for the NR advanced calling(VoNR or Vo5G) titles, used to match in settings search [CHAR LIMIT=NONE] -->
<string name="keywords_nr_advanced_calling">vo5g, vonr, advanced calling, 5g calling</string>
<!-- List of synonyms for add language, used to match in settings search [CHAR LIMIT=NONE] --> <!-- List of synonyms for add language, used to match in settings search [CHAR LIMIT=NONE] -->
<string name="keywords_add_language">add language, add a language</string> <string name="keywords_add_language">add language, add a language</string>

View File

@@ -247,6 +247,13 @@
android:summary="@string/enable_2g_summary" android:summary="@string/enable_2g_summary"
settings:controller="com.android.settings.network.telephony.Enable2gPreferenceController" /> settings:controller="com.android.settings.network.telephony.Enable2gPreferenceController" />
<SwitchPreference
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> </PreferenceCategory>
<Preference <Preference

View File

@@ -193,6 +193,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
.addListener(videoCallingPreferenceController); .addListener(videoCallingPreferenceController);
use(ContactDiscoveryPreferenceController.class).init(getParentFragmentManager(), mSubId, use(ContactDiscoveryPreferenceController.class).init(getParentFragmentManager(), mSubId,
getLifecycle()); getLifecycle());
use(NrAdvancedCallingPreferenceController.class).init(mSubId);
} }
@Override @Override

View File

@@ -0,0 +1,197 @@
/*
* 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.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.SwitchPreference;
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;
/**
* Preference controller for "Enhanced 4G LTE"
*/
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 mIsVonrVisibleFromCarrierConfig = false;
private boolean mIsNrEnableFromCarrierConfig = false;
private boolean mHas5gCapability = false;
private Integer mCallState;
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;
}
mIsVonrVisibleFromCarrierConfig = carrierConfig.getBoolean(
CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL);
int[] nrAvailabilities = carrierConfig.getIntArray(
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
mIsNrEnableFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
Log.d(TAG, "mHas5gCapability: " + mHas5gCapability
+ ",mIsNrEnabledFromCarrierConfig: " + mIsNrEnableFromCarrierConfig
+ ",mIsVonrVisibleFromCarrierConfig: " + mIsVonrVisibleFromCarrierConfig);
return this;
}
@Override
public int getAvailabilityStatus(int subId) {
init(subId);
if (mHas5gCapability && mIsNrEnableFromCarrierConfig && 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 SwitchPreference switchPreference = (SwitchPreference) 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 mTelephonyManager.isVoNrEnabled();
}
@VisibleForTesting
protected boolean isCallStateIdle() {
return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE);
}
private boolean isUserControlAllowed() {
return isCallStateIdle();
}
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.
mCallState = mLocalTelephonyManager.getCallState();
mLocalTelephonyManager.registerTelephonyCallback(
mContext.getMainExecutor(), mTelephonyCallback);
}
public void unregister() {
mCallState = null;
if (mLocalTelephonyManager != null) {
mLocalTelephonyManager.unregisterTelephonyCallback(this);
}
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* 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.SwitchPreference;
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 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 NrAdvancedCallingPreferenceControllerTest {
private static final int SUB_ID = 2;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private TelephonyManager mInvalidTelephonyManager;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private CarrierConfigManager mCarrierConfigManager;
private NrAdvancedCallingPreferenceController mController;
private SwitchPreference 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);
when(mContext.getSystemService(CarrierConfigManager.class))
.thenReturn(mCarrierConfigManager);
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(mCarrierConfigManager).getConfigForSubId(SUB_ID);
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false);
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_vonrDisabled_returnUnavailable() {
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false);
mController.init(SUB_ID);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_vonrEnabled_returnAvailable() {
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
public void updateState_configOn_prefChecked() {
doReturn(TelephonyManager.ENABLE_VONR_SUCCESS).when(
mTelephonyManager).setVoNrEnabled(anyBoolean());
doReturn(true).when(mTelephonyManager).isVoNrEnabled();
mPreference.setChecked(false);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
}
}