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.telecom.DisconnectCause; 22 23 import com.android.phone.PhoneGlobals; 24 import com.android.phone.common.R; 25 import com.android.phone.ImsUtil; 26 27 public class DisconnectCauseUtil { 28 29 /** 30 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more generic 31 * {@link android.telecom.DisconnectCause}.object, possibly populated with a localized message 32 * and tone. 33 * 34 * @param context The context. 35 * @param telephonyDisconnectCause The code for the reason for the disconnect. 36 */ 37 public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause) { 38 return toTelecomDisconnectCause(telephonyDisconnectCause, null /* reason */); 39 } 40 41 /** 42 * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more generic 43 * {@link android.telecom.DisconnectCause}.object, possibly populated with a localized message 44 * and tone. 45 * 46 * @param context The context. 47 * @param telephonyDisconnectCause The code for the reason for the disconnect. 48 * @param reason Description of the reason for the disconnect, not intended for the user to see.. 49 */ 50 public static DisconnectCause toTelecomDisconnectCause( 51 int telephonyDisconnectCause, String reason) { 52 Context context = PhoneGlobals.getInstance(); 53 return new DisconnectCause( 54 toTelecomDisconnectCauseCode(telephonyDisconnectCause), 55 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause), 56 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause), 57 toTelecomDisconnectReason(telephonyDisconnectCause, reason), 58 toTelecomDisconnectCauseTone(telephonyDisconnectCause)); 59 } 60 61 /** 62 * Convert the {@link android.telephony.DisconnectCause} disconnect code into a 63 * {@link android.telecom.DisconnectCause} disconnect code. 64 * @return The disconnect code as defined in {@link android.telecom.DisconnectCause}. 65 */ 66 private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) { 67 switch (telephonyDisconnectCause) { 68 case android.telephony.DisconnectCause.LOCAL: 69 return DisconnectCause.LOCAL; 70 71 case android.telephony.DisconnectCause.NORMAL: 72 return DisconnectCause.REMOTE; 73 74 case android.telephony.DisconnectCause.OUTGOING_CANCELED: 75 return DisconnectCause.CANCELED; 76 77 case android.telephony.DisconnectCause.INCOMING_MISSED: 78 return DisconnectCause.MISSED; 79 80 case android.telephony.DisconnectCause.INCOMING_REJECTED: 81 return DisconnectCause.REJECTED; 82 83 case android.telephony.DisconnectCause.BUSY: 84 return DisconnectCause.BUSY; 85 86 case android.telephony.DisconnectCause.CALL_BARRED: 87 case android.telephony.DisconnectCause.CDMA_ACCESS_BLOCKED: 88 case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY: 89 case android.telephony.DisconnectCause.CS_RESTRICTED: 90 case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY: 91 case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL: 92 case android.telephony.DisconnectCause.EMERGENCY_ONLY: 93 case android.telephony.DisconnectCause.FDN_BLOCKED: 94 case android.telephony.DisconnectCause.LIMIT_EXCEEDED: 95 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 96 return DisconnectCause.RESTRICTED; 97 98 case android.telephony.DisconnectCause.CDMA_ACCESS_FAILURE: 99 case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED: 100 case android.telephony.DisconnectCause.CDMA_CALL_LOST: 101 case android.telephony.DisconnectCause.CDMA_DROP: 102 case android.telephony.DisconnectCause.CDMA_INTERCEPT: 103 case android.telephony.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: 104 case android.telephony.DisconnectCause.CDMA_PREEMPTED: 105 case android.telephony.DisconnectCause.CDMA_REORDER: 106 case android.telephony.DisconnectCause.CDMA_RETRY_ORDER: 107 case android.telephony.DisconnectCause.CDMA_SO_REJECT: 108 case android.telephony.DisconnectCause.CONGESTION: 109 case android.telephony.DisconnectCause.ICC_ERROR: 110 case android.telephony.DisconnectCause.INVALID_CREDENTIALS: 111 case android.telephony.DisconnectCause.INVALID_NUMBER: 112 case android.telephony.DisconnectCause.LOST_SIGNAL: 113 case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 114 case android.telephony.DisconnectCause.NUMBER_UNREACHABLE: 115 case android.telephony.DisconnectCause.OUTGOING_FAILURE: 116 case android.telephony.DisconnectCause.OUT_OF_NETWORK: 117 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 118 case android.telephony.DisconnectCause.POWER_OFF: 119 case android.telephony.DisconnectCause.SERVER_ERROR: 120 case android.telephony.DisconnectCause.SERVER_UNREACHABLE: 121 case android.telephony.DisconnectCause.TIMED_OUT: 122 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 123 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 124 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD: 125 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS: 126 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL: 127 case android.telephony.DisconnectCause.ERROR_UNSPECIFIED: 128 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 129 case android.telephony.DisconnectCause.DATA_DISABLED: 130 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 131 case android.telephony.DisconnectCause.DIALED_ON_WRONG_SLOT: 132 return DisconnectCause.ERROR; 133 134 case android.telephony.DisconnectCause.DIALED_MMI: 135 case android.telephony.DisconnectCause.EXITED_ECM: 136 case android.telephony.DisconnectCause.MMI: 137 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 138 return DisconnectCause.OTHER; 139 140 case android.telephony.DisconnectCause.NOT_VALID: 141 case android.telephony.DisconnectCause.NOT_DISCONNECTED: 142 return DisconnectCause.UNKNOWN; 143 144 case android.telephony.DisconnectCause.CALL_PULLED: 145 return DisconnectCause.CALL_PULLED; 146 147 case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE: 148 return DisconnectCause.ANSWERED_ELSEWHERE; 149 150 default: 151 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode", 152 "Unrecognized Telephony DisconnectCause " 153 + telephonyDisconnectCause); 154 return DisconnectCause.UNKNOWN; 155 } 156 } 157 158 /** 159 * Returns a label for to the disconnect cause to be shown to the user. 160 */ 161 private static CharSequence toTelecomDisconnectCauseLabel( 162 Context context, int telephonyDisconnectCause) { 163 if (context == null ) { 164 return ""; 165 } 166 167 Integer resourceId = null; 168 switch (telephonyDisconnectCause) { 169 case android.telephony.DisconnectCause.BUSY: 170 resourceId = R.string.callFailed_userBusy; 171 break; 172 173 case android.telephony.DisconnectCause.CONGESTION: 174 resourceId = R.string.callFailed_congestion; 175 break; 176 177 case android.telephony.DisconnectCause.TIMED_OUT: 178 resourceId = R.string.callFailed_timedOut; 179 break; 180 181 case android.telephony.DisconnectCause.SERVER_UNREACHABLE: 182 resourceId = R.string.callFailed_server_unreachable; 183 break; 184 185 case android.telephony.DisconnectCause.NUMBER_UNREACHABLE: 186 resourceId = R.string.callFailed_number_unreachable; 187 break; 188 189 case android.telephony.DisconnectCause.INVALID_CREDENTIALS: 190 resourceId = R.string.callFailed_invalid_credentials; 191 break; 192 193 case android.telephony.DisconnectCause.SERVER_ERROR: 194 resourceId = R.string.callFailed_server_error; 195 break; 196 197 case android.telephony.DisconnectCause.OUT_OF_NETWORK: 198 resourceId = R.string.callFailed_out_of_network; 199 break; 200 201 case android.telephony.DisconnectCause.LOST_SIGNAL: 202 case android.telephony.DisconnectCause.CDMA_DROP: 203 resourceId = R.string.callFailed_noSignal; 204 break; 205 206 case android.telephony.DisconnectCause.LIMIT_EXCEEDED: 207 resourceId = R.string.callFailed_limitExceeded; 208 break; 209 210 case android.telephony.DisconnectCause.POWER_OFF: 211 resourceId = R.string.callFailed_powerOff; 212 break; 213 214 case android.telephony.DisconnectCause.ICC_ERROR: 215 resourceId = R.string.callFailed_simError; 216 break; 217 218 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 219 resourceId = R.string.callFailed_outOfService; 220 break; 221 222 case android.telephony.DisconnectCause.INVALID_NUMBER: 223 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 224 resourceId = R.string.callFailed_unobtainable_number; 225 break; 226 227 case android.telephony.DisconnectCause.CALL_PULLED: 228 resourceId = R.string.callEnded_pulled; 229 break; 230 231 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 232 resourceId = R.string.callFailed_maximum_reached; 233 break; 234 235 case android.telephony.DisconnectCause.DATA_DISABLED: 236 resourceId = R.string.callFailed_data_disabled; 237 break; 238 239 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 240 resourceId = R.string.callFailed_data_limit_reached; 241 break; 242 243 default: 244 break; 245 } 246 return resourceId == null ? "" : context.getResources().getString(resourceId); 247 } 248 249 /** 250 * Returns a description of the disconnect cause to be shown to the user. 251 */ 252 private static CharSequence toTelecomDisconnectCauseDescription( 253 Context context, int telephonyDisconnectCause) { 254 if (context == null ) { 255 return ""; 256 } 257 258 Integer resourceId = null; 259 switch (telephonyDisconnectCause) { 260 case android.telephony.DisconnectCause.CALL_BARRED: 261 resourceId = R.string.callFailed_cb_enabled; 262 break; 263 264 case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED: 265 resourceId = R.string.callFailed_cdma_activation; 266 break; 267 268 case android.telephony.DisconnectCause.FDN_BLOCKED: 269 resourceId = R.string.callFailed_fdn_only; 270 break; 271 272 case android.telephony.DisconnectCause.CS_RESTRICTED: 273 resourceId = R.string.callFailed_dsac_restricted; 274 break; 275 276 case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY: 277 resourceId = R.string.callFailed_dsac_restricted_emergency; 278 break; 279 280 case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL: 281 resourceId = R.string.callFailed_dsac_restricted_normal; 282 break; 283 284 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD: 285 resourceId = R.string.callFailed_dialToUssd; 286 break; 287 288 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS: 289 resourceId = R.string.callFailed_dialToSs; 290 break; 291 292 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL: 293 resourceId = R.string.callFailed_dialToDial; 294 break; 295 296 case android.telephony.DisconnectCause.OUTGOING_FAILURE: 297 // We couldn't successfully place the call; there was some 298 // failure in the telephony layer. 299 // TODO: Need UI spec for this failure case; for now just 300 // show a generic error. 301 resourceId = R.string.incall_error_call_failed; 302 break; 303 304 case android.telephony.DisconnectCause.POWER_OFF: 305 // Radio is explictly powered off because the device is in airplane mode. 306 307 // TODO: Offer the option to turn the radio on, and automatically retry the call 308 // once network registration is complete. 309 310 if (ImsUtil.shouldPromoteWfc(context)) { 311 resourceId = R.string.incall_error_promote_wfc; 312 } else if (ImsUtil.isWfcModeWifiOnly(context)) { 313 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 314 } else if (ImsUtil.isWfcEnabled(context)) { 315 resourceId = R.string.incall_error_power_off_wfc; 316 } else { 317 resourceId = R.string.incall_error_power_off; 318 } 319 break; 320 321 case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY: 322 // Only emergency calls are allowed when in emergency callback mode. 323 resourceId = R.string.incall_error_ecm_emergency_only; 324 break; 325 326 case android.telephony.DisconnectCause.EMERGENCY_ONLY: 327 // Only emergency numbers are allowed, but we tried to dial 328 // a non-emergency number. 329 resourceId = R.string.incall_error_emergency_only; 330 break; 331 332 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 333 // No network connection. 334 if (ImsUtil.shouldPromoteWfc(context)) { 335 resourceId = R.string.incall_error_promote_wfc; 336 } else if (ImsUtil.isWfcModeWifiOnly(context)) { 337 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 338 } else if (ImsUtil.isWfcEnabled(context)) { 339 resourceId = R.string.incall_error_out_of_service_wfc; 340 } else { 341 resourceId = R.string.incall_error_out_of_service; 342 } 343 break; 344 345 case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 346 // The supplied Intent didn't contain a valid phone number. 347 // (This is rare and should only ever happen with broken 348 // 3rd-party apps.) For now just show a generic error. 349 resourceId = R.string.incall_error_no_phone_number_supplied; 350 break; 351 352 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 353 // TODO: Need to bring up the "Missing Voicemail Number" dialog, which 354 // will ultimately take us to the Call Settings. 355 resourceId = R.string.incall_error_missing_voicemail_number; 356 break; 357 358 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 359 resourceId = R.string.callFailed_video_call_tty_enabled; 360 break; 361 362 case android.telephony.DisconnectCause.CALL_PULLED: 363 resourceId = R.string.callEnded_pulled; 364 break; 365 366 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 367 resourceId = R.string.callFailed_maximum_reached; 368 369 case android.telephony.DisconnectCause.OUTGOING_CANCELED: 370 // We don't want to show any dialog for the canceled case since the call was 371 // either canceled by the user explicitly (end-call button pushed immediately) 372 // or some other app canceled the call and immediately issued a new CALL to 373 // replace it. 374 break; 375 376 case android.telephony.DisconnectCause.DATA_DISABLED: 377 resourceId = R.string.callFailed_data_disabled; 378 break; 379 380 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 381 resourceId = R.string.callFailed_data_limit_reached_description; 382 break; 383 384 default: 385 break; 386 } 387 return resourceId == null ? "" : context.getResources().getString(resourceId); 388 } 389 390 private static String toTelecomDisconnectReason(int telephonyDisconnectCause, String reason) { 391 String causeAsString = android.telephony.DisconnectCause.toString(telephonyDisconnectCause); 392 if (reason == null) { 393 return causeAsString; 394 } else { 395 return reason + ", " + causeAsString; 396 } 397 } 398 399 /** 400 * Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played. 401 */ 402 private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause) { 403 switch (telephonyDisconnectCause) { 404 case android.telephony.DisconnectCause.BUSY: 405 return ToneGenerator.TONE_SUP_BUSY; 406 407 case android.telephony.DisconnectCause.CONGESTION: 408 return ToneGenerator.TONE_SUP_CONGESTION; 409 410 case android.telephony.DisconnectCause.CDMA_REORDER: 411 return ToneGenerator.TONE_CDMA_REORDER; 412 413 case android.telephony.DisconnectCause.CDMA_INTERCEPT: 414 return ToneGenerator.TONE_CDMA_ABBR_INTERCEPT; 415 416 case android.telephony.DisconnectCause.CDMA_DROP: 417 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 418 return ToneGenerator.TONE_CDMA_CALLDROP_LITE; 419 420 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 421 return ToneGenerator.TONE_SUP_ERROR; 422 423 case android.telephony.DisconnectCause.ERROR_UNSPECIFIED: 424 case android.telephony.DisconnectCause.LOCAL: 425 case android.telephony.DisconnectCause.NORMAL: 426 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 427 return ToneGenerator.TONE_PROP_PROMPT; 428 429 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 430 // Do not play any tones if disconnected because of a successful merge. 431 default: 432 return ToneGenerator.TONE_UNKNOWN; 433 } 434 } 435 } 436