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