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