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