• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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