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