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