1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.services.telephony; 18 19 import static android.media.ToneGenerator.TONE_PROP_PROMPT; 20 import static android.media.ToneGenerator.TONE_SUP_BUSY; 21 22 import static junit.framework.Assert.assertNotNull; 23 import static junit.framework.TestCase.assertEquals; 24 25 import static org.testng.Assert.assertFalse; 26 import static org.testng.Assert.assertTrue; 27 28 import android.content.Context; 29 import android.content.res.Configuration; 30 import android.content.res.Resources; 31 import android.os.PersistableBundle; 32 import android.telephony.CarrierConfigManager; 33 import android.telephony.DisconnectCause; 34 35 import androidx.test.InstrumentationRegistry; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.TelephonyTestBase; 39 import com.android.internal.telephony.CallFailCause; 40 import com.android.phone.R; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.Locale; 47 48 @RunWith(AndroidJUnit4.class) 49 public class DisconnectCauseUtilTest extends TelephonyTestBase { 50 51 // constants 52 public static final int PHONE_ID = 123; 53 public static final String EMPTY_STRING = ""; 54 55 private final FlagsAdapter mFeatureFlags = new FlagsAdapter(){ 56 @Override 57 public boolean doNotOverridePreciseLabel() { 58 return true; 59 } 60 }; 61 62 @Before setUp()63 public void setUp() throws Exception { 64 super.setUp(); 65 } 66 67 /** 68 * Verifies that a call drop due to loss of WIFI results in a disconnect cause of error and that 69 * the label, description and tone are all present. 70 */ 71 @Test testDropDueToWifiLoss()72 public void testDropDueToWifiLoss() { 73 android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause( 74 DisconnectCause.WIFI_LOST); 75 assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode()); 76 assertEquals(TONE_PROP_PROMPT, tcCause.getTone()); 77 assertNotNull(tcCause.getDescription()); 78 assertNotNull(tcCause.getReason()); 79 } 80 81 /** 82 * ensure the default behavior was not changed when a disconnect cause comes in as 83 * DisconnectCause.ERROR_UNSPECIFIED 84 */ 85 @Test testDefaultDisconnectCauseBehaviorForCauseNotInCarrierBusyToneArray()86 public void testDefaultDisconnectCauseBehaviorForCauseNotInCarrierBusyToneArray() { 87 android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause( 88 DisconnectCause.ERROR_UNSPECIFIED, -1, EMPTY_STRING, PHONE_ID, null, mFeatureFlags); 89 // CODE 90 assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode()); 91 // LABEL 92 safeAssertLabel(null, tcCause); 93 // TONE 94 assertEquals(TONE_PROP_PROMPT, tcCause.getTone()); 95 } 96 97 /** 98 * verify that if a precise label is given Telephony, the label is not overridden by Telecom 99 */ 100 @Test testDefaultPhoneConfig_NoPreciseLabelGiven()101 public void testDefaultPhoneConfig_NoPreciseLabelGiven() { 102 android.telecom.DisconnectCause tcCause = 103 DisconnectCauseUtil.toTelecomDisconnectCause(DisconnectCause.BUSY, 104 -1 /* precise label is NOT given */, 105 EMPTY_STRING, PHONE_ID, null /* carrier config is NOT set */, 106 mFeatureFlags); 107 assertBusyCauseWithTargetLabel(R.string.callFailed_userBusy, tcCause); 108 } 109 110 /** 111 * verify that if a precise label is given Telephony, the label is not overridden by Telecom 112 */ 113 @Test testDefaultPhoneConfig_PreciseLabelProvided()114 public void testDefaultPhoneConfig_PreciseLabelProvided() { 115 android.telecom.DisconnectCause tcCause = 116 DisconnectCauseUtil.toTelecomDisconnectCause(DisconnectCause.BUSY, 117 CallFailCause.USER_BUSY /* Telephony defined a precise label */, 118 EMPTY_STRING, PHONE_ID, null /* carrier config is NOT set */, 119 mFeatureFlags); 120 // Note: The precise label should not be overridden even though the carrier defined 121 // the cause to play a busy tone 122 assertBusyCauseWithTargetLabel(R.string.clh_callFailed_user_busy_txt, tcCause); 123 } 124 125 /** 126 * special case: The Carrier has re-defined a disconnect code that should play a busy tone. 127 * Thus, the code, label, and tone should be remapped. 128 * <p> 129 * <p> 130 * Verify that if the disconnect cause is in the carrier busy tone array that the expected 131 * label, tone, and code are returned. 132 */ 133 @Test testCarrierSetBusyToneArray_NoPreciseLabelGiven()134 public void testCarrierSetBusyToneArray_NoPreciseLabelGiven() { 135 android.telecom.DisconnectCause tcCause = 136 DisconnectCauseUtil.toTelecomDisconnectCause( 137 DisconnectCause.BUSY, -1 /* precise label is NOT given */, 138 EMPTY_STRING, PHONE_ID, null, getBundleWithBusyToneArray(), mFeatureFlags, 139 false); 140 141 assertBusyCauseWithTargetLabel(R.string.callFailed_userBusy, tcCause); 142 } 143 144 /** 145 * special case: The Carrier has re-defined a disconnect code that should play a busy tone. 146 * Thus, the code, label, and tone should be remapped. 147 * <p> 148 * <p> 149 * Verify that if the disconnect cause is in the carrier busy tone array and the Telephony 150 * stack has provided a precise label, the label is not overridden. 151 */ 152 @Test testCarrierSetBusyToneArray_PreciseLabelProvided()153 public void testCarrierSetBusyToneArray_PreciseLabelProvided() { 154 android.telecom.DisconnectCause tcCause = 155 DisconnectCauseUtil.toTelecomDisconnectCause(DisconnectCause.BUSY, 156 CallFailCause.USER_BUSY /* Telephony defined a precise label */, 157 EMPTY_STRING, PHONE_ID, null, getBundleWithBusyToneArray(), mFeatureFlags, 158 false); 159 // Note: The precise label should not be overridden even though the carrier defined 160 // the cause to play a busy tone 161 assertBusyCauseWithTargetLabel(R.string.clh_callFailed_user_busy_txt, tcCause); 162 } 163 164 /** 165 * Ensure the helper doesCarrierClassifyDisconnectCauseAsBusyCause does not hit a NPE if a 166 * NULL carrier config is passed in. 167 */ 168 @Test testDoesCarrierClassifyDisconnectCauseAsBusyCause_nullConfig()169 public void testDoesCarrierClassifyDisconnectCauseAsBusyCause_nullConfig() { 170 assertFalse(DisconnectCauseUtil.doesCarrierClassifyDisconnectCauseAsBusyCause(-1, null)); 171 } 172 173 /** 174 * Ensure the helper doesCarrierClassifyDisconnectCauseAsBusyCause does not hit a NPE if an 175 * EMPTY carrier config is passed in. 176 */ 177 @Test testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigDoesNotDefineArray()178 public void testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigDoesNotDefineArray() { 179 PersistableBundle config = new PersistableBundle(); 180 assertFalse(DisconnectCauseUtil.doesCarrierClassifyDisconnectCauseAsBusyCause(-1, config)); 181 } 182 183 /** 184 * Ensure the helper doesCarrierClassifyDisconnectCauseAsBusyCause does not hit a NPE if an 185 * EMPTY array is defined for KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY. 186 */ 187 @Test testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasEmptyArray()188 public void testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasEmptyArray() { 189 PersistableBundle config = new PersistableBundle(); 190 int[] carrierBusyArr = {}; // NOTE: This is intentionally let empty 191 192 config.putIntArray( 193 CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, 194 carrierBusyArr); 195 196 assertFalse(DisconnectCauseUtil.doesCarrierClassifyDisconnectCauseAsBusyCause(-1, config)); 197 } 198 199 /** 200 * Ensure {@link DisconnectCauseUtil#doesCarrierClassifyDisconnectCauseAsBusyCause} returns 201 * FALSE is the passed in disconnect cause is NOT the busy tone array 202 */ 203 @Test testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasBusyToneButNotMatch()204 public void testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasBusyToneButNotMatch() { 205 assertFalse(DisconnectCauseUtil.doesCarrierClassifyDisconnectCauseAsBusyCause(-1, 206 getBundleWithBusyToneArray())); 207 } 208 209 /** 210 * Ensure {@link DisconnectCauseUtil#doesCarrierClassifyDisconnectCauseAsBusyCause} returns 211 * TRUE if the disconnect cause is defined in the busy tone array (by the Carrier) 212 */ 213 @Test testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasBusyTone()214 public void testDoesCarrierClassifyDisconnectCauseAsBusyCause_ConfigHasBusyTone() { 215 assertTrue(DisconnectCauseUtil.doesCarrierClassifyDisconnectCauseAsBusyCause( 216 DisconnectCause.BUSY, getBundleWithBusyToneArray())); 217 } 218 assertBusyCauseWithTargetLabel(Integer targetLabel, android.telecom.DisconnectCause disconnectCause)219 private void assertBusyCauseWithTargetLabel(Integer targetLabel, 220 android.telecom.DisconnectCause disconnectCause) { 221 // CODE: Describes the cause of a disconnected call 222 assertEquals(android.telecom.DisconnectCause.BUSY, disconnectCause.getCode()); 223 // LABEL: This is the label that the user sees 224 safeAssertLabel(targetLabel, disconnectCause); 225 // TONE: This is the DTMF tone being played to the user 226 assertEquals(TONE_SUP_BUSY, disconnectCause.getTone()); 227 } 228 getBundleWithBusyToneArray()229 private PersistableBundle getBundleWithBusyToneArray() { 230 int[] carrierBusyArr = {DisconnectCause.BUSY}; 231 PersistableBundle config = new PersistableBundle(); 232 233 config.putIntArray( 234 CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, 235 carrierBusyArr); 236 return config; 237 } 238 getResourcesForLocale(Context context, Locale locale)239 private Resources getResourcesForLocale(Context context, Locale locale) { 240 Configuration config = new Configuration(); 241 config.setToDefaults(); 242 config.setLocale(locale); 243 Context localeContext = context.createConfigurationContext(config); 244 return localeContext.getResources(); 245 } 246 safeAssertLabel(Integer resourceId, android.telecom.DisconnectCause disconnectCause)247 private void safeAssertLabel(Integer resourceId, 248 android.telecom.DisconnectCause disconnectCause) { 249 Resources r = getResourcesForLocale(InstrumentationRegistry.getTargetContext(), Locale.US); 250 if (resourceId == null || r == null) { 251 return; 252 } 253 String label = r.getString(resourceId); 254 assertEquals(label, disconnectCause.getLabel()); 255 } 256 257 /** 258 * Verifies that an ICC_ERROR disconnect cause generates a message which mentions there is no 259 * SIM. 260 */ 261 @Test testIccError()262 public void testIccError() { 263 android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause( 264 DisconnectCause.ICC_ERROR); 265 assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode()); 266 assertNotNull(tcCause.getLabel()); 267 assertNotNull(tcCause.getDescription()); 268 } 269 } 270