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 */ toTelecomDisconnectCause(int telephonyDisconnectCause)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 */ toTelecomDisconnectCause( int telephonyDisconnectCause, String reason)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 */ toTelecomDisconnectCauseCode(int telephonyDisconnectCause)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 return DisconnectCause.ERROR; 132 133 case android.telephony.DisconnectCause.DIALED_MMI: 134 case android.telephony.DisconnectCause.EXITED_ECM: 135 case android.telephony.DisconnectCause.MMI: 136 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 137 return DisconnectCause.OTHER; 138 139 case android.telephony.DisconnectCause.NOT_VALID: 140 case android.telephony.DisconnectCause.NOT_DISCONNECTED: 141 return DisconnectCause.UNKNOWN; 142 143 case android.telephony.DisconnectCause.CALL_PULLED: 144 return DisconnectCause.CALL_PULLED; 145 146 case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE: 147 return DisconnectCause.ANSWERED_ELSEWHERE; 148 149 default: 150 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode", 151 "Unrecognized Telephony DisconnectCause " 152 + telephonyDisconnectCause); 153 return DisconnectCause.UNKNOWN; 154 } 155 } 156 157 /** 158 * Returns a label for to the disconnect cause to be shown to the user. 159 */ toTelecomDisconnectCauseLabel( Context context, int telephonyDisconnectCause)160 private static CharSequence toTelecomDisconnectCauseLabel( 161 Context context, int telephonyDisconnectCause) { 162 if (context == null ) { 163 return ""; 164 } 165 166 Integer resourceId = null; 167 switch (telephonyDisconnectCause) { 168 case android.telephony.DisconnectCause.BUSY: 169 resourceId = R.string.callFailed_userBusy; 170 break; 171 172 case android.telephony.DisconnectCause.CONGESTION: 173 resourceId = R.string.callFailed_congestion; 174 break; 175 176 case android.telephony.DisconnectCause.TIMED_OUT: 177 resourceId = R.string.callFailed_timedOut; 178 break; 179 180 case android.telephony.DisconnectCause.SERVER_UNREACHABLE: 181 resourceId = R.string.callFailed_server_unreachable; 182 break; 183 184 case android.telephony.DisconnectCause.NUMBER_UNREACHABLE: 185 resourceId = R.string.callFailed_number_unreachable; 186 break; 187 188 case android.telephony.DisconnectCause.INVALID_CREDENTIALS: 189 resourceId = R.string.callFailed_invalid_credentials; 190 break; 191 192 case android.telephony.DisconnectCause.SERVER_ERROR: 193 resourceId = R.string.callFailed_server_error; 194 break; 195 196 case android.telephony.DisconnectCause.OUT_OF_NETWORK: 197 resourceId = R.string.callFailed_out_of_network; 198 break; 199 200 case android.telephony.DisconnectCause.LOST_SIGNAL: 201 case android.telephony.DisconnectCause.CDMA_DROP: 202 resourceId = R.string.callFailed_noSignal; 203 break; 204 205 case android.telephony.DisconnectCause.LIMIT_EXCEEDED: 206 resourceId = R.string.callFailed_limitExceeded; 207 break; 208 209 case android.telephony.DisconnectCause.POWER_OFF: 210 resourceId = R.string.callFailed_powerOff; 211 break; 212 213 case android.telephony.DisconnectCause.ICC_ERROR: 214 resourceId = R.string.callFailed_simError; 215 break; 216 217 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 218 resourceId = R.string.callFailed_outOfService; 219 break; 220 221 case android.telephony.DisconnectCause.INVALID_NUMBER: 222 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 223 resourceId = R.string.callFailed_unobtainable_number; 224 break; 225 226 case android.telephony.DisconnectCause.CALL_PULLED: 227 resourceId = R.string.callEnded_pulled; 228 break; 229 230 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 231 resourceId = R.string.callFailed_maximum_reached; 232 break; 233 234 case android.telephony.DisconnectCause.DATA_DISABLED: 235 resourceId = R.string.callFailed_data_disabled; 236 break; 237 238 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 239 resourceId = R.string.callFailed_data_limit_reached; 240 break; 241 242 default: 243 break; 244 } 245 return resourceId == null ? "" : context.getResources().getString(resourceId); 246 } 247 248 /** 249 * Returns a description of the disconnect cause to be shown to the user. 250 */ toTelecomDisconnectCauseDescription( Context context, int telephonyDisconnectCause)251 private static CharSequence toTelecomDisconnectCauseDescription( 252 Context context, int telephonyDisconnectCause) { 253 if (context == null ) { 254 return ""; 255 } 256 257 Integer resourceId = null; 258 switch (telephonyDisconnectCause) { 259 case android.telephony.DisconnectCause.CALL_BARRED: 260 resourceId = R.string.callFailed_cb_enabled; 261 break; 262 263 case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED: 264 resourceId = R.string.callFailed_cdma_activation; 265 break; 266 267 case android.telephony.DisconnectCause.FDN_BLOCKED: 268 resourceId = R.string.callFailed_fdn_only; 269 break; 270 271 case android.telephony.DisconnectCause.CS_RESTRICTED: 272 resourceId = R.string.callFailed_dsac_restricted; 273 break; 274 275 case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY: 276 resourceId = R.string.callFailed_dsac_restricted_emergency; 277 break; 278 279 case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL: 280 resourceId = R.string.callFailed_dsac_restricted_normal; 281 break; 282 283 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD: 284 resourceId = R.string.callFailed_dialToUssd; 285 break; 286 287 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS: 288 resourceId = R.string.callFailed_dialToSs; 289 break; 290 291 case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL: 292 resourceId = R.string.callFailed_dialToDial; 293 break; 294 295 case android.telephony.DisconnectCause.OUTGOING_FAILURE: 296 // We couldn't successfully place the call; there was some 297 // failure in the telephony layer. 298 // TODO: Need UI spec for this failure case; for now just 299 // show a generic error. 300 resourceId = R.string.incall_error_call_failed; 301 break; 302 303 case android.telephony.DisconnectCause.POWER_OFF: 304 // Radio is explictly powered off because the device is in airplane mode. 305 306 // TODO: Offer the option to turn the radio on, and automatically retry the call 307 // once network registration is complete. 308 309 if (ImsUtil.shouldPromoteWfc(context)) { 310 resourceId = R.string.incall_error_promote_wfc; 311 } else if (ImsUtil.isWfcModeWifiOnly(context)) { 312 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 313 } else if (ImsUtil.isWfcEnabled(context)) { 314 resourceId = R.string.incall_error_power_off_wfc; 315 } else { 316 resourceId = R.string.incall_error_power_off; 317 } 318 break; 319 320 case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY: 321 // Only emergency calls are allowed when in emergency callback mode. 322 resourceId = R.string.incall_error_ecm_emergency_only; 323 break; 324 325 case android.telephony.DisconnectCause.EMERGENCY_ONLY: 326 // Only emergency numbers are allowed, but we tried to dial 327 // a non-emergency number. 328 resourceId = R.string.incall_error_emergency_only; 329 break; 330 331 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 332 // No network connection. 333 if (ImsUtil.shouldPromoteWfc(context)) { 334 resourceId = R.string.incall_error_promote_wfc; 335 } else if (ImsUtil.isWfcModeWifiOnly(context)) { 336 resourceId = R.string.incall_error_wfc_only_no_wireless_network; 337 } else if (ImsUtil.isWfcEnabled(context)) { 338 resourceId = R.string.incall_error_out_of_service_wfc; 339 } else { 340 resourceId = R.string.incall_error_out_of_service; 341 } 342 break; 343 344 case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 345 // The supplied Intent didn't contain a valid phone number. 346 // (This is rare and should only ever happen with broken 347 // 3rd-party apps.) For now just show a generic error. 348 resourceId = R.string.incall_error_no_phone_number_supplied; 349 break; 350 351 case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING: 352 // TODO: Need to bring up the "Missing Voicemail Number" dialog, which 353 // will ultimately take us to the Call Settings. 354 resourceId = R.string.incall_error_missing_voicemail_number; 355 break; 356 357 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 358 resourceId = R.string.callFailed_video_call_tty_enabled; 359 break; 360 361 case android.telephony.DisconnectCause.CALL_PULLED: 362 resourceId = R.string.callEnded_pulled; 363 break; 364 365 case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED: 366 resourceId = R.string.callFailed_maximum_reached; 367 368 case android.telephony.DisconnectCause.OUTGOING_CANCELED: 369 // We don't want to show any dialog for the canceled case since the call was 370 // either canceled by the user explicitly (end-call button pushed immediately) 371 // or some other app canceled the call and immediately issued a new CALL to 372 // replace it. 373 break; 374 375 case android.telephony.DisconnectCause.DATA_DISABLED: 376 resourceId = R.string.callFailed_data_disabled; 377 break; 378 379 case android.telephony.DisconnectCause.DATA_LIMIT_REACHED: 380 resourceId = R.string.callFailed_data_limit_reached; 381 break; 382 383 default: 384 break; 385 } 386 return resourceId == null ? "" : context.getResources().getString(resourceId); 387 } 388 toTelecomDisconnectReason(int telephonyDisconnectCause, String reason)389 private static String toTelecomDisconnectReason(int telephonyDisconnectCause, String reason) { 390 String causeAsString = android.telephony.DisconnectCause.toString(telephonyDisconnectCause); 391 if (reason == null) { 392 return causeAsString; 393 } else { 394 return reason + ", " + causeAsString; 395 } 396 } 397 398 /** 399 * Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played. 400 */ toTelecomDisconnectCauseTone(int telephonyDisconnectCause)401 private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause) { 402 switch (telephonyDisconnectCause) { 403 case android.telephony.DisconnectCause.BUSY: 404 return ToneGenerator.TONE_SUP_BUSY; 405 406 case android.telephony.DisconnectCause.CONGESTION: 407 return ToneGenerator.TONE_SUP_CONGESTION; 408 409 case android.telephony.DisconnectCause.CDMA_REORDER: 410 return ToneGenerator.TONE_CDMA_REORDER; 411 412 case android.telephony.DisconnectCause.CDMA_INTERCEPT: 413 return ToneGenerator.TONE_CDMA_ABBR_INTERCEPT; 414 415 case android.telephony.DisconnectCause.CDMA_DROP: 416 case android.telephony.DisconnectCause.OUT_OF_SERVICE: 417 return ToneGenerator.TONE_CDMA_CALLDROP_LITE; 418 419 case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER: 420 return ToneGenerator.TONE_SUP_ERROR; 421 422 case android.telephony.DisconnectCause.ERROR_UNSPECIFIED: 423 case android.telephony.DisconnectCause.LOCAL: 424 case android.telephony.DisconnectCause.NORMAL: 425 case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED: 426 return ToneGenerator.TONE_PROP_PROMPT; 427 428 case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY: 429 // Do not play any tones if disconnected because of a successful merge. 430 default: 431 return ToneGenerator.TONE_UNKNOWN; 432 } 433 } 434 } 435