1 /** 2 * Copyright (C) 2014 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 android.content.Context; 20 import android.media.ToneGenerator; 21 import android.os.PersistableBundle; 22 import android.provider.Settings; 23 import android.telecom.DisconnectCause; 24 import android.telephony.CarrierConfigManager; 25 import android.telephony.SubscriptionManager; 26 import android.telephony.ims.ImsReasonInfo; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.telephony.CallFailCause; 30 import com.android.internal.telephony.Phone; 31 import com.android.internal.telephony.PhoneFactory; 32 import com.android.phone.ImsUtil; 33 import com.android.phone.PhoneGlobals; 34 import com.android.phone.common.R; 35 36 public class DisconnectCauseUtil { 37 38 /** 39 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more 40 * generic {@link android.telecom.DisconnectCause} object, possibly populated with a localized 41 * message and tone. 42 * 43 * @param telephonyDisconnectCause The code for the reason for the disconnect. 44 */ toTelecomDisconnectCause(int telephonyDisconnectCause)45 public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause) { 46 return toTelecomDisconnectCause(telephonyDisconnectCause, 47 CallFailCause.NOT_VALID, null /* reason */); 48 } 49 50 /** 51 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more 52 * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized 53 * message and tone. 54 * 55 * @param telephonyDisconnectCause The code for the reason for the disconnect. 56 * @param reason Description of the reason for the disconnect, not intended for the user to see.. 57 */ toTelecomDisconnectCause( int telephonyDisconnectCause, String reason)58 public static DisconnectCause toTelecomDisconnectCause( 59 int telephonyDisconnectCause, String reason) { 60 return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID, reason); 61 } 62 63 /** 64 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more 65 * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized 66 * message and tone. 67 * 68 * @param telephonyDisconnectCause The code for the reason for the disconnect. 69 * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect. 70 * @param reason Description of the reason for the disconnect, not intended for the user to see.. 71 */ toTelecomDisconnectCause( int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason)72 public static DisconnectCause toTelecomDisconnectCause( 73 int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason) { 74 return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause, 75 reason, SubscriptionManager.getDefaultVoicePhoneId(), null); 76 } 77 78 /** 79 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more 80 * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized 81 * message and tone for Slot. 82 * 83 * @param telephonyDisconnectCause The code for the reason for the disconnect. 84 * @param phoneId To support localized message based on phoneId 85 */ toTelecomDisconnectCause(int telephonyDisconnectCause, String reason, int phoneId)86 public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, 87 String reason, int phoneId) { 88 return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID, 89 reason, phoneId, null); 90 } 91 92 /** 93 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more 94 * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized 95 * message and tone for Slot. 96 * @param telephonyDisconnectCause The code for the reason for the disconnect. 97 * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect. 98 * @param reason Description of the reason for the disconnect, not intended for the user to see. 99 * @param phoneId To support localized message based on phoneId 100 * @param imsReasonInfo 101 */ toTelecomDisconnectCause( int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason, int phoneId, ImsReasonInfo imsReasonInfo)102 public static DisconnectCause toTelecomDisconnectCause( 103 int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason, 104 int phoneId, ImsReasonInfo imsReasonInfo) { 105 return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause, 106 reason, phoneId, imsReasonInfo, getCarrierConfigBundle(phoneId)); 107 } 108 109 /** 110 * Final pre-processing method in creating a DisconnectCause. This method should NOT be called 111 * from another class directly. It only has private-package visibility for testing. 112 * 113 * @param carrierConfig 114 */ 115 @VisibleForTesting toTelecomDisconnectCause( int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason, int phoneId, ImsReasonInfo imsReasonInfo, PersistableBundle carrierConfig)116 static DisconnectCause toTelecomDisconnectCause( 117 int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason, 118 int phoneId, ImsReasonInfo imsReasonInfo, PersistableBundle carrierConfig) { 119 Context context = PhoneGlobals.getInstance(); 120 121 return new DisconnectCause( 122 toTelecomDisconnectCauseCode(telephonyDisconnectCause, carrierConfig), 123 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause, 124 telephonyPreciseDisconnectCause, carrierConfig), 125 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId), 126 toTelecomDisconnectReason(context, telephonyDisconnectCause, reason, phoneId), 127 toTelecomDisconnectCauseTone(telephonyDisconnectCause, carrierConfig), 128 telephonyDisconnectCause, 129 telephonyPreciseDisconnectCause, 130 imsReasonInfo); 131 } 132 133 /** 134 * Convert the {@link android.telephony.DisconnectCause} disconnect code into a 135 * {@link android.telecom.DisconnectCause} disconnect code. 136 * @return The disconnect code as defined in {@link android.telecom.DisconnectCause}. 137 */ toTelecomDisconnectCauseCode(int telephonyDisconnectCause, PersistableBundle carrierConfig)138 private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause, 139 PersistableBundle carrierConfig) { 140 141 // special case: some carriers determine what disconnect causes play the BUSY tone. 142 // hence, must adjust the disconnectCause CODE to match the tone. 143 if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause, 144 carrierConfig)) { 145 return DisconnectCause.BUSY; 146 } 147 148 switch (telephonyDisconnectCause) { 149 case android.telephony.DisconnectCause.LOCAL: 150 // The call was still disconnected locally, so this is not an error condition. 151 case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED: 152 return DisconnectCause.LOCAL; 153 154 case android.telephony.DisconnectCause.NORMAL: 155 case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED: 156 return DisconnectCause.REMOTE; 157 158 case android.telephony.DisconnectCause.OUTGOING_CANCELED: 159 return DisconnectCause.CANCELED; 160 161 case android.telephony.DisconnectCause.INCOMING_MISSED: 162 return DisconnectCause.MISSED; 163 164 case android.telephony.DisconnectCause.INCOMING_REJECTED: 165 return DisconnectCause.REJECTED; 166 167 case android.telephony.DisconnectCause.BUSY: 168 return DisconnectCause.BUSY; 169 170 case android.telephony.DisconnectCause.CALL_BARRED: 171 case android.telephony.DisconnectCause.CDMA_ACCESS_BLOCKED: 172 case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY: 173 case android.telephony.DisconnectCause.CS_RESTRICTED: 174 case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY: 175 case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL: 176 case android.telephony.DisconnectCause.EMERGENCY_ONLY: 177 case android.telephony.DisconnectCause.FDN_BLOCKED: 178 case android.telephony.DisconnectCause.LIMIT_EXCEEDED: 179 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 180 return DisconnectCause.RESTRICTED; 181 182 case android.telephony.DisconnectCause.CDMA_ACCESS_FAILURE: 183 case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED: 184 case android.telephony.DisconnectCause.CDMA_CALL_LOST: 185 case android.telephony.DisconnectCause.CDMA_DROP: 186 case android.telephony.DisconnectCause.CDMA_INTERCEPT: 187 case android.telephony.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: 188 case android.telephony.DisconnectCause.CDMA_PREEMPTED: 189 case android.telephony.DisconnectCause.CDMA_REORDER: 190 case android.telephony.DisconnectCause.CDMA_RETRY_ORDER: 191 case android.telephony.DisconnectCause.CDMA_SO_REJECT: 192 case android.telephony.DisconnectCause.CONGESTION: 193 case android.telephony.DisconnectCause.ICC_ERROR: 194 case android.telephony.DisconnectCause.INVALID_CREDENTIALS: 195 case android.telephony.DisconnectCause.INVALID_NUMBER: 196 case android.telephony.DisconnectCause.LOST_SIGNAL: 197 case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 198 case android.telephony.DisconnectCause.NUMBER_UNREACHABLE: 199 case android.telephony.DisconnectCause.OUTGOING_FAILURE: 200 case android.telephony.DisconnectCause.OUT_OF_NETWORK: 201 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 202 case android.telephony.DisconnectCause.POWER_OFF: 203 case android.telephony.DisconnectCause.LOW_BATTERY: 204 case android.telephony.DisconnectCause.DIAL_LOW_BATTERY: 205 case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE: 206 case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION: 207 case android.telephony.DisconnectCause.SERVER_ERROR: 208 case android.telephony.DisconnectCause.SERVER_UNREACHABLE: 209 case android.telephony.DisconnectCause.TIMED_OUT: 210 case android.telephony.DisconnectCause.ALREADY_DIALING: 211 case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING: 212 case android.telephony.DisconnectCause.CALLING_DISABLED: 213 case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS: 214 case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS: 215 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 216 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 217 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD: 218 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS: 219 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL: 220 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO: 221 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS: 222 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD: 223 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL: 224 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO: 225 case android.telephony.DisconnectCause.ERROR_UNSPECIFIED: 226 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 227 case android.telephony.DisconnectCause.DATA_DISABLED: 228 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 229 case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING: 230 case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED: 231 case android.telephony.DisconnectCause.WIFI_LOST: 232 case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED: 233 case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL: 234 case android.telephony.DisconnectCause.MEDIA_TIMEOUT: 235 case android.telephony.DisconnectCause.SATELLITE_ENABLED: 236 return DisconnectCause.ERROR; 237 238 case android.telephony.DisconnectCause.DIALED_MMI: 239 case android.telephony.DisconnectCause.EXITED_ECM: 240 case android.telephony.DisconnectCause.MMI: 241 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 242 return DisconnectCause.OTHER; 243 244 case android.telephony.DisconnectCause.NOT_VALID: 245 case android.telephony.DisconnectCause.NOT_DISCONNECTED: 246 return DisconnectCause.UNKNOWN; 247 248 case android.telephony.DisconnectCause.CALL_PULLED: 249 return DisconnectCause.CALL_PULLED; 250 251 case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE: 252 return DisconnectCause.ANSWERED_ELSEWHERE; 253 254 default: 255 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode", 256 "Unrecognized Telephony DisconnectCause " 257 + telephonyDisconnectCause); 258 return DisconnectCause.UNKNOWN; 259 } 260 } 261 262 /** 263 * Returns a label for to the disconnect cause to be shown to the user. 264 */ toTelecomDisconnectCauseLabel( Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, PersistableBundle carrierConfig)265 private static CharSequence toTelecomDisconnectCauseLabel( 266 Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, 267 PersistableBundle carrierConfig) { 268 CharSequence label; 269 270 // special case: some carriers determine what disconnect causes play the BUSY tone. 271 // hence, must adjust the disconnectCause LABEL to match the tone. 272 if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause, 273 carrierConfig)) { 274 return context.getResources().getString(R.string.callFailed_userBusy); 275 } 276 277 if (telephonyPreciseDisconnectCause != CallFailCause.NOT_VALID) { 278 label = getLabelFromPreciseDisconnectCause(context, telephonyPreciseDisconnectCause, 279 telephonyDisconnectCause); 280 } else { 281 label = getLabelFromDisconnectCause(context, telephonyDisconnectCause); 282 } 283 return label; 284 } 285 286 /** 287 * Returns a label for to the generic disconnect cause to be shown to the user. 288 */ getLabelFromDisconnectCause( Context context, int telephonyDisconnectCause)289 private static CharSequence getLabelFromDisconnectCause( 290 Context context, int telephonyDisconnectCause) { 291 if (context == null) { 292 return ""; 293 } 294 295 Integer resourceId = null; 296 switch (telephonyDisconnectCause) { 297 case android.telephony.DisconnectCause.BUSY: 298 resourceId = R.string.callFailed_userBusy; 299 break; 300 301 case android.telephony.DisconnectCause.CDMA_REORDER: 302 resourceId = R.string.callFailed_NetworkBusy; 303 break; 304 305 case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED: 306 resourceId = R.string.callFailed_NetworkCongested; 307 break; 308 309 case android.telephony.DisconnectCause.CONGESTION: 310 resourceId = R.string.callFailed_congestion; 311 break; 312 313 case android.telephony.DisconnectCause.TIMED_OUT: 314 resourceId = R.string.callFailed_timedOut; 315 break; 316 317 case android.telephony.DisconnectCause.SERVER_UNREACHABLE: 318 resourceId = R.string.callFailed_server_unreachable; 319 break; 320 321 case android.telephony.DisconnectCause.NUMBER_UNREACHABLE: 322 resourceId = R.string.callFailed_number_unreachable; 323 break; 324 325 case android.telephony.DisconnectCause.INVALID_CREDENTIALS: 326 resourceId = R.string.callFailed_invalid_credentials; 327 break; 328 329 case android.telephony.DisconnectCause.SERVER_ERROR: 330 resourceId = R.string.callFailed_server_error; 331 break; 332 333 case android.telephony.DisconnectCause.OUT_OF_NETWORK: 334 resourceId = R.string.callFailed_out_of_network; 335 break; 336 337 case android.telephony.DisconnectCause.LOST_SIGNAL: 338 case android.telephony.DisconnectCause.CDMA_DROP: 339 resourceId = R.string.callFailed_noSignal; 340 break; 341 342 case android.telephony.DisconnectCause.LIMIT_EXCEEDED: 343 resourceId = R.string.callFailed_limitExceeded; 344 break; 345 346 case android.telephony.DisconnectCause.POWER_OFF: 347 resourceId = R.string.callFailed_powerOff; 348 break; 349 350 case android.telephony.DisconnectCause.LOW_BATTERY: 351 resourceId = R.string.callFailed_low_battery; 352 break; 353 354 case android.telephony.DisconnectCause.DIAL_LOW_BATTERY: 355 resourceId = R.string.dialFailed_low_battery; 356 break; 357 358 case android.telephony.DisconnectCause.ICC_ERROR: 359 resourceId = R.string.callFailed_simError; 360 break; 361 362 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 363 resourceId = R.string.callFailed_outOfService; 364 break; 365 366 case android.telephony.DisconnectCause.INVALID_NUMBER: 367 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 368 resourceId = R.string.callFailed_unobtainable_number; 369 break; 370 371 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 372 resourceId = R.string.incall_error_missing_voicemail_number; 373 break; 374 375 case android.telephony.DisconnectCause.CALL_PULLED: 376 resourceId = R.string.callEnded_pulled; 377 break; 378 379 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 380 resourceId = R.string.callFailed_maximum_reached; 381 break; 382 383 case android.telephony.DisconnectCause.DATA_DISABLED: 384 resourceId = R.string.callFailed_data_disabled; 385 break; 386 387 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 388 resourceId = R.string.callFailed_data_limit_reached; 389 break; 390 case android.telephony.DisconnectCause.WIFI_LOST: 391 resourceId = R.string.callFailed_wifi_lost; 392 break; 393 case android.telephony.DisconnectCause.ALREADY_DIALING: 394 resourceId = R.string.callFailed_already_dialing; 395 break; 396 case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING: 397 resourceId = R.string.callFailed_already_ringing; 398 break; 399 case android.telephony.DisconnectCause.CALLING_DISABLED: 400 resourceId = R.string.callFailed_calling_disabled; 401 break; 402 case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS: 403 resourceId = R.string.callFailed_too_many_calls; 404 break; 405 case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL: 406 int airplaneMode = Settings.Global.getInt(context.getContentResolver(), 407 Settings.Global.AIRPLANE_MODE_ON, 0); 408 resourceId = R.string.incall_error_call_failed; 409 if (airplaneMode != 0) { 410 resourceId = R.string.incall_error_power_off; 411 } 412 break; 413 case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS: 414 resourceId = R.string.callFailed_otasp_provisioning_in_process; 415 break; 416 case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE: 417 resourceId = R.string.callFailed_emergency_call_over_wfc_not_available; 418 break; 419 case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION: 420 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location; 421 break; 422 case android.telephony.DisconnectCause.SATELLITE_ENABLED: 423 resourceId = R.string.incall_error_satellite_enabled; 424 break; 425 default: 426 break; 427 } 428 return resourceId == null ? "" : context.getResources().getString(resourceId); 429 } 430 431 /** 432 * Returns a label for to the precise disconnect cause to be shown to the user. 433 */ getLabelFromPreciseDisconnectCause( Context context, int telephonyPreciseDisconnectCause, int telephonyDisconnectCause)434 private static CharSequence getLabelFromPreciseDisconnectCause( 435 Context context, int telephonyPreciseDisconnectCause, int telephonyDisconnectCause) { 436 if (context == null) { 437 return ""; 438 } 439 440 Integer resourceId = null; 441 switch (telephonyPreciseDisconnectCause) { 442 case CallFailCause.UNOBTAINABLE_NUMBER: 443 resourceId = R.string.clh_callFailed_unassigned_number_txt; 444 break; 445 case CallFailCause.NO_ROUTE_TO_DEST: 446 resourceId = R.string.clh_callFailed_no_route_to_destination_txt; 447 break; 448 case CallFailCause.CHANNEL_UNACCEPTABLE: 449 resourceId = R.string.clh_callFailed_channel_unacceptable_txt; 450 break; 451 case CallFailCause.OPERATOR_DETERMINED_BARRING: 452 resourceId = R.string.clh_callFailed_operator_determined_barring_txt; 453 break; 454 case CallFailCause.NORMAL_CLEARING: 455 resourceId = R.string.clh_callFailed_normal_call_clearing_txt; 456 break; 457 case CallFailCause.USER_BUSY: 458 resourceId = R.string.clh_callFailed_user_busy_txt; 459 break; 460 case CallFailCause.NO_USER_RESPONDING: 461 resourceId = R.string.clh_callFailed_no_user_responding_txt; 462 break; 463 case CallFailCause.USER_ALERTING_NO_ANSWER: 464 resourceId = R.string.clh_callFailed_user_alerting_txt; 465 break; 466 case CallFailCause.CALL_REJECTED: 467 resourceId = R.string.clh_callFailed_call_rejected_txt; 468 break; 469 case CallFailCause.NUMBER_CHANGED: 470 resourceId = R.string.clh_callFailed_number_changed_txt; 471 break; 472 case CallFailCause.PRE_EMPTION: 473 resourceId = R.string.clh_callFailed_pre_emption_txt; 474 break; 475 case CallFailCause.NON_SELECTED_USER_CLEARING: 476 resourceId = R.string.clh_callFailed_non_selected_user_clearing_txt; 477 break; 478 case CallFailCause.DESTINATION_OUT_OF_ORDER: 479 resourceId = R.string.clh_callFailed_destination_out_of_order_txt; 480 break; 481 case CallFailCause.INVALID_NUMBER_FORMAT: 482 resourceId = R.string.clh_callFailed_invalid_number_format_txt; 483 break; 484 case CallFailCause.FACILITY_REJECTED: 485 resourceId = R.string.clh_callFailed_facility_rejected_txt; 486 break; 487 case CallFailCause.STATUS_ENQUIRY: 488 resourceId = R.string.clh_callFailed_response_to_STATUS_ENQUIRY_txt; 489 break; 490 case CallFailCause.NORMAL_UNSPECIFIED: 491 resourceId = R.string.clh_callFailed_normal_unspecified_txt; 492 break; 493 case CallFailCause.NO_CIRCUIT_AVAIL: 494 resourceId = R.string.clh_callFailed_no_circuit_available_txt; 495 break; 496 case CallFailCause.NETWORK_OUT_OF_ORDER: 497 resourceId = R.string.clh_callFailed_network_out_of_order_txt; 498 break; 499 case CallFailCause.TEMPORARY_FAILURE: 500 resourceId = R.string.clh_callFailed_temporary_failure_txt; 501 break; 502 case CallFailCause.SWITCHING_CONGESTION: 503 resourceId = R.string.clh_callFailed_switching_equipment_congestion_txt; 504 break; 505 case CallFailCause.ACCESS_INFORMATION_DISCARDED: 506 resourceId = R.string.clh_callFailed_access_information_discarded_txt; 507 break; 508 case CallFailCause.CHANNEL_NOT_AVAIL: 509 resourceId = R.string.clh_callFailed_requested_circuit_txt; 510 break; 511 case CallFailCause.RESOURCES_UNAVAILABLE_UNSPECIFIED: 512 resourceId = R.string.clh_callFailed_resources_unavailable_unspecified_txt; 513 break; 514 case CallFailCause.QOS_NOT_AVAIL: 515 resourceId = R.string.clh_callFailed_quality_of_service_unavailable_txt; 516 break; 517 case CallFailCause.REQUESTED_FACILITY_NOT_SUBSCRIBED: 518 resourceId = R.string.clh_callFailed_requested_facility_not_subscribed_txt; 519 break; 520 case CallFailCause.INCOMING_CALL_BARRED_WITHIN_CUG: 521 resourceId = R.string.clh_callFailed_incoming_calls_barred_within_the_CUG_txt; 522 break; 523 case CallFailCause.BEARER_CAPABILITY_NOT_AUTHORISED: 524 resourceId = R.string.clh_callFailed_bearer_capability_not_authorized_txt; 525 break; 526 case CallFailCause.BEARER_NOT_AVAIL: 527 resourceId = R.string.clh_callFailed_bearer_capability_not_presently_available_txt; 528 break; 529 case CallFailCause.SERVICE_OR_OPTION_NOT_AVAILABLE: 530 resourceId = 531 R.string.clh_callFailed_service_or_option_not_available_unspecified_txt; 532 break; 533 case CallFailCause.BEARER_SERVICE_NOT_IMPLEMENTED: 534 resourceId = R.string.clh_callFailed_bearer_service_not_implemented_txt; 535 break; 536 case CallFailCause.ACM_LIMIT_EXCEEDED: 537 resourceId = R.string.clh_callFailed_ACM_equal_to_or_greater_than_ACMmax_txt; 538 break; 539 case CallFailCause.REQUESTED_FACILITY_NOT_IMPLEMENTED: 540 resourceId = R.string.clh_callFailed_requested_facility_not_implemented_txt; 541 break; 542 case CallFailCause.ONLY_RESTRICTED_DIGITAL_INFO_BC_AVAILABLE: 543 resourceId = R.string 544 .clh_callFailed_only_restricted_digital_information_bearer_capability_is_available_txt; 545 break; 546 case CallFailCause.SERVICE_OR_OPTION_NOT_IMPLEMENTED: 547 resourceId = 548 R.string.clh_callFailed_service_or_option_not_implemented_unspecified_txt; 549 break; 550 case CallFailCause.INVALID_TRANSACTION_ID_VALUE: 551 resourceId = R.string.clh_callFailed_invalid_transaction_identifier_value_txt; 552 break; 553 case CallFailCause.USER_NOT_MEMBER_OF_CUG: 554 resourceId = R.string.clh_callFailed_user_not_member_of_CUG_txt; 555 break; 556 case CallFailCause.INCOMPATIBLE_DESTINATION: 557 resourceId = R.string.clh_callFailed_incompatible_destination_txt; 558 break; 559 case CallFailCause.INVALID_TRANSIT_NETWORK_SELECTION: 560 resourceId = R.string.clh_callFailed_invalid_transit_network_selection_txt; 561 break; 562 case CallFailCause.SEMANTICALLY_INCORRECT_MESSAGE: 563 resourceId = R.string.clh_callFailed_semantically_incorrect_message_txt; 564 break; 565 case CallFailCause.INVALID_MANDATORY_INFORMATION: 566 resourceId = R.string.clh_callFailed_invalid_mandatory_information_txt; 567 break; 568 case CallFailCause.MESSAGE_TYPE_NON_EXISTENT: 569 resourceId = 570 R.string.clh_callFailed_message_type_non_existent_or_not_implemented_txt; 571 break; 572 case CallFailCause.MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROT_STATE: 573 resourceId = R.string 574 .clh_callFailed_message_type_not_compatible_with_protocol_state_txt; 575 break; 576 case CallFailCause.IE_NON_EXISTENT_OR_NOT_IMPLEMENTED: 577 resourceId = R.string 578 .clh_callFailed_information_element_non_existent_or_not_implemented_txt; 579 break; 580 case CallFailCause.CONDITIONAL_IE_ERROR: 581 resourceId = R.string.clh_callFailed_conditional_IE_error_txt; 582 break; 583 case CallFailCause.MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE: 584 resourceId = R.string.clh_callFailed_message_not_compatible_with_protocol_state_txt; 585 break; 586 case CallFailCause.RECOVERY_ON_TIMER_EXPIRY: 587 resourceId = R.string.clh_callFailed_recovery_on_timer_expiry_txt; 588 break; 589 case CallFailCause.PROTOCOL_ERROR_UNSPECIFIED: 590 resourceId = R.string.clh_callFailed_protocol_Error_unspecified_txt; 591 break; 592 case CallFailCause.INTERWORKING_UNSPECIFIED: 593 resourceId = R.string.clh_callFailed_interworking_unspecified_txt; 594 break; 595 default: 596 switch (telephonyDisconnectCause) { 597 case android.telephony.DisconnectCause.POWER_OFF: 598 resourceId = R.string.clh_callFailed_powerOff_txt; 599 break; 600 case android.telephony.DisconnectCause.ICC_ERROR: 601 resourceId = R.string.clh_callFailed_simError_txt; 602 break; 603 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 604 resourceId = R.string.clh_incall_error_out_of_service_txt; 605 break; 606 case android.telephony.DisconnectCause.SATELLITE_ENABLED: 607 resourceId = R.string.clh_callFailed_satelliteEnabled_txt; 608 break; 609 default: 610 resourceId = R.string.clh_card_title_call_ended_txt; 611 break; 612 } 613 break; 614 } 615 return context.getResources().getString(resourceId); 616 } 617 618 /** 619 * Returns a description of the disconnect cause to be shown to the user. 620 */ toTelecomDisconnectCauseDescription( Context context, int telephonyDisconnectCause, int phoneId)621 private static CharSequence toTelecomDisconnectCauseDescription( 622 Context context, int telephonyDisconnectCause, int phoneId) { 623 if (context == null ) { 624 return ""; 625 } 626 627 Integer resourceId = null; 628 switch (telephonyDisconnectCause) { 629 case android.telephony.DisconnectCause.CALL_BARRED: 630 resourceId = R.string.callFailed_cb_enabled; 631 break; 632 633 case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED: 634 resourceId = R.string.callFailed_cdma_activation; 635 break; 636 637 case android.telephony.DisconnectCause.CDMA_REORDER: 638 resourceId = R.string.callFailed_NetworkBusy; 639 break; 640 641 case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED: 642 resourceId = R.string.callFailed_NetworkCongested; 643 break; 644 645 case android.telephony.DisconnectCause.FDN_BLOCKED: 646 resourceId = R.string.callFailed_fdn_only; 647 break; 648 649 case android.telephony.DisconnectCause.CS_RESTRICTED: 650 resourceId = R.string.callFailed_dsac_restricted; 651 break; 652 653 case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY: 654 resourceId = R.string.callFailed_dsac_restricted_emergency; 655 break; 656 657 case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL: 658 resourceId = R.string.callFailed_dsac_restricted_normal; 659 break; 660 661 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD: 662 resourceId = R.string.callFailed_dialToUssd; 663 break; 664 665 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS: 666 resourceId = R.string.callFailed_dialToSs; 667 break; 668 669 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL: 670 resourceId = R.string.callFailed_dialToDial; 671 break; 672 673 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO: 674 resourceId = R.string.callFailed_dialToDialVideo; 675 break; 676 677 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS: 678 resourceId = R.string.callFailed_dialVideoToSs; 679 break; 680 681 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD: 682 resourceId = R.string.callFailed_dialVideoToUssd; 683 break; 684 685 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL: 686 resourceId = R.string.callFailed_dialVideoToDial; 687 break; 688 689 case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO: 690 resourceId = R.string.callFailed_dialVideoToDialVideo; 691 break; 692 693 case android.telephony.DisconnectCause.OUTGOING_FAILURE: 694 // We couldn't successfully place the call; there was some 695 // failure in the telephony layer. 696 // TODO: Need UI spec for this failure case; for now just 697 // show a generic error. 698 resourceId = R.string.incall_error_call_failed; 699 break; 700 701 case android.telephony.DisconnectCause.POWER_OFF: 702 // Radio is explicitly powered off because the device's radio is off. 703 704 // TODO: Offer the option to turn the radio on, and automatically retry the call 705 // once network registration is complete. 706 707 if (isRadioOffForThermalMitigation(phoneId)) { 708 resourceId = R.string.incall_error_power_off_thermal; 709 } else if (ImsUtil.shouldPromoteWfc(context, phoneId)) { 710 resourceId = R.string.incall_error_promote_wfc; 711 } else if (ImsUtil.isWfcModeWifiOnly(context, phoneId)) { 712 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 713 } else if (ImsUtil.isWfcEnabled(context, phoneId)) { 714 resourceId = R.string.incall_error_power_off_wfc; 715 } else { 716 resourceId = R.string.incall_error_power_off; 717 } 718 break; 719 720 case android.telephony.DisconnectCause.LOW_BATTERY: 721 resourceId = R.string.callFailed_low_battery; 722 break; 723 724 case android.telephony.DisconnectCause.DIAL_LOW_BATTERY: 725 resourceId = R.string.dialFailed_low_battery; 726 break; 727 728 case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY: 729 // Only emergency calls are allowed when in emergency callback mode. 730 resourceId = R.string.incall_error_ecm_emergency_only; 731 break; 732 733 case android.telephony.DisconnectCause.EMERGENCY_ONLY: 734 // Only emergency numbers are allowed, but we tried to dial 735 // a non-emergency number. 736 resourceId = R.string.incall_error_emergency_only; 737 break; 738 739 case android.telephony.DisconnectCause.ICC_ERROR: 740 resourceId = R.string.callFailed_simError; 741 break; 742 743 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 744 // No network connection. 745 if (ImsUtil.shouldPromoteWfc(context, phoneId)) { 746 resourceId = R.string.incall_error_promote_wfc; 747 } else if (ImsUtil.isWfcModeWifiOnly(context, phoneId)) { 748 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 749 } else if (ImsUtil.isWfcEnabled(context, phoneId)) { 750 resourceId = R.string.incall_error_out_of_service_wfc; 751 } else { 752 resourceId = R.string.incall_error_out_of_service; 753 } 754 break; 755 756 case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 757 // The supplied Intent didn't contain a valid phone number. 758 // (This is rare and should only ever happen with broken 759 // 3rd-party apps.) For now just show a generic error. 760 resourceId = R.string.incall_error_no_phone_number_supplied; 761 break; 762 763 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 764 resourceId = R.string.incall_error_missing_voicemail_number; 765 break; 766 767 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 768 resourceId = R.string.callFailed_video_call_tty_enabled; 769 break; 770 771 case android.telephony.DisconnectCause.CALL_PULLED: 772 resourceId = R.string.callEnded_pulled; 773 break; 774 775 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 776 resourceId = R.string.callFailed_maximum_reached; 777 778 case android.telephony.DisconnectCause.OUTGOING_CANCELED: 779 // We don't want to show any dialog for the canceled case since the call was 780 // either canceled by the user explicitly (end-call button pushed immediately) 781 // or some other app canceled the call and immediately issued a new CALL to 782 // replace it. 783 break; 784 785 case android.telephony.DisconnectCause.DATA_DISABLED: 786 resourceId = R.string.callFailed_data_disabled; 787 break; 788 789 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 790 resourceId = R.string.callFailed_data_limit_reached_description; 791 break; 792 case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING: 793 resourceId = com.android.internal.R.string.mmiErrorWhileRoaming; 794 break; 795 796 case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED: 797 resourceId = R.string.callFailed_imei_not_accepted; 798 break; 799 800 case android.telephony.DisconnectCause.WIFI_LOST: 801 resourceId = R.string.callFailed_wifi_lost; 802 break; 803 case android.telephony.DisconnectCause.ALREADY_DIALING: 804 resourceId = R.string.callFailed_already_dialing; 805 break; 806 case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING: 807 resourceId = R.string.callFailed_already_ringing; 808 break; 809 case android.telephony.DisconnectCause.CALLING_DISABLED: 810 resourceId = R.string.callFailed_calling_disabled; 811 break; 812 case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS: 813 resourceId = R.string.callFailed_too_many_calls; 814 break; 815 case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL: 816 int airplaneMode = Settings.Global.getInt(context.getContentResolver(), 817 Settings.Global.AIRPLANE_MODE_ON, 0); 818 resourceId = R.string.incall_error_call_failed; 819 if (airplaneMode != 0) { 820 resourceId = R.string.incall_error_power_off; 821 } 822 break; 823 case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS: 824 resourceId = R.string.callFailed_otasp_provisioning_in_process; 825 break; 826 case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE: 827 resourceId = R.string.callFailed_emergency_call_over_wfc_not_available; 828 break; 829 case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION: 830 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location; 831 break; 832 case android.telephony.DisconnectCause.SATELLITE_ENABLED: 833 resourceId = R.string.incall_error_satellite_enabled; 834 break; 835 default: 836 break; 837 } 838 return resourceId == null ? "" : context.getResources().getString(resourceId); 839 } 840 isRadioOffForThermalMitigation(int phoneId)841 private static boolean isRadioOffForThermalMitigation(int phoneId) { 842 Phone phone = PhoneFactory.getPhone(phoneId); 843 return phone == null ? false : phone.isRadioOffForThermalMitigation(); 844 } 845 846 /** 847 * Maps the telephony {@link android.telephony.DisconnectCause} into a reason string which is 848 * returned in the Telecom {@link DisconnectCause#getReason()}. 849 * 850 * @param context The current context. 851 * @param telephonyDisconnectCause The {@link android.telephony.DisconnectCause} code. 852 * @param reason A reason provided by the caller; only used if a more specific reason cannot 853 * be determined here. 854 * @return The disconnect reason. 855 */ toTelecomDisconnectReason(Context context, int telephonyDisconnectCause, String reason, int phoneId)856 private static String toTelecomDisconnectReason(Context context, int telephonyDisconnectCause, 857 String reason, int phoneId) { 858 859 if (context == null) { 860 return ""; 861 } 862 863 switch (telephonyDisconnectCause) { 864 case android.telephony.DisconnectCause.POWER_OFF: 865 // Airplane mode (radio off) 866 // intentional fall-through 867 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 868 // No network connection. 869 if (ImsUtil.shouldPromoteWfc(context, phoneId)) { 870 return android.telecom.DisconnectCause.REASON_WIFI_ON_BUT_WFC_OFF; 871 } 872 break; 873 case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED: 874 return DisconnectCause.REASON_IMS_ACCESS_BLOCKED; 875 case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED: 876 return DisconnectCause.REASON_EMERGENCY_CALL_PLACED; 877 } 878 879 // If no specific code-mapping found, then fall back to using the reason. 880 String causeAsString = android.telephony.DisconnectCause.toString(telephonyDisconnectCause); 881 if (reason == null) { 882 return causeAsString; 883 } else { 884 return reason + ", " + causeAsString; 885 } 886 } 887 888 /** 889 * Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played. 890 */ toTelecomDisconnectCauseTone(int telephonyDisconnectCause, PersistableBundle carrierConfig)891 private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause, 892 PersistableBundle carrierConfig) { 893 894 // special case: some carriers determine what disconnect causes play the BUSY tone. 895 if (doesCarrierClassifyDisconnectCauseAsBusyCause(telephonyDisconnectCause, 896 carrierConfig)) { 897 return ToneGenerator.TONE_SUP_BUSY; 898 } 899 900 switch (telephonyDisconnectCause) { 901 case android.telephony.DisconnectCause.CONGESTION: 902 return ToneGenerator.TONE_SUP_CONGESTION; 903 904 case android.telephony.DisconnectCause.CDMA_REORDER: 905 return ToneGenerator.TONE_CDMA_REORDER; 906 907 case android.telephony.DisconnectCause.CDMA_INTERCEPT: 908 return ToneGenerator.TONE_CDMA_ABBR_INTERCEPT; 909 910 case android.telephony.DisconnectCause.CDMA_DROP: 911 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 912 return ToneGenerator.TONE_CDMA_CALLDROP_LITE; 913 914 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 915 return ToneGenerator.TONE_SUP_ERROR; 916 917 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 918 // Do not play any tones if disconnected because of a successful merge. 919 return -1; 920 921 case android.telephony.DisconnectCause.ERROR_UNSPECIFIED: 922 case android.telephony.DisconnectCause.LOCAL: 923 case android.telephony.DisconnectCause.NORMAL: 924 case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED: 925 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 926 case android.telephony.DisconnectCause.WIFI_LOST: 927 default: 928 return ToneGenerator.TONE_PROP_PROMPT; 929 } 930 } 931 932 /** 933 * Helper method that examines the carrierConfig KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY 934 * containing the DisconnectCauses that are classified as DisconnectCause.BUSY 935 * @param telephonyDisconnectCause 936 * @param carrierConfig object that holds all the carrier specific settings 937 * @return whether the cause is in the carrier config busy tone array 938 */ doesCarrierClassifyDisconnectCauseAsBusyCause( int telephonyDisconnectCause, PersistableBundle carrierConfig)939 private static boolean doesCarrierClassifyDisconnectCauseAsBusyCause( 940 int telephonyDisconnectCause, PersistableBundle carrierConfig) { 941 int[] busyToneArray = carrierConfig.getIntArray( 942 CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY); 943 for (int busyTone : busyToneArray) { 944 if (busyTone == telephonyDisconnectCause) { 945 return true; 946 } 947 } 948 return false; 949 } 950 getCarrierConfigBundle(int phoneId)951 private static PersistableBundle getCarrierConfigBundle(int phoneId) { 952 Phone phone = PhoneFactory.getPhone(phoneId); 953 PersistableBundle config; 954 955 if (phone != null) { 956 config = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId()); 957 } else { 958 config = PhoneGlobals.getInstance().getCarrierConfig(); 959 } 960 961 return config; 962 } 963 964 } 965