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