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