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