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.os.PersistableBundle;
22 import android.provider.Settings;
23 import android.telecom.DisconnectCause;
24 import android.telephony.CarrierConfigManager;
25 import android.telephony.SubscriptionManager;
26 
27 import com.android.internal.telephony.CallFailCause;
28 import com.android.internal.telephony.Phone;
29 import com.android.internal.telephony.PhoneFactory;
30 import com.android.phone.ImsUtil;
31 import com.android.phone.PhoneGlobals;
32 import com.android.phone.common.R;
33 
34 public class DisconnectCauseUtil {
35 
36    /**
37     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
38     * generic {@link android.telecom.DisconnectCause} object, possibly populated with a localized
39     * message and tone.
40     *
41     * @param telephonyDisconnectCause The code for the reason for the disconnect.
42     */
toTelecomDisconnectCause(int telephonyDisconnectCause)43     public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause) {
44         return toTelecomDisconnectCause(telephonyDisconnectCause,
45                 CallFailCause.NOT_VALID, null /* reason */);
46     }
47 
48    /**
49     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
50     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
51     * message and tone.
52     *
53     * @param telephonyDisconnectCause The code for the reason for the disconnect.
54     * @param reason Description of the reason for the disconnect, not intended for the user to see..
55     */
toTelecomDisconnectCause( int telephonyDisconnectCause, String reason)56     public static DisconnectCause toTelecomDisconnectCause(
57             int telephonyDisconnectCause, String reason) {
58         return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID, reason);
59     }
60 
61    /**
62     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
63     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
64     * message and tone.
65     *
66     * @param telephonyDisconnectCause The code for the reason for the disconnect.
67     * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
68     * @param reason Description of the reason for the disconnect, not intended for the user to see..
69     */
toTelecomDisconnectCause( int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason)70     public static DisconnectCause toTelecomDisconnectCause(
71             int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason) {
72         return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPerciseDisconnectCause,
73                 reason, SubscriptionManager.getDefaultVoicePhoneId());
74     }
75 
76     /**
77      * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
78      * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
79      * message and tone for Slot.
80      *
81      * @param telephonyDisconnectCause The code for the reason for the disconnect.
82      * @param phoneId To support localized message based on phoneId
83      */
toTelecomDisconnectCause(int telephonyDisconnectCause, String reason, int phoneId)84     public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
85             String reason, int phoneId) {
86         return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID,
87                 reason, phoneId);
88     }
89 
90    /**
91     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
92     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
93     * message and tone for Slot.
94     *
95     * @param telephonyDisconnectCause The code for the reason for the disconnect.
96     * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
97     * @param reason Description of the reason for the disconnect, not intended for the user to see..
98     * @param phoneId To support localized message based on phoneId
99     */
toTelecomDisconnectCause( int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason, int phoneId)100     public static DisconnectCause toTelecomDisconnectCause(
101             int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason,
102             int phoneId) {
103         Context context = PhoneGlobals.getInstance();
104         return new DisconnectCause(
105                 toTelecomDisconnectCauseCode(telephonyDisconnectCause),
106                 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
107                         telephonyPerciseDisconnectCause),
108                 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
109                 toTelecomDisconnectReason(context,telephonyDisconnectCause, reason, phoneId),
110                 toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId));
111     }
112 
113     /**
114      * Convert the {@link android.telephony.DisconnectCause} disconnect code into a
115      * {@link android.telecom.DisconnectCause} disconnect code.
116      * @return The disconnect code as defined in {@link android.telecom.DisconnectCause}.
117      */
toTelecomDisconnectCauseCode(int telephonyDisconnectCause)118     private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) {
119         switch (telephonyDisconnectCause) {
120             case android.telephony.DisconnectCause.LOCAL:
121             //  The call was still disconnected locally, so this is not an error condition.
122             case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
123                 return DisconnectCause.LOCAL;
124 
125             case android.telephony.DisconnectCause.NORMAL:
126             case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED:
127                 return DisconnectCause.REMOTE;
128 
129             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
130                 return DisconnectCause.CANCELED;
131 
132             case android.telephony.DisconnectCause.INCOMING_MISSED:
133                 return DisconnectCause.MISSED;
134 
135             case android.telephony.DisconnectCause.INCOMING_REJECTED:
136                 return DisconnectCause.REJECTED;
137 
138             case android.telephony.DisconnectCause.BUSY:
139                 return DisconnectCause.BUSY;
140 
141             case android.telephony.DisconnectCause.CALL_BARRED:
142             case android.telephony.DisconnectCause.CDMA_ACCESS_BLOCKED:
143             case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY:
144             case android.telephony.DisconnectCause.CS_RESTRICTED:
145             case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY:
146             case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL:
147             case android.telephony.DisconnectCause.EMERGENCY_ONLY:
148             case android.telephony.DisconnectCause.FDN_BLOCKED:
149             case android.telephony.DisconnectCause.LIMIT_EXCEEDED:
150             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
151                 return DisconnectCause.RESTRICTED;
152 
153             case android.telephony.DisconnectCause.CDMA_ACCESS_FAILURE:
154             case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
155             case android.telephony.DisconnectCause.CDMA_CALL_LOST:
156             case android.telephony.DisconnectCause.CDMA_DROP:
157             case android.telephony.DisconnectCause.CDMA_INTERCEPT:
158             case android.telephony.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE:
159             case android.telephony.DisconnectCause.CDMA_PREEMPTED:
160             case android.telephony.DisconnectCause.CDMA_REORDER:
161             case android.telephony.DisconnectCause.CDMA_RETRY_ORDER:
162             case android.telephony.DisconnectCause.CDMA_SO_REJECT:
163             case android.telephony.DisconnectCause.CONGESTION:
164             case android.telephony.DisconnectCause.ICC_ERROR:
165             case android.telephony.DisconnectCause.INVALID_CREDENTIALS:
166             case android.telephony.DisconnectCause.INVALID_NUMBER:
167             case android.telephony.DisconnectCause.LOST_SIGNAL:
168             case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
169             case android.telephony.DisconnectCause.NUMBER_UNREACHABLE:
170             case android.telephony.DisconnectCause.OUTGOING_FAILURE:
171             case android.telephony.DisconnectCause.OUT_OF_NETWORK:
172             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
173             case android.telephony.DisconnectCause.POWER_OFF:
174             case android.telephony.DisconnectCause.LOW_BATTERY:
175             case android.telephony.DisconnectCause.DIAL_LOW_BATTERY:
176             case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
177             case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
178             case android.telephony.DisconnectCause.SERVER_ERROR:
179             case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
180             case android.telephony.DisconnectCause.TIMED_OUT:
181             case android.telephony.DisconnectCause.ALREADY_DIALING:
182             case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING:
183             case android.telephony.DisconnectCause.CALLING_DISABLED:
184             case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
185             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
186             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
187             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
188             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
189             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
190             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
191             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO:
192             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS:
193             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD:
194             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL:
195             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
196             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
197             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
198             case android.telephony.DisconnectCause.DATA_DISABLED:
199             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
200             case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING:
201             case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED:
202             case android.telephony.DisconnectCause.WIFI_LOST:
203             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
204             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
205             case android.telephony.DisconnectCause.MEDIA_TIMEOUT:
206                 return DisconnectCause.ERROR;
207 
208             case android.telephony.DisconnectCause.DIALED_MMI:
209             case android.telephony.DisconnectCause.EXITED_ECM:
210             case android.telephony.DisconnectCause.MMI:
211             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
212                 return DisconnectCause.OTHER;
213 
214             case android.telephony.DisconnectCause.NOT_VALID:
215             case android.telephony.DisconnectCause.NOT_DISCONNECTED:
216                 return DisconnectCause.UNKNOWN;
217 
218             case android.telephony.DisconnectCause.CALL_PULLED:
219                 return DisconnectCause.CALL_PULLED;
220 
221             case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE:
222                 return DisconnectCause.ANSWERED_ELSEWHERE;
223 
224             default:
225                 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode",
226                         "Unrecognized Telephony DisconnectCause "
227                         + telephonyDisconnectCause);
228                 return DisconnectCause.UNKNOWN;
229         }
230     }
231 
232     /**
233      * Returns a label for to the disconnect cause to be shown to the user.
234      */
toTelecomDisconnectCauseLabel( Context context, int telephonyDisconnectCause, int telephonyPerciseDisconnectCause)235     private static CharSequence toTelecomDisconnectCauseLabel(
236             Context context, int telephonyDisconnectCause, int telephonyPerciseDisconnectCause) {
237         CharSequence label;
238         if (telephonyPerciseDisconnectCause != CallFailCause.NOT_VALID) {
239             label = getLabelFromPreciseDisconnectCause(context, telephonyPerciseDisconnectCause,
240                     telephonyDisconnectCause);
241         } else {
242             label = getLabelFromDisconnectCause(context, telephonyDisconnectCause);
243         }
244         return label;
245     }
246 
247     /**
248      * Returns a label for to the generic disconnect cause to be shown to the user.
249      */
getLabelFromDisconnectCause( Context context, int telephonyDisconnectCause)250     private static CharSequence getLabelFromDisconnectCause(
251             Context context, int telephonyDisconnectCause) {
252         if (context == null) {
253             return "";
254         }
255 
256         Integer resourceId = null;
257         switch (telephonyDisconnectCause) {
258             case android.telephony.DisconnectCause.BUSY:
259                 resourceId = R.string.callFailed_userBusy;
260                 break;
261 
262             case android.telephony.DisconnectCause.CDMA_REORDER:
263                 resourceId = R.string.callFailed_NetworkBusy;
264                 break;
265 
266             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
267                 resourceId = R.string.callFailed_NetworkCongested;
268                 break;
269 
270             case android.telephony.DisconnectCause.CONGESTION:
271                 resourceId = R.string.callFailed_congestion;
272                 break;
273 
274             case android.telephony.DisconnectCause.TIMED_OUT:
275                 resourceId = R.string.callFailed_timedOut;
276                 break;
277 
278             case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
279                 resourceId = R.string.callFailed_server_unreachable;
280                 break;
281 
282             case android.telephony.DisconnectCause.NUMBER_UNREACHABLE:
283                 resourceId = R.string.callFailed_number_unreachable;
284                 break;
285 
286             case android.telephony.DisconnectCause.INVALID_CREDENTIALS:
287                 resourceId = R.string.callFailed_invalid_credentials;
288                 break;
289 
290             case android.telephony.DisconnectCause.SERVER_ERROR:
291                 resourceId = R.string.callFailed_server_error;
292                 break;
293 
294             case android.telephony.DisconnectCause.OUT_OF_NETWORK:
295                 resourceId = R.string.callFailed_out_of_network;
296                 break;
297 
298             case android.telephony.DisconnectCause.LOST_SIGNAL:
299             case android.telephony.DisconnectCause.CDMA_DROP:
300                 resourceId = R.string.callFailed_noSignal;
301                 break;
302 
303             case android.telephony.DisconnectCause.LIMIT_EXCEEDED:
304                 resourceId = R.string.callFailed_limitExceeded;
305                 break;
306 
307             case android.telephony.DisconnectCause.POWER_OFF:
308                 resourceId = R.string.callFailed_powerOff;
309                 break;
310 
311             case android.telephony.DisconnectCause.LOW_BATTERY:
312                 resourceId = R.string.callFailed_low_battery;
313                 break;
314 
315             case android.telephony.DisconnectCause.DIAL_LOW_BATTERY:
316                 resourceId = R.string.dialFailed_low_battery;
317                 break;
318 
319             case android.telephony.DisconnectCause.ICC_ERROR:
320                 resourceId = R.string.callFailed_simError;
321                 break;
322 
323             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
324                 resourceId = R.string.callFailed_outOfService;
325                 break;
326 
327             case android.telephony.DisconnectCause.INVALID_NUMBER:
328             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
329                 resourceId = R.string.callFailed_unobtainable_number;
330                 break;
331 
332             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
333                 resourceId = R.string.incall_error_missing_voicemail_number;
334                 break;
335 
336             case android.telephony.DisconnectCause.CALL_PULLED:
337                 resourceId = R.string.callEnded_pulled;
338                 break;
339 
340             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
341                 resourceId = R.string.callFailed_maximum_reached;
342                 break;
343 
344             case android.telephony.DisconnectCause.DATA_DISABLED:
345                 resourceId = R.string.callFailed_data_disabled;
346                 break;
347 
348             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
349                 resourceId = R.string.callFailed_data_limit_reached;
350                 break;
351             case android.telephony.DisconnectCause.WIFI_LOST:
352                 resourceId = R.string.callFailed_wifi_lost;
353                 break;
354             case android.telephony.DisconnectCause.ALREADY_DIALING:
355                 resourceId = R.string.callFailed_already_dialing;
356                 break;
357             case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING:
358                 resourceId = R.string.callFailed_already_ringing;
359                 break;
360             case android.telephony.DisconnectCause.CALLING_DISABLED:
361                 resourceId = R.string.callFailed_calling_disabled;
362                 break;
363             case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
364                 resourceId = R.string.callFailed_too_many_calls;
365                 break;
366             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
367                 int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
368                         Settings.Global.AIRPLANE_MODE_ON, 0);
369                 resourceId = R.string.incall_error_call_failed;
370                 if (airplaneMode != 0) {
371                     resourceId = R.string.incall_error_power_off;
372                 }
373                 break;
374             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
375                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
376                 break;
377             case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
378                 resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
379                 break;
380             case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
381                 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
382                 break;
383             default:
384                 break;
385         }
386         return resourceId == null ? "" : context.getResources().getString(resourceId);
387     }
388 
389     /**
390      * Returns a label for to the precise disconnect cause to be shown to the user.
391      */
getLabelFromPreciseDisconnectCause( Context context, int telephonyPreciseDisconnectCause, int telephonyDisconnectCause)392     private static CharSequence getLabelFromPreciseDisconnectCause(
393             Context context, int telephonyPreciseDisconnectCause, int telephonyDisconnectCause) {
394         if (context == null) {
395             return "";
396         }
397 
398         Integer resourceId = null;
399         switch (telephonyPreciseDisconnectCause) {
400             case CallFailCause.UNOBTAINABLE_NUMBER:
401                 resourceId = R.string.clh_callFailed_unassigned_number_txt;
402                 break;
403             case CallFailCause.NO_ROUTE_TO_DEST:
404                 resourceId = R.string.clh_callFailed_no_route_to_destination_txt;
405                 break;
406             case CallFailCause.CHANNEL_UNACCEPTABLE:
407                 resourceId = R.string.clh_callFailed_channel_unacceptable_txt;
408                 break;
409             case CallFailCause.OPERATOR_DETERMINED_BARRING:
410                 resourceId = R.string.clh_callFailed_operator_determined_barring_txt;
411                 break;
412             case CallFailCause.NORMAL_CLEARING:
413                 resourceId = R.string.clh_callFailed_normal_call_clearing_txt;
414                 break;
415             case CallFailCause.USER_BUSY:
416                 resourceId = R.string.clh_callFailed_user_busy_txt;
417                 break;
418             case CallFailCause.NO_USER_RESPONDING:
419                 resourceId = R.string.clh_callFailed_no_user_responding_txt;
420                 break;
421             case CallFailCause.USER_ALERTING_NO_ANSWER:
422                 resourceId = R.string.clh_callFailed_user_alerting_txt;
423                 break;
424             case CallFailCause.CALL_REJECTED:
425                 resourceId = R.string.clh_callFailed_call_rejected_txt;
426                 break;
427             case CallFailCause.NUMBER_CHANGED:
428                 resourceId = R.string.clh_callFailed_number_changed_txt;
429                 break;
430             case CallFailCause.PRE_EMPTION:
431                 resourceId = R.string.clh_callFailed_pre_emption_txt;
432                 break;
433             case CallFailCause.NON_SELECTED_USER_CLEARING:
434                 resourceId = R.string.clh_callFailed_non_selected_user_clearing_txt;
435                 break;
436             case CallFailCause.DESTINATION_OUT_OF_ORDER:
437                 resourceId = R.string.clh_callFailed_destination_out_of_order_txt;
438                 break;
439             case CallFailCause.INVALID_NUMBER_FORMAT:
440                 resourceId = R.string.clh_callFailed_invalid_number_format_txt;
441                 break;
442             case CallFailCause.FACILITY_REJECTED:
443                 resourceId = R.string.clh_callFailed_facility_rejected_txt;
444                 break;
445             case CallFailCause.STATUS_ENQUIRY:
446                 resourceId = R.string.clh_callFailed_response_to_STATUS_ENQUIRY_txt;
447                 break;
448             case CallFailCause.NORMAL_UNSPECIFIED:
449                 resourceId = R.string.clh_callFailed_normal_unspecified_txt;
450                 break;
451             case CallFailCause.NO_CIRCUIT_AVAIL:
452                 resourceId = R.string.clh_callFailed_no_circuit_available_txt;
453                 break;
454             case CallFailCause.NETWORK_OUT_OF_ORDER:
455                 resourceId = R.string.clh_callFailed_network_out_of_order_txt;
456                 break;
457             case CallFailCause.TEMPORARY_FAILURE:
458                 resourceId = R.string.clh_callFailed_temporary_failure_txt;
459                 break;
460             case CallFailCause.SWITCHING_CONGESTION:
461                 resourceId = R.string.clh_callFailed_switching_equipment_congestion_txt;
462                 break;
463             case CallFailCause.ACCESS_INFORMATION_DISCARDED:
464                 resourceId = R.string.clh_callFailed_access_information_discarded_txt;
465                 break;
466             case CallFailCause.CHANNEL_NOT_AVAIL:
467                 resourceId = R.string.clh_callFailed_requested_circuit_txt;
468                 break;
469             case CallFailCause.RESOURCES_UNAVAILABLE_UNSPECIFIED:
470                 resourceId = R.string.clh_callFailed_resources_unavailable_unspecified_txt;
471                 break;
472             case CallFailCause.QOS_NOT_AVAIL:
473                 resourceId = R.string.clh_callFailed_quality_of_service_unavailable_txt;
474                 break;
475             case CallFailCause.REQUESTED_FACILITY_NOT_SUBSCRIBED:
476                 resourceId = R.string.clh_callFailed_requested_facility_not_subscribed_txt;
477                 break;
478             case CallFailCause.INCOMING_CALL_BARRED_WITHIN_CUG:
479                 resourceId = R.string.clh_callFailed_incoming_calls_barred_within_the_CUG_txt;
480                 break;
481             case CallFailCause.BEARER_CAPABILITY_NOT_AUTHORISED:
482                 resourceId = R.string.clh_callFailed_bearer_capability_not_authorized_txt;
483                 break;
484             case CallFailCause.BEARER_NOT_AVAIL:
485                 resourceId = R.string.clh_callFailed_bearer_capability_not_presently_available_txt;
486                 break;
487             case CallFailCause.SERVICE_OR_OPTION_NOT_AVAILABLE:
488                 resourceId =
489                         R.string.clh_callFailed_service_or_option_not_available_unspecified_txt;
490                 break;
491             case CallFailCause.BEARER_SERVICE_NOT_IMPLEMENTED:
492                 resourceId = R.string.clh_callFailed_bearer_service_not_implemented_txt;
493                 break;
494             case CallFailCause.ACM_LIMIT_EXCEEDED:
495                 resourceId = R.string.clh_callFailed_ACM_equal_to_or_greater_than_ACMmax_txt;
496                 break;
497             case CallFailCause.REQUESTED_FACILITY_NOT_IMPLEMENTED:
498                 resourceId = R.string.clh_callFailed_requested_facility_not_implemented_txt;
499                 break;
500             case CallFailCause.ONLY_RESTRICTED_DIGITAL_INFO_BC_AVAILABLE:
501                 resourceId = R.string
502                         .clh_callFailed_only_restricted_digital_information_bearer_capability_is_available_txt;
503                 break;
504             case CallFailCause.SERVICE_OR_OPTION_NOT_IMPLEMENTED:
505                 resourceId =
506                         R.string.clh_callFailed_service_or_option_not_implemented_unspecified_txt;
507                 break;
508             case CallFailCause.INVALID_TRANSACTION_ID_VALUE:
509                 resourceId = R.string.clh_callFailed_invalid_transaction_identifier_value_txt;
510                 break;
511             case CallFailCause.USER_NOT_MEMBER_OF_CUG:
512                 resourceId = R.string.clh_callFailed_user_not_member_of_CUG_txt;
513                 break;
514             case CallFailCause.INCOMPATIBLE_DESTINATION:
515                 resourceId = R.string.clh_callFailed_incompatible_destination_txt;
516                 break;
517             case CallFailCause.INVALID_TRANSIT_NETWORK_SELECTION:
518                 resourceId = R.string.clh_callFailed_invalid_transit_network_selection_txt;
519                 break;
520             case CallFailCause.SEMANTICALLY_INCORRECT_MESSAGE:
521                 resourceId = R.string.clh_callFailed_semantically_incorrect_message_txt;
522                 break;
523             case CallFailCause.INVALID_MANDATORY_INFORMATION:
524                 resourceId = R.string.clh_callFailed_invalid_mandatory_information_txt;
525                 break;
526             case CallFailCause.MESSAGE_TYPE_NON_EXISTENT:
527                 resourceId =
528                         R.string.clh_callFailed_message_type_non_existent_or_not_implemented_txt;
529                 break;
530             case CallFailCause.MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROT_STATE:
531                 resourceId = R.string
532                         .clh_callFailed_message_type_not_compatible_with_protocol_state_txt;
533                 break;
534             case CallFailCause.IE_NON_EXISTENT_OR_NOT_IMPLEMENTED:
535                 resourceId = R.string
536                         .clh_callFailed_information_element_non_existent_or_not_implemented_txt;
537                 break;
538             case CallFailCause.CONDITIONAL_IE_ERROR:
539                 resourceId = R.string.clh_callFailed_conditional_IE_error_txt;
540                 break;
541             case CallFailCause.MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE:
542                 resourceId = R.string.clh_callFailed_message_not_compatible_with_protocol_state_txt;
543                 break;
544             case CallFailCause.RECOVERY_ON_TIMER_EXPIRY:
545                 resourceId = R.string.clh_callFailed_recovery_on_timer_expiry_txt;
546                 break;
547             case CallFailCause.PROTOCOL_ERROR_UNSPECIFIED:
548                 resourceId = R.string.clh_callFailed_protocol_Error_unspecified_txt;
549                 break;
550             case CallFailCause.INTERWORKING_UNSPECIFIED:
551                 resourceId = R.string.clh_callFailed_interworking_unspecified_txt;
552                 break;
553             default:
554                 switch (telephonyDisconnectCause) {
555                     case android.telephony.DisconnectCause.POWER_OFF:
556                         resourceId = R.string.clh_callFailed_powerOff_txt;
557                         break;
558                     case android.telephony.DisconnectCause.ICC_ERROR:
559                         resourceId = R.string.clh_callFailed_simError_txt;
560                         break;
561                     case android.telephony.DisconnectCause.OUT_OF_SERVICE:
562                         resourceId = R.string.clh_incall_error_out_of_service_txt;
563                         break;
564                     default:
565                         resourceId = R.string.clh_card_title_call_ended_txt;
566                         break;
567                 }
568                 break;
569         }
570         return context.getResources().getString(resourceId);
571     }
572 
573     /**
574      * Returns a description of the disconnect cause to be shown to the user.
575      */
toTelecomDisconnectCauseDescription( Context context, int telephonyDisconnectCause, int phoneId)576     private static CharSequence toTelecomDisconnectCauseDescription(
577             Context context, int telephonyDisconnectCause, int phoneId) {
578         if (context == null ) {
579             return "";
580         }
581 
582         Integer resourceId = null;
583         switch (telephonyDisconnectCause) {
584             case android.telephony.DisconnectCause.CALL_BARRED:
585                 resourceId = R.string.callFailed_cb_enabled;
586                 break;
587 
588             case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
589                 resourceId = R.string.callFailed_cdma_activation;
590                 break;
591 
592             case android.telephony.DisconnectCause.CDMA_REORDER:
593                 resourceId = R.string.callFailed_NetworkBusy;
594                 break;
595 
596             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
597                 resourceId = R.string.callFailed_NetworkCongested;
598                 break;
599 
600             case android.telephony.DisconnectCause.FDN_BLOCKED:
601                 resourceId = R.string.callFailed_fdn_only;
602                 break;
603 
604             case android.telephony.DisconnectCause.CS_RESTRICTED:
605                 resourceId = R.string.callFailed_dsac_restricted;
606                 break;
607 
608             case android.telephony.DisconnectCause.CS_RESTRICTED_EMERGENCY:
609                 resourceId = R.string.callFailed_dsac_restricted_emergency;
610                 break;
611 
612             case android.telephony.DisconnectCause.CS_RESTRICTED_NORMAL:
613                 resourceId = R.string.callFailed_dsac_restricted_normal;
614                 break;
615 
616             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
617                 resourceId = R.string.callFailed_dialToUssd;
618                 break;
619 
620             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
621                 resourceId = R.string.callFailed_dialToSs;
622                 break;
623 
624             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
625                 resourceId = R.string.callFailed_dialToDial;
626                 break;
627 
628             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO:
629                 resourceId = R.string.callFailed_dialToDialVideo;
630                 break;
631 
632             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS:
633                 resourceId = R.string.callFailed_dialVideoToSs;
634                 break;
635 
636             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD:
637                 resourceId = R.string.callFailed_dialVideoToUssd;
638                 break;
639 
640             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL:
641                 resourceId = R.string.callFailed_dialVideoToDial;
642                 break;
643 
644             case android.telephony.DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
645                 resourceId = R.string.callFailed_dialVideoToDialVideo;
646                 break;
647 
648             case android.telephony.DisconnectCause.OUTGOING_FAILURE:
649                 // We couldn't successfully place the call; there was some
650                 // failure in the telephony layer.
651                 // TODO: Need UI spec for this failure case; for now just
652                 // show a generic error.
653                 resourceId = R.string.incall_error_call_failed;
654                 break;
655 
656             case android.telephony.DisconnectCause.POWER_OFF:
657                 // Radio is explictly powered off because the device is in airplane mode.
658 
659                 // TODO: Offer the option to turn the radio on, and automatically retry the call
660                 // once network registration is complete.
661 
662                 if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
663                     resourceId = R.string.incall_error_promote_wfc;
664                 } else if (ImsUtil.isWfcModeWifiOnly(context, phoneId)) {
665                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
666                 } else if (ImsUtil.isWfcEnabled(context, phoneId)) {
667                     resourceId = R.string.incall_error_power_off_wfc;
668                 } else {
669                     resourceId = R.string.incall_error_power_off;
670                 }
671                 break;
672 
673             case android.telephony.DisconnectCause.LOW_BATTERY:
674                 resourceId = R.string.callFailed_low_battery;
675                 break;
676 
677             case android.telephony.DisconnectCause.DIAL_LOW_BATTERY:
678                 resourceId = R.string.dialFailed_low_battery;
679                 break;
680 
681             case android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY:
682                 // Only emergency calls are allowed when in emergency callback mode.
683                 resourceId = R.string.incall_error_ecm_emergency_only;
684                 break;
685 
686             case android.telephony.DisconnectCause.EMERGENCY_ONLY:
687                 // Only emergency numbers are allowed, but we tried to dial
688                 // a non-emergency number.
689                 resourceId = R.string.incall_error_emergency_only;
690                 break;
691 
692             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
693                 // No network connection.
694                 if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
695                     resourceId = R.string.incall_error_promote_wfc;
696                 } else if (ImsUtil.isWfcModeWifiOnly(context, phoneId)) {
697                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
698                 } else if (ImsUtil.isWfcEnabled(context, phoneId)) {
699                     resourceId = R.string.incall_error_out_of_service_wfc;
700                 } else {
701                     resourceId = R.string.incall_error_out_of_service;
702                 }
703                 break;
704 
705             case android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
706                 // The supplied Intent didn't contain a valid phone number.
707                 // (This is rare and should only ever happen with broken
708                 // 3rd-party apps.) For now just show a generic error.
709                 resourceId = R.string.incall_error_no_phone_number_supplied;
710                 break;
711 
712             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
713                 resourceId = R.string.incall_error_missing_voicemail_number;
714                 break;
715 
716             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
717                 resourceId = R.string.callFailed_video_call_tty_enabled;
718                 break;
719 
720             case android.telephony.DisconnectCause.CALL_PULLED:
721                 resourceId = R.string.callEnded_pulled;
722                 break;
723 
724             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
725                 resourceId = R.string.callFailed_maximum_reached;
726 
727             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
728                 // We don't want to show any dialog for the canceled case since the call was
729                 // either canceled by the user explicitly (end-call button pushed immediately)
730                 // or some other app canceled the call and immediately issued a new CALL to
731                 // replace it.
732                 break;
733 
734             case android.telephony.DisconnectCause.DATA_DISABLED:
735                 resourceId = R.string.callFailed_data_disabled;
736                 break;
737 
738             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
739                 resourceId = R.string.callFailed_data_limit_reached_description;
740                 break;
741             case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING:
742                 resourceId = com.android.internal.R.string.mmiErrorWhileRoaming;
743                 break;
744 
745             case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED:
746                 resourceId = R.string.callFailed_imei_not_accepted;
747                 break;
748 
749             case android.telephony.DisconnectCause.WIFI_LOST:
750                 resourceId = R.string.callFailed_wifi_lost;
751                 break;
752             case android.telephony.DisconnectCause.ALREADY_DIALING:
753                 resourceId = R.string.callFailed_already_dialing;
754                 break;
755             case android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING:
756                 resourceId = R.string.callFailed_already_ringing;
757                 break;
758             case android.telephony.DisconnectCause.CALLING_DISABLED:
759                 resourceId = R.string.callFailed_calling_disabled;
760                 break;
761             case android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS:
762                 resourceId = R.string.callFailed_too_many_calls;
763                 break;
764             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
765                 int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
766                         Settings.Global.AIRPLANE_MODE_ON, 0);
767                 resourceId = R.string.incall_error_call_failed;
768                 if (airplaneMode != 0) {
769                     resourceId = R.string.incall_error_power_off;
770                 }
771                 break;
772             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
773                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
774                 break;
775             case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
776                 resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
777                 break;
778             case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
779                 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
780                 break;
781             default:
782                 break;
783         }
784         return resourceId == null ? "" : context.getResources().getString(resourceId);
785     }
786 
787     /**
788      * Maps the telephony {@link android.telephony.DisconnectCause} into a reason string which is
789      * returned in the Telecom {@link DisconnectCause#getReason()}.
790      *
791      * @param context The current context.
792      * @param telephonyDisconnectCause The {@link android.telephony.DisconnectCause} code.
793      * @param reason A reason provided by the caller; only used if a more specific reason cannot
794      *               be determined here.
795      * @return The disconnect reason.
796      */
toTelecomDisconnectReason(Context context, int telephonyDisconnectCause, String reason, int phoneId)797     private static String toTelecomDisconnectReason(Context context, int telephonyDisconnectCause,
798             String reason, int phoneId) {
799 
800         if (context == null) {
801             return "";
802         }
803 
804         switch (telephonyDisconnectCause) {
805             case android.telephony.DisconnectCause.POWER_OFF:
806                 // Airplane mode (radio off)
807                 // intentional fall-through
808             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
809                 // No network connection.
810                 if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
811                     return android.telecom.DisconnectCause.REASON_WIFI_ON_BUT_WFC_OFF;
812                 }
813                 break;
814             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
815                 return DisconnectCause.REASON_IMS_ACCESS_BLOCKED;
816             case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
817                 return DisconnectCause.REASON_EMERGENCY_CALL_PLACED;
818         }
819 
820         // If no specific code-mapping found, then fall back to using the reason.
821         String causeAsString = android.telephony.DisconnectCause.toString(telephonyDisconnectCause);
822         if (reason == null) {
823             return causeAsString;
824         } else {
825             return reason + ", " + causeAsString;
826         }
827     }
828 
829     /**
830      * Returns the tone to play for the disconnect cause, or UNKNOWN if none should be played.
831      */
toTelecomDisconnectCauseTone(int telephonyDisconnectCause, int phoneId)832     private static int toTelecomDisconnectCauseTone(int telephonyDisconnectCause, int phoneId) {
833         Phone phone = PhoneFactory.getPhone(phoneId);
834         PersistableBundle config;
835         if (phone != null) {
836             config = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
837         } else {
838             config = PhoneGlobals.getInstance().getCarrierConfig();
839         }
840         int[] busyToneArray = config.getIntArray(
841                 CarrierConfigManager.KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY);
842         for (int busyTone : busyToneArray) {
843             if (busyTone == telephonyDisconnectCause) {
844                 return ToneGenerator.TONE_SUP_BUSY;
845             }
846         }
847         switch (telephonyDisconnectCause) {
848             case android.telephony.DisconnectCause.CONGESTION:
849                 return ToneGenerator.TONE_SUP_CONGESTION;
850 
851             case android.telephony.DisconnectCause.CDMA_REORDER:
852                 return ToneGenerator.TONE_CDMA_REORDER;
853 
854             case android.telephony.DisconnectCause.CDMA_INTERCEPT:
855                 return ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
856 
857             case android.telephony.DisconnectCause.CDMA_DROP:
858             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
859                 return ToneGenerator.TONE_CDMA_CALLDROP_LITE;
860 
861             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
862                 return ToneGenerator.TONE_SUP_ERROR;
863 
864             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
865                 // Do not play any tones if disconnected because of a successful merge.
866                 return -1;
867 
868             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
869             case android.telephony.DisconnectCause.LOCAL:
870             case android.telephony.DisconnectCause.NORMAL:
871             case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED:
872             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
873             case android.telephony.DisconnectCause.WIFI_LOST:
874             default:
875                 return ToneGenerator.TONE_PROP_PROMPT;
876         }
877     }
878 }
879