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