[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: febb11ca24 -s ours am: 21de308b62 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telephony/+/20148386
Change-Id: I79bd3d84a82d9910de074a1c979cb527d1ae2aa9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8218a84..d0f427c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -207,8 +207,6 @@
<style name="DialerAlertDialogTheme"
parent="@android:style/Theme.Material.Light.Dialog">
<item name="android:forceDarkAllowed">true</item>
- <item name="android:colorAccent">@color/dialer_theme_color</item>
- <item name="android:textColor">?android:attr/textColorPrimaryInverseDisableOnly</item>
</style>
<style name="Empty" parent="@android:style/Theme.Material.Light">
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index f2641a1..226664d 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.READ_PHONE_STATE;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions;
import android.app.Notification;
@@ -47,6 +48,7 @@
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -59,6 +61,7 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyCapabilities;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.phone.settings.VoicemailSettingsActivity;
@@ -895,15 +898,21 @@
Log.i(LOG_TAG, msg);
}
- /**
- * In case network selection notification shows up repeatedly under
- * unstable network condition. The logic is to check whether or not
- * the service state keeps in no service condition for at least
- * {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}.
- * And checking {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times.
- * To avoid the notification showing up for the momentary state.
- */
private void shouldShowNotification(int serviceState, int subId) {
+ // "Network selection unavailable" notification should only show when network selection is
+ // visible to the end user. Some CC items e.g. KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL
+ // can be overridden to hide the network selection to the end user. In this case, the
+ // notification is not shown to avoid confusion to the end user.
+ if (!shouldDisplayNetworkSelectOptions(subId)) {
+ logi("Skipping network selection unavailable notification due to carrier policy.");
+ return;
+ }
+
+ // In unstable network condition, the phone may go in and out of service. Add logic here to
+ // debounce the network selection notification. The notification only shows after phone is
+ // out of service, AND fulfills one of the two conditions below:
+ // - Out of service lasts {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIME_IN_MS}
+ // - Or has checked {@link #NETWORK_SELECTION_NOTIFICATION_MAX_PENDING_TIMES} times
if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
if (mPreviousServiceState.get(subId, STATE_UNKNOWN_SERVICE)
!= ServiceState.STATE_OUT_OF_SERVICE) {
@@ -955,4 +964,111 @@
private static long getTimeStamp() {
return SystemClock.elapsedRealtime();
}
+
+ // TODO(b/243010310): merge methods below with Settings#MobileNetworkUtils and optimize them.
+ // The methods below are copied from com.android.settings.network.telephony.MobileNetworkUtils
+ // to make sure the network selection unavailable notification should not show when network
+ // selection menu is not visible to the end user in Settings app.
+ private boolean shouldDisplayNetworkSelectOptions(int subId) {
+ final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+ final CarrierConfigManager carrierConfigManager = mContext.getSystemService(
+ CarrierConfigManager.class);
+ final PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
+
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ || carrierConfig == null
+ || !carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
+ || carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
+ || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
+ && !telephonyManager.isManualNetworkSelectionAllowed())) {
+ return false;
+ }
+
+ if (isWorldMode(carrierConfig)) {
+ final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
+ (int) telephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
+ if (networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO) {
+ return false;
+ }
+ if (shouldSpeciallyUpdateGsmCdma(telephonyManager, carrierConfig)) {
+ return false;
+ }
+ if (networkMode == RILConstants.NETWORK_MODE_LTE_GSM_WCDMA) {
+ return true;
+ }
+ }
+
+ return isGsmBasicOptions(telephonyManager, carrierConfig);
+ }
+
+ private static boolean isWorldMode(@NonNull PersistableBundle carrierConfig) {
+ return carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
+ }
+
+ private static boolean shouldSpeciallyUpdateGsmCdma(@NonNull TelephonyManager telephonyManager,
+ @NonNull PersistableBundle carrierConfig) {
+ if (!isWorldMode(carrierConfig)) {
+ return false;
+ }
+
+ final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
+ (int) telephonyManager.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
+ if (networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM
+ || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
+ || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA
+ || networkMode == RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA
+ || networkMode
+ == RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+ || networkMode == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
+ if (!isTdscdmaSupported(telephonyManager, carrierConfig)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isTdscdmaSupported(@NonNull TelephonyManager telephonyManager,
+ @NonNull PersistableBundle carrierConfig) {
+ if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
+ return true;
+ }
+ final String[] numericArray = carrierConfig.getStringArray(
+ CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
+ if (numericArray == null) {
+ return false;
+ }
+ final ServiceState serviceState = telephonyManager.getServiceState();
+ final String operatorNumeric =
+ (serviceState != null) ? serviceState.getOperatorNumeric() : null;
+ if (operatorNumeric == null) {
+ return false;
+ }
+ for (String numeric : numericArray) {
+ if (operatorNumeric.equals(numeric)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isGsmBasicOptions(@NonNull TelephonyManager telephonyManager,
+ @NonNull PersistableBundle carrierConfig) {
+ if (!carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
+ && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
+ return true;
+ }
+
+ if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+ return true;
+ }
+
+ return false;
+ }
+ // END of TODO:(b/243010310): merge methods above with Settings#MobileNetworkUtils and optimize.
}
diff --git a/src/com/android/phone/PhoneDisplayMessage.java b/src/com/android/phone/PhoneDisplayMessage.java
index be7fc7f..c487cba 100644
--- a/src/com/android/phone/PhoneDisplayMessage.java
+++ b/src/com/android/phone/PhoneDisplayMessage.java
@@ -80,7 +80,8 @@
sDisplayMessageDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
sDisplayMessageDialog.show();
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 99e73ff..f6ec75d 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -549,15 +549,15 @@
mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
mTelephonyCallbacks = new PhoneAppCallback[tm.getSupportedModemCount()];
-
- for (Phone phone : PhoneFactory.getPhones()) {
- int subId = phone.getSubId();
- PhoneAppCallback callback = new PhoneAppCallback(subId);
- tm.createForSubscriptionId(subId).registerTelephonyCallback(
- TelephonyManager.INCLUDE_LOCATION_DATA_NONE, mHandler::post, callback);
- mTelephonyCallbacks[phone.getPhoneId()] = callback;
+ if (tm.getSupportedModemCount() > 0) {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ int subId = phone.getSubId();
+ PhoneAppCallback callback = new PhoneAppCallback(subId);
+ tm.createForSubscriptionId(subId).registerTelephonyCallback(
+ TelephonyManager.INCLUDE_LOCATION_DATA_NONE, mHandler::post, callback);
+ mTelephonyCallbacks[phone.getPhoneId()] = callback;
+ }
}
-
mCarrierVvmPackageInstalledReceiver.register(this);
//set the default values for the preferences in the phone.
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 66cef64..4122dfe 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -8095,8 +8095,7 @@
}
String vvmPackage = componentName.getPackageName();
if (!callingPackage.equals(vvmPackage)) {
- throw new SecurityException("Caller not current active visual voicemail package["
- + vvmPackage + "]");
+ throw new SecurityException("Caller not current active visual voicemail package");
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 695a4a4..d0aad4a 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -469,7 +469,7 @@
// build the dialog
final AlertDialog newDialog =
- FrameworksUtils.makeAlertDialogBuilder(contextThemeWrapper)
+ new AlertDialog.Builder(contextThemeWrapper)
.setMessage(text)
.setView(dialogView)
.setPositiveButton(R.string.send_button, mUSSDDialogListener)
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9321e1e..587ac43 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -25,6 +25,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsReasonInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallFailCause;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
@@ -101,14 +102,29 @@
public static DisconnectCause toTelecomDisconnectCause(
int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason,
int phoneId, ImsReasonInfo imsReasonInfo) {
+ return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause,
+ reason, phoneId, imsReasonInfo, getCarrierConfigBundle(phoneId));
+ }
+
+ /**
+ * Final pre-processing method in creating a DisconnectCause. This method should NOT be called
+ * from another class directly. It only has private-package visibility for testing.
+ *
+ * @param carrierConfig
+ */
+ @VisibleForTesting
+ static DisconnectCause toTelecomDisconnectCause(
+ int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason,
+ int phoneId, ImsReasonInfo imsReasonInfo, PersistableBundle carrierConfig) {
Context context = PhoneGlobals.getInstance();
+
return new DisconnectCause(
- toTelecomDisconnectCauseCode(telephonyDisconnectCause),
+ toTelecomDisconnectCauseCode(telephonyDisconnectCause, carrierConfig),
toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
- telephonyPreciseDisconnectCause),
+ telephonyPreciseDisconnectCause, carrierConfig),
toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
- toTelecomDisconnectReason(context,telephonyDisconnectCause, reason, phoneId),
- toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId),
+ toTelecomDisconnectReason(context, telephonyDisconnectCause, reason, phoneId),
+ toTelecomDisconnectCauseTone(telephonyDisconnectCause, carrierConfig),
telephonyDisconnectCause,
telephonyPreciseDisconnectCause,
imsReasonInfo);
@@ -119,7 +135,16 @@
* {@link android.telecom.DisconnectCause} disconnect code.
* @return The disconnect code as defined in {@link android.telecom.DisconnectCause}.
*/
- private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) {
+ private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause,
+ PersistableBundle carrierConfig) {
+
+ // special case: some carriers determine what disconnect causes play the BUSY tone.
+ // hence, must adjust the disconnectCause CODE to match the tone.
+ if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause,
+ carrierConfig)) {
+ return DisconnectCause.BUSY;
+ }
+
switch (telephonyDisconnectCause) {
case android.telephony.DisconnectCause.LOCAL:
// The call was still disconnected locally, so this is not an error condition.
@@ -237,8 +262,17 @@
* Returns a label for to the disconnect cause to be shown to the user.
*/
private static CharSequence toTelecomDisconnectCauseLabel(
- Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause) {
+ Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause,
+ PersistableBundle carrierConfig) {
CharSequence label;
+
+ // special case: some carriers determine what disconnect causes play the BUSY tone.
+ // hence, must adjust the disconnectCause LABEL to match the tone.
+ if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause,
+ carrierConfig)) {
+ return context.getResources().getString(R.string.callFailed_userBusy);
+ }
+
if (telephonyPreciseDisconnectCause != CallFailCause.NOT_VALID) {
label = getLabelFromPreciseDisconnectCause(context, telephonyPreciseDisconnectCause,
telephonyDisconnectCause);
@@ -695,6 +729,10 @@
resourceId = R.string.incall_error_emergency_only;
break;
+ case android.telephony.DisconnectCause.ICC_ERROR:
+ resourceId = R.string.callFailed_simError;
+ break;
+
case android.telephony.DisconnectCause.OUT_OF_SERVICE:
// No network connection.
if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
@@ -840,21 +878,15 @@
/**
* Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played.
*/
- private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause, int phoneId) {
- Phone phone = PhoneFactory.getPhone(phoneId);
- PersistableBundle config;
- if (phone != null) {
- config = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
- } else {
- config = PhoneGlobals.getInstance().getCarrierConfig();
+ private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause,
+ PersistableBundle carrierConfig) {
+
+ // special case: some carriers determine what disconnect causes play the BUSY tone.
+ if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause,
+ carrierConfig)) {
+ return ToneGenerator.TONE_SUP_BUSY;
}
- int[] busyToneArray = config.getIntArray(
- CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY);
- for (int busyTone : busyToneArray) {
- if (busyTone == telephonyDisconnectCause) {
- return ToneGenerator.TONE_SUP_BUSY;
- }
- }
+
switch (telephonyDisconnectCause) {
case android.telephony.DisconnectCause.CONGESTION:
return ToneGenerator.TONE_SUP_CONGESTION;
@@ -886,4 +918,37 @@
return ToneGenerator.TONE_PROP_PROMPT;
}
}
+
+ /**
+ * Helper method that examines the carrierConfig KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY
+ * containing the DisconnectCauses that are classified as DisconnectCause.BUSY
+ * @param telephonyDisconnectCause
+ * @param carrierConfig object that holds all the carrier specific settings
+ * @return whether the cause is in the carrier config busy tone array
+ */
+ private static boolean doesCarrierClassifyDisconnectCauseAsBusyCause(
+ int telephonyDisconnectCause, PersistableBundle carrierConfig) {
+ int[] busyToneArray = carrierConfig.getIntArray(
+ CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY);
+ for (int busyTone : busyToneArray) {
+ if (busyTone == telephonyDisconnectCause) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static PersistableBundle getCarrierConfigBundle(int phoneId) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ PersistableBundle config;
+
+ if (phone != null) {
+ config = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
+ } else {
+ config = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+
+ return config;
+ }
+
}
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
index 7f9efdc..28a7b02 100644
--- a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -17,19 +17,104 @@
package com.android.services.telephony;
import static android.media.ToneGenerator.TONE_PROP_PROMPT;
+import static android.media.ToneGenerator.TONE_SUP_BUSY;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.common.R;
+
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+
@RunWith(AndroidJUnit4.class)
-public class DisconnectCauseUtilTest {
+public class DisconnectCauseUtilTest extends TelephonyTestBase {
+
+ // constants
+ public static final int PHONE_ID = 123;
+ public static final String EMPTY_STRING = "";
+
+ // dynamic
+ private Context mContext;
+ private HashMap<InstanceKey, Object> mOldInstances = new HashMap<InstanceKey, Object>();
+ private ArrayList<InstanceKey> mInstanceKeys = new ArrayList<InstanceKey>();
+
+ //Mocks
+ @Mock
+ private GsmCdmaPhone mMockPhone;
+
+ // inner classes
+ private static class InstanceKey {
+ public final Class mClass;
+ public final String mInstName;
+ public final Object mObj;
+
+ InstanceKey(final Class c, final String instName, final Object obj) {
+ mClass = c;
+ mInstName = instName;
+ mObj = obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof InstanceKey)) {
+ return false;
+ }
+
+ InstanceKey other = (InstanceKey) obj;
+ return (other.mClass == mClass && other.mInstName.equals(mInstName)
+ && other.mObj == mObj);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ // objects that call static getInstance()
+ mMockPhone = Mockito.mock(GsmCdmaPhone.class);
+ mContext = InstrumentationRegistry.getTargetContext();
+ // set mocks
+ setSinglePhone();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // restoreInstance.
+ // Not doing so will potentially "confuse" other tests with the mocked instance
+ restoreInstance(PhoneFactory.class, "sPhones", null);
+ super.tearDown();
+ }
+
+
/**
* Verifies that a call drop due to loss of WIFI results in a disconnect cause of error and that
* the label, description and tone are all present.
@@ -43,4 +128,109 @@
assertNotNull(tcCause.getDescription());
assertNotNull(tcCause.getReason());
}
+
+ /**
+ * ensure the default behavior was not changed when a disconnect cause comes in as
+ * DisconnectCause.ERROR_UNSPECIFIED
+ */
+ @Test
+ public void testDefaultDisconnectCauseBehaviorForCauseNotInCarrierBusyToneArray() {
+ android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause(
+ DisconnectCause.ERROR_UNSPECIFIED, EMPTY_STRING, PHONE_ID);
+ // CODE
+ assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode());
+ // LABEL
+ safeAssertLabel(null, tcCause);
+ // TONE
+ assertEquals(TONE_PROP_PROMPT, tcCause.getTone());
+ }
+
+ /**
+ * Simulate a Carrier classifying the DisconnectCause.ERROR_UNSPECIFIED as a
+ * DisconnectCause.BUSY. The code, label, and tone should match DisconnectCause.BUSY.
+ */
+ @Test
+ public void testCarrierSetDisconnectCauseInBusyToneArray() {
+ int[] carrierBusyArr = {DisconnectCause.BUSY, DisconnectCause.ERROR_UNSPECIFIED};
+ PersistableBundle config = new PersistableBundle();
+
+ config.putIntArray(
+ CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+ carrierBusyArr);
+
+ android.telecom.DisconnectCause tcCause =
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ DisconnectCause.ERROR_UNSPECIFIED, -1,
+ EMPTY_STRING, PHONE_ID, null, config);
+
+ // CODE
+ assertEquals(android.telecom.DisconnectCause.BUSY, tcCause.getCode());
+ // LABEL
+ safeAssertLabel(R.string.callFailed_userBusy, tcCause);
+ // TONE
+ assertEquals(TONE_SUP_BUSY, tcCause.getTone());
+ }
+
+ private void setSinglePhone() throws Exception {
+ Phone[] mPhones = new Phone[]{mMockPhone};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ }
+
+
+ protected synchronized void replaceInstance(final Class c, final String instanceName,
+ final Object obj, final Object newValue)
+ throws Exception {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+
+ InstanceKey key = new InstanceKey(c, instanceName, obj);
+ if (!mOldInstances.containsKey(key)) {
+ mOldInstances.put(key, field.get(obj));
+ mInstanceKeys.add(key);
+ }
+ field.set(obj, newValue);
+ }
+
+ protected synchronized void restoreInstance(final Class c, final String instanceName,
+ final Object obj) throws Exception {
+ InstanceKey key = new InstanceKey(c, instanceName, obj);
+ if (mOldInstances.containsKey(key)) {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+ field.set(obj, mOldInstances.get(key));
+ mOldInstances.remove(key);
+ mInstanceKeys.remove(key);
+ }
+ }
+
+ private Resources getResourcesForLocale(Context context, Locale locale) {
+ Configuration config = new Configuration();
+ config.setToDefaults();
+ config.setLocale(locale);
+ Context localeContext = context.createConfigurationContext(config);
+ return localeContext.getResources();
+ }
+
+ private void safeAssertLabel(Integer resourceId,
+ android.telecom.DisconnectCause disconnectCause) {
+ Resources r = getResourcesForLocale(mContext, Locale.US);
+ if (resourceId == null || r == null) {
+ return;
+ }
+ String label = r.getString(resourceId);
+ assertEquals(label, disconnectCause.getLabel());
+ }
+
+ /**
+ * Verifies that an ICC_ERROR disconnect cause generates a message which mentions there is no
+ * SIM.
+ */
+ @Test
+ public void testIccError() {
+ android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause(
+ DisconnectCause.ICC_ERROR);
+ assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode());
+ assertNotNull(tcCause.getLabel());
+ assertNotNull(tcCause.getDescription());
+ }
}