Add an API to get recent IMS disconnect reasons am: 525c3a2fdc am: ac99f22a28 am...
[android/platform/frameworks/opt/net/ims.git] / src / java / com / android / ims / ImsManager.java
1 /*
2  * Copyright (c) 2013 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.ims;
18
19 import android.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.os.IBinder;
25 import android.os.Message;
26 import android.os.Parcel;
27 import android.os.PersistableBundle;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.SystemProperties;
31 import android.provider.Settings;
32 import android.telecom.TelecomManager;
33 import android.telephony.CarrierConfigManager;
34 import android.telephony.Rlog;
35 import android.telephony.ServiceState;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38
39 import com.android.ims.internal.IImsCallSession;
40 import com.android.ims.internal.IImsConfig;
41 import com.android.ims.internal.IImsEcbm;
42 import com.android.ims.internal.IImsMultiEndpoint;
43 import com.android.ims.internal.IImsRegistrationListener;
44 import com.android.ims.internal.IImsService;
45 import com.android.ims.internal.IImsUt;
46 import com.android.ims.internal.ImsCallSession;
47
48 import java.io.FileDescriptor;
49 import java.io.PrintWriter;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.concurrent.ConcurrentLinkedDeque;
53
54 /**
55  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
56  * the operator's IMS network. This class is the starting point for any IMS actions.
57  * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
58  * <p>The APIs in this class allows you to:</p>
59  *
60  * @hide
61  */
62 public class ImsManager {
63
64     /*
65      * Debug flag to override configuration flag
66      */
67     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
68     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
69     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
70     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
71     public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
72     public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
73     public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
74     public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
75
76     /**
77      * For accessing the IMS related service.
78      * Internal use only.
79      * @hide
80      */
81     private static final String IMS_SERVICE = "ims";
82
83     /**
84      * The result code to be sent back with the incoming call {@link PendingIntent}.
85      * @see #open(PendingIntent, ImsConnectionStateListener)
86      */
87     public static final int INCOMING_CALL_RESULT_CODE = 101;
88
89     /**
90      * Key to retrieve the call ID from an incoming call intent.
91      * @see #open(PendingIntent, ImsConnectionStateListener)
92      */
93     public static final String EXTRA_CALL_ID = "android:imsCallID";
94
95     /**
96      * Action to broadcast when ImsService is up.
97      * Internal use only.
98      * @hide
99      */
100     public static final String ACTION_IMS_SERVICE_UP =
101             "com.android.ims.IMS_SERVICE_UP";
102
103     /**
104      * Action to broadcast when ImsService is down.
105      * Internal use only.
106      * @hide
107      */
108     public static final String ACTION_IMS_SERVICE_DOWN =
109             "com.android.ims.IMS_SERVICE_DOWN";
110
111     /**
112      * Action to broadcast when ImsService registration fails.
113      * Internal use only.
114      * @hide
115      */
116     public static final String ACTION_IMS_REGISTRATION_ERROR =
117             "com.android.ims.REGISTRATION_ERROR";
118
119     /**
120      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
121      * A long value; the phone ID corresponding to the IMS service coming up or down.
122      * Internal use only.
123      * @hide
124      */
125     public static final String EXTRA_PHONE_ID = "android:phone_id";
126
127     /**
128      * Action for the incoming call intent for the Phone app.
129      * Internal use only.
130      * @hide
131      */
132     public static final String ACTION_IMS_INCOMING_CALL =
133             "com.android.ims.IMS_INCOMING_CALL";
134
135     /**
136      * Part of the ACTION_IMS_INCOMING_CALL intents.
137      * An integer value; service identifier obtained from {@link ImsManager#open}.
138      * Internal use only.
139      * @hide
140      */
141     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
142
143     /**
144      * Part of the ACTION_IMS_INCOMING_CALL intents.
145      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
146      * The value "true" indicates that the incoming call is for USSD.
147      * Internal use only.
148      * @hide
149      */
150     public static final String EXTRA_USSD = "android:ussd";
151
152     /**
153      * Part of the ACTION_IMS_INCOMING_CALL intents.
154      * A boolean value; Flag to indicate whether the call is an unknown
155      * dialing call. Such calls are originated by sending commands (like
156      * AT commands) directly to modem without Android involvement.
157      * Even though they are not incoming calls, they are propagated
158      * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
159      * Internal use only.
160      * @hide
161      */
162     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
163
164     private static final String TAG = "ImsManager";
165     private static final boolean DBG = true;
166
167     private static HashMap<Integer, ImsManager> sImsManagerInstances =
168             new HashMap<Integer, ImsManager>();
169
170     private Context mContext;
171     private int mPhoneId;
172     private IImsService mImsService = null;
173     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
174     // Ut interface for the supplementary service configuration
175     private ImsUt mUt = null;
176     // Interface to get/set ims config items
177     private ImsConfig mConfig = null;
178     private boolean mConfigUpdated = false;
179
180     private ImsConfigListener mImsConfigListener;
181
182     // ECBM interface
183     private ImsEcbm mEcbm = null;
184
185     private ImsMultiEndpoint mMultiEndpoint = null;
186
187     // SystemProperties used as cache
188     private static final String VOLTE_PROVISIONED_PROP = "net.lte.ims.volte.provisioned";
189     private static final String WFC_PROVISIONED_PROP = "net.lte.ims.wfc.provisioned";
190     private static final String VT_PROVISIONED_PROP = "net.lte.ims.vt.provisioned";
191     // Flag indicating data enabled or not. This flag should be in sync with
192     // DcTracker.isDataEnabled(). The flag will be set later during boot up.
193     private static final String DATA_ENABLED_PROP = "net.lte.ims.data.enabled";
194
195     public static final String TRUE = "true";
196     public static final String FALSE = "false";
197
198     // mRecentDisconnectReasons stores the last 16 disconnect reasons
199     private static final int MAX_RECENT_DISCONNECT_REASONS = 16;
200     private ConcurrentLinkedDeque<ImsReasonInfo> mRecentDisconnectReasons =
201             new ConcurrentLinkedDeque<>();
202
203     /**
204      * Gets a manager instance.
205      *
206      * @param context application context for creating the manager object
207      * @param phoneId the phone ID for the IMS Service
208      * @return the manager instance corresponding to the phoneId
209      */
210     public static ImsManager getInstance(Context context, int phoneId) {
211         synchronized (sImsManagerInstances) {
212             if (sImsManagerInstances.containsKey(phoneId)) {
213                 return sImsManagerInstances.get(phoneId);
214             }
215
216             ImsManager mgr = new ImsManager(context, phoneId);
217             sImsManagerInstances.put(phoneId, mgr);
218
219             return mgr;
220         }
221     }
222
223     /**
224      * Returns the user configuration of Enhanced 4G LTE Mode setting
225      */
226     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
227         // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
228         // If user changes SIM from editable mode to uneditable mode, need to return true.
229         if (!getBooleanCarrierConfig(context,
230                     CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
231             return true;
232         }
233         int enabled = android.provider.Settings.Global.getInt(
234                     context.getContentResolver(),
235                     android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
236         return (enabled == 1) ? true : false;
237     }
238
239     /**
240      * Change persistent Enhanced 4G LTE Mode setting
241      */
242     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
243         int value = enabled ? 1 : 0;
244         android.provider.Settings.Global.putInt(
245                 context.getContentResolver(),
246                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
247
248         if (isNonTtyOrTtyOnVolteEnabled(context)) {
249             ImsManager imsManager = ImsManager.getInstance(context,
250                     SubscriptionManager.getDefaultVoicePhoneId());
251             if (imsManager != null) {
252                 try {
253                     imsManager.setAdvanced4GMode(enabled);
254                 } catch (ImsException ie) {
255                     // do nothing
256                 }
257             }
258         }
259     }
260
261     /**
262      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
263      * supported.
264      */
265     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
266         if (getBooleanCarrierConfig(context,
267                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
268             return true;
269         }
270
271         return Settings.Secure.getInt(context.getContentResolver(),
272                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
273                 == TelecomManager.TTY_MODE_OFF;
274     }
275
276     /**
277      * Returns a platform configuration for VoLTE which may override the user setting.
278      */
279     public static boolean isVolteEnabledByPlatform(Context context) {
280         if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
281                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
282             return true;
283         }
284
285         return context.getResources().getBoolean(
286                 com.android.internal.R.bool.config_device_volte_available)
287                 && getBooleanCarrierConfig(context,
288                         CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
289                 && isGbaValid(context);
290     }
291
292     /**
293      * Indicates whether VoLTE is provisioned on device
294      */
295     public static boolean isVolteProvisionedOnDevice(Context context) {
296         if (getBooleanCarrierConfig(context,
297                     CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
298             ImsManager mgr = ImsManager.getInstance(context,
299                     SubscriptionManager.getDefaultVoicePhoneId());
300             if (mgr != null) {
301                 return mgr.isVolteProvisioned();
302             }
303         }
304
305         return true;
306     }
307
308     /**
309      * Indicates whether VoWifi is provisioned on device
310      */
311     public static boolean isWfcProvisionedOnDevice(Context context) {
312         if (getBooleanCarrierConfig(context,
313                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
314             ImsManager mgr = ImsManager.getInstance(context,
315                     SubscriptionManager.getDefaultVoicePhoneId());
316             if (mgr != null) {
317                 return mgr.isWfcProvisioned();
318             }
319         }
320
321         return true;
322     }
323
324     /**
325      * Indicates whether VT is provisioned on device
326      */
327     public static boolean isVtProvisionedOnDevice(Context context) {
328         if (getBooleanCarrierConfig(context,
329                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
330             ImsManager mgr = ImsManager.getInstance(context,
331                     SubscriptionManager.getDefaultVoicePhoneId());
332             if (mgr != null) {
333                 return mgr.isVtProvisioned();
334             }
335         }
336
337         return true;
338     }
339
340     /**
341      * Returns a platform configuration for VT which may override the user setting.
342      *
343      * Note: VT presumes that VoLTE is enabled (these are configuration settings
344      * which must be done correctly).
345      */
346     public static boolean isVtEnabledByPlatform(Context context) {
347         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
348                 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
349             return true;
350         }
351
352         return
353                 context.getResources().getBoolean(
354                         com.android.internal.R.bool.config_device_vt_available) &&
355                 getBooleanCarrierConfig(context,
356                         CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
357                 isGbaValid(context);
358     }
359
360     /**
361      * Returns the user configuration of VT setting
362      */
363     public static boolean isVtEnabledByUser(Context context) {
364         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
365                 android.provider.Settings.Global.VT_IMS_ENABLED,
366                 ImsConfig.FeatureValueConstants.ON);
367         return (enabled == 1) ? true : false;
368     }
369
370     /**
371      * Change persistent VT enabled setting
372      */
373     public static void setVtSetting(Context context, boolean enabled) {
374         int value = enabled ? 1 : 0;
375         android.provider.Settings.Global.putInt(context.getContentResolver(),
376                 android.provider.Settings.Global.VT_IMS_ENABLED, value);
377
378         ImsManager imsManager = ImsManager.getInstance(context,
379                 SubscriptionManager.getDefaultVoicePhoneId());
380         if (imsManager != null) {
381             try {
382                 ImsConfig config = imsManager.getConfigInterface();
383                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
384                         TelephonyManager.NETWORK_TYPE_LTE,
385                         enabled ? ImsConfig.FeatureValueConstants.ON
386                                 : ImsConfig.FeatureValueConstants.OFF,
387                         imsManager.mImsConfigListener);
388
389                 if (enabled) {
390                     log("setVtSetting() : turnOnIms");
391                     imsManager.turnOnIms();
392                 } else if (isTurnOffImsAllowedByPlatform(context)
393                         && (!isVolteEnabledByPlatform(context)
394                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
395                     log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
396                     imsManager.turnOffIms();
397                 }
398             } catch (ImsException e) {
399                 loge("setVtSetting(): ", e);
400             }
401         }
402     }
403
404     /*
405      * Returns whether turning off ims is allowed by platform.
406      * The platform property may override the carrier config.
407      */
408     private static boolean isTurnOffImsAllowedByPlatform(Context context) {
409         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
410                 PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
411             return true;
412         }
413         return getBooleanCarrierConfig(context,
414                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
415     }
416
417     /**
418      * Returns the user configuration of WFC setting
419      */
420     public static boolean isWfcEnabledByUser(Context context) {
421         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
422                 android.provider.Settings.Global.WFC_IMS_ENABLED,
423                 getBooleanCarrierConfig(context,
424                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
425                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
426         return (enabled == 1) ? true : false;
427     }
428
429     /**
430      * Change persistent WFC enabled setting
431      */
432     public static void setWfcSetting(Context context, boolean enabled) {
433         int value = enabled ? 1 : 0;
434         android.provider.Settings.Global.putInt(context.getContentResolver(),
435                 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
436
437         ImsManager imsManager = ImsManager.getInstance(context,
438                 SubscriptionManager.getDefaultVoicePhoneId());
439         if (imsManager != null) {
440             try {
441                 ImsConfig config = imsManager.getConfigInterface();
442                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
443                         TelephonyManager.NETWORK_TYPE_IWLAN,
444                         enabled ? ImsConfig.FeatureValueConstants.ON
445                                 : ImsConfig.FeatureValueConstants.OFF,
446                         imsManager.mImsConfigListener);
447
448                 if (enabled) {
449                     log("setWfcSetting() : turnOnIms");
450                     imsManager.turnOnIms();
451                 } else if (isTurnOffImsAllowedByPlatform(context)
452                         && (!isVolteEnabledByPlatform(context)
453                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
454                     log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
455                     imsManager.turnOffIms();
456                 }
457
458                 TelephonyManager tm = (TelephonyManager) context
459                         .getSystemService(Context.TELEPHONY_SERVICE);
460                 setWfcModeInternal(context, enabled
461                         // Choose wfc mode per current roaming preference
462                         ? getWfcMode(context, tm.isNetworkRoaming())
463                         // Force IMS to register over LTE when turning off WFC
464                         : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
465             } catch (ImsException e) {
466                 loge("setWfcSetting(): ", e);
467             }
468         }
469     }
470
471     /**
472      * Returns the user configuration of WFC preference setting
473      */
474     public static int getWfcMode(Context context) {
475         int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
476                 android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
477                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
478         if (DBG) log("getWfcMode - setting=" + setting);
479         return setting;
480     }
481
482     /**
483      * Change persistent WFC preference setting
484      */
485     public static void setWfcMode(Context context, int wfcMode) {
486         if (DBG) log("setWfcMode - setting=" + wfcMode);
487         android.provider.Settings.Global.putInt(context.getContentResolver(),
488                 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
489
490         setWfcModeInternal(context, wfcMode);
491     }
492
493     /**
494      * Returns the user configuration of WFC preference setting
495      *
496      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
497      */
498     public static int getWfcMode(Context context, boolean roaming) {
499         int setting = 0;
500         if (!roaming) {
501             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
502                     android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
503                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
504             if (DBG) log("getWfcMode - setting=" + setting);
505         } else {
506             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
507                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
508                     getIntCarrierConfig(context,
509                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
510             if (DBG) log("getWfcMode (roaming) - setting=" + setting);
511         }
512         return setting;
513     }
514
515     /**
516      * Change persistent WFC preference setting
517      *
518      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
519      */
520     public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
521         if (!roaming) {
522             if (DBG) log("setWfcMode - setting=" + wfcMode);
523             android.provider.Settings.Global.putInt(context.getContentResolver(),
524                     android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
525         } else {
526             if (DBG) log("setWfcMode (roaming) - setting=" + wfcMode);
527             android.provider.Settings.Global.putInt(context.getContentResolver(),
528                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
529         }
530
531         TelephonyManager tm = (TelephonyManager)
532                 context.getSystemService(Context.TELEPHONY_SERVICE);
533         if (roaming == tm.isNetworkRoaming()) {
534             setWfcModeInternal(context, wfcMode);
535         }
536     }
537
538     private static void setWfcModeInternal(Context context, int wfcMode) {
539         final ImsManager imsManager = ImsManager.getInstance(context,
540                 SubscriptionManager.getDefaultVoicePhoneId());
541         if (imsManager != null) {
542             final int value = wfcMode;
543             Thread thread = new Thread(new Runnable() {
544                 public void run() {
545                     try {
546                         imsManager.getConfigInterface().setProvisionedValue(
547                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
548                                 value);
549                     } catch (ImsException e) {
550                         // do nothing
551                     }
552                 }
553             });
554             thread.start();
555         }
556     }
557
558     /**
559      * Returns the user configuration of WFC roaming setting
560      */
561     public static boolean isWfcRoamingEnabledByUser(Context context) {
562         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
563                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
564                 getBooleanCarrierConfig(context,
565                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
566                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
567         return (enabled == 1) ? true : false;
568     }
569
570     /**
571      * Change persistent WFC roaming enabled setting
572      */
573     public static void setWfcRoamingSetting(Context context, boolean enabled) {
574         android.provider.Settings.Global.putInt(context.getContentResolver(),
575                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
576                 enabled ? ImsConfig.FeatureValueConstants.ON
577                         : ImsConfig.FeatureValueConstants.OFF);
578
579         setWfcRoamingSettingInternal(context, enabled);
580     }
581
582     private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
583         final ImsManager imsManager = ImsManager.getInstance(context,
584                 SubscriptionManager.getDefaultVoicePhoneId());
585         if (imsManager != null) {
586             final int value = enabled
587                     ? ImsConfig.FeatureValueConstants.ON
588                     : ImsConfig.FeatureValueConstants.OFF;
589             Thread thread = new Thread(new Runnable() {
590                 public void run() {
591                     try {
592                         imsManager.getConfigInterface().setProvisionedValue(
593                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
594                                 value);
595                     } catch (ImsException e) {
596                         // do nothing
597                     }
598                 }
599             });
600             thread.start();
601         }
602     }
603
604     /**
605      * Returns a platform configuration for WFC which may override the user
606      * setting. Note: WFC presumes that VoLTE is enabled (these are
607      * configuration settings which must be done correctly).
608      */
609     public static boolean isWfcEnabledByPlatform(Context context) {
610         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
611                 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
612             return true;
613         }
614
615         return
616                context.getResources().getBoolean(
617                        com.android.internal.R.bool.config_device_wfc_ims_available) &&
618                getBooleanCarrierConfig(context,
619                        CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
620                isGbaValid(context);
621     }
622
623     /**
624      * If carrier requires that IMS is only available if GBA capable SIM is used,
625      * then this function checks GBA bit in EF IST.
626      *
627      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
628      */
629     private static boolean isGbaValid(Context context) {
630         if (getBooleanCarrierConfig(context,
631                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
632             final TelephonyManager telephonyManager = TelephonyManager.getDefault();
633             String efIst = telephonyManager.getIsimIst();
634             if (efIst == null) {
635                 loge("ISF is NULL");
636                 return true;
637             }
638             boolean result = efIst != null && efIst.length() > 1 &&
639                     (0x02 & (byte)efIst.charAt(1)) != 0;
640             if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
641             return result;
642         }
643         return true;
644     }
645
646     /**
647      * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
648      *
649      * We cannot register receiver in ImsManager because this would lead to resource leak.
650      * ImsManager can be created in different processes and it is not notified when that process
651      * is about to be terminated.
652      *
653      * @hide
654      * */
655     public static void onProvisionedValueChanged(Context context, int item, String value) {
656         if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
657         ImsManager mgr = ImsManager.getInstance(context,
658                 SubscriptionManager.getDefaultVoicePhoneId());
659
660         switch (item) {
661             case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
662                 mgr.setVolteProvisionedProperty(value.equals("1"));
663                 if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
664                 break;
665
666             case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
667                 mgr.setWfcProvisionedProperty(value.equals("1"));
668                 if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
669                 break;
670
671             case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
672                 mgr.setVtProvisionedProperty(value.equals("1"));
673                 if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
674                 break;
675
676         }
677     }
678
679     private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Void> {
680         @Override
681         protected Void doInBackground(Void... params) {
682             // disable on any error
683             setVolteProvisionedProperty(false);
684             setWfcProvisionedProperty(false);
685             setVtProvisionedProperty(false);
686
687             try {
688                 ImsConfig config = getConfigInterface();
689                 if (config != null) {
690                     setVolteProvisionedProperty(getProvisionedBool(config,
691                             ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
692                     if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
693
694                     setWfcProvisionedProperty(getProvisionedBool(config,
695                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
696                     if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
697
698                     setVtProvisionedProperty(getProvisionedBool(config,
699                             ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
700                     if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
701
702                 }
703             } catch (ImsException ie) {
704                 Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
705             }
706
707             return null;
708         }
709
710         private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
711             return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
712         }
713     }
714
715     /** Asynchronously get VoLTE, WFC, VT provisioning statuses */
716     private void updateProvisionedValues() {
717         if (getBooleanCarrierConfig(mContext,
718                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
719
720             new AsyncUpdateProvisionedValues().execute();
721         }
722     }
723
724     /**
725      * Sync carrier config and user settings with ImsConfig.
726      *
727      * @param context for the manager object
728      * @param phoneId phone id
729      * @param force update
730      */
731     public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
732         if (!force) {
733             if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
734                 log("updateImsServiceConfig: SIM not ready");
735                 // Don't disable IMS if SIM is not ready
736                 return;
737             }
738         }
739
740         final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
741         if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
742             try {
743                 imsManager.updateProvisionedValues();
744
745                 // TODO: Extend ImsConfig API and set all feature values in single function call.
746
747                 // Note: currently the order of updates is set to produce different order of
748                 // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
749                 // differentiate this code path from vendor code perspective.
750                 boolean isImsUsed = imsManager.updateVolteFeatureValue();
751                 isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
752                 isImsUsed |= imsManager.updateVideoCallFeatureValue();
753
754                 if (isImsUsed || !isTurnOffImsAllowedByPlatform(context)) {
755                     // Turn on IMS if it is used.
756                     // Also, if turning off is not allowed for current carrier,
757                     // we need to turn IMS on because it might be turned off before
758                     // phone switched to current carrier.
759                     log("updateImsServiceConfig: turnOnIms");
760                     imsManager.turnOnIms();
761                 } else {
762                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
763                     log("updateImsServiceConfig: turnOffIms");
764                     imsManager.turnOffIms();
765                 }
766
767                 imsManager.mConfigUpdated = true;
768             } catch (ImsException e) {
769                 loge("updateImsServiceConfig: ", e);
770                 imsManager.mConfigUpdated = false;
771             }
772         }
773     }
774
775     /**
776      * Update VoLTE config
777      * @return whether feature is On
778      * @throws ImsException
779      */
780     private boolean updateVolteFeatureValue() throws ImsException {
781         boolean available = isVolteEnabledByPlatform(mContext);
782         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
783         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
784         boolean isFeatureOn = available && enabled && isNonTty;
785
786         log("updateVolteFeatureValue: available = " + available
787                 + ", enabled = " + enabled
788                 + ", nonTTY = " + isNonTty);
789
790         getConfigInterface().setFeatureValue(
791                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
792                 TelephonyManager.NETWORK_TYPE_LTE,
793                 isFeatureOn ?
794                         ImsConfig.FeatureValueConstants.ON :
795                         ImsConfig.FeatureValueConstants.OFF,
796                 mImsConfigListener);
797
798         return isFeatureOn;
799     }
800
801     /**
802      * Update video call over LTE config
803      * @return whether feature is On
804      * @throws ImsException
805      */
806     private boolean updateVideoCallFeatureValue() throws ImsException {
807         boolean available = isVtEnabledByPlatform(mContext);
808         boolean enabled = isVtEnabledByUser(mContext);
809         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
810         boolean isDataEnabled = isDataEnabled();
811         boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
812                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
813
814         boolean isFeatureOn = available && enabled && isNonTty
815                 && (ignoreDataEnabledChanged || isDataEnabled);
816
817         log("updateVideoCallFeatureValue: available = " + available
818                 + ", enabled = " + enabled
819                 + ", nonTTY = " + isNonTty
820                 + ", data enabled = " + isDataEnabled);
821
822         getConfigInterface().setFeatureValue(
823                 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
824                 TelephonyManager.NETWORK_TYPE_LTE,
825                 isFeatureOn ?
826                         ImsConfig.FeatureValueConstants.ON :
827                         ImsConfig.FeatureValueConstants.OFF,
828                 mImsConfigListener);
829
830         return isFeatureOn;
831     }
832
833     /**
834      * Update WFC config
835      * @return whether feature is On
836      * @throws ImsException
837      */
838     private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
839         boolean isNetworkRoaming = TelephonyManager.getDefault().isNetworkRoaming();
840         boolean available = isWfcEnabledByPlatform(mContext);
841         boolean enabled = isWfcEnabledByUser(mContext);
842         int mode = getWfcMode(mContext, isNetworkRoaming);
843         boolean roaming = isWfcRoamingEnabledByUser(mContext);
844         boolean isFeatureOn = available && enabled;
845
846         log("updateWfcFeatureAndProvisionedValues: available = " + available
847                 + ", enabled = " + enabled
848                 + ", mode = " + mode
849                 + ", roaming = " + roaming);
850
851         getConfigInterface().setFeatureValue(
852                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
853                 TelephonyManager.NETWORK_TYPE_IWLAN,
854                 isFeatureOn ?
855                         ImsConfig.FeatureValueConstants.ON :
856                         ImsConfig.FeatureValueConstants.OFF,
857                 mImsConfigListener);
858
859         if (!isFeatureOn) {
860             mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
861             roaming = false;
862         }
863         setWfcModeInternal(mContext, mode);
864         setWfcRoamingSettingInternal(mContext, roaming);
865
866         return isFeatureOn;
867     }
868
869     private ImsManager(Context context, int phoneId) {
870         mContext = context;
871         mPhoneId = phoneId;
872         createImsService(true);
873     }
874
875     /*
876      * Returns a flag indicating whether the IMS service is available.
877      */
878     public boolean isServiceAvailable() {
879         if (mImsService != null) {
880             return true;
881         }
882
883         IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
884         if (binder != null) {
885             return true;
886         }
887
888         return false;
889     }
890
891     public void setImsConfigListener(ImsConfigListener listener) {
892         mImsConfigListener = listener;
893     }
894
895     /**
896      * Opens the IMS service for making calls and/or receiving generic IMS calls.
897      * The caller may make subsquent calls through {@link #makeCall}.
898      * The IMS service will register the device to the operator's network with the credentials
899      * (from ISIM) periodically in order to receive calls from the operator's network.
900      * When the IMS service receives a new call, it will send out an intent with
901      * the provided action string.
902      * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
903      *
904      * @param serviceClass a service class specified in {@link ImsServiceClass}
905      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
906      * @param incomingCallPendingIntent When an incoming call is received,
907      *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
908      *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
909      *        as the result code and the intent to fill in the call ID; It cannot be null
910      * @param listener To listen to IMS registration events; It cannot be null
911      * @return identifier (greater than 0) for the specified service
912      * @throws NullPointerException if {@code incomingCallPendingIntent}
913      *      or {@code listener} is null
914      * @throws ImsException if calling the IMS service results in an error
915      * @see #getCallId
916      * @see #getServiceId
917      */
918     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
919             ImsConnectionStateListener listener) throws ImsException {
920         checkAndThrowExceptionIfServiceUnavailable();
921
922         if (incomingCallPendingIntent == null) {
923             throw new NullPointerException("incomingCallPendingIntent can't be null");
924         }
925
926         if (listener == null) {
927             throw new NullPointerException("listener can't be null");
928         }
929
930         int result = 0;
931
932         try {
933             result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
934                     createRegistrationListenerProxy(serviceClass, listener));
935         } catch (RemoteException e) {
936             throw new ImsException("open()", e,
937                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
938         }
939
940         if (result <= 0) {
941             // If the return value is a minus value,
942             // it means that an error occurred in the service.
943             // So, it needs to convert to the reason code specified in ImsReasonInfo.
944             throw new ImsException("open()", (result * (-1)));
945         }
946
947         return result;
948     }
949
950     /**
951      * Adds registration listener to the IMS service.
952      *
953      * @param serviceClass a service class specified in {@link ImsServiceClass}
954      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
955      * @param listener To listen to IMS registration events; It cannot be null
956      * @throws NullPointerException if {@code listener} is null
957      * @throws ImsException if calling the IMS service results in an error
958      */
959     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
960             throws ImsException {
961         checkAndThrowExceptionIfServiceUnavailable();
962
963         if (listener == null) {
964             throw new NullPointerException("listener can't be null");
965         }
966
967         try {
968             mImsService.addRegistrationListener(mPhoneId, serviceClass,
969                     createRegistrationListenerProxy(serviceClass, listener));
970         } catch (RemoteException e) {
971             throw new ImsException("addRegistrationListener()", e,
972                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
973         }
974     }
975
976     /**
977      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
978      * All the resources that were allocated to the service are also released.
979      *
980      * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
981      * @throws ImsException if calling the IMS service results in an error
982      */
983     public void close(int serviceId) throws ImsException {
984         checkAndThrowExceptionIfServiceUnavailable();
985
986         try {
987             mImsService.close(serviceId);
988         } catch (RemoteException e) {
989             throw new ImsException("close()", e,
990                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
991         } finally {
992             mUt = null;
993             mConfig = null;
994             mEcbm = null;
995             mMultiEndpoint = null;
996         }
997     }
998
999     /**
1000      * Gets the configuration interface to provision / withdraw the supplementary service settings.
1001      *
1002      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1003      * @return the Ut interface instance
1004      * @throws ImsException if getting the Ut interface results in an error
1005      */
1006     public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
1007             throws ImsException {
1008         // FIXME: manage the multiple Ut interfaces based on the service id
1009         if (mUt == null) {
1010             checkAndThrowExceptionIfServiceUnavailable();
1011
1012             try {
1013                 IImsUt iUt = mImsService.getUtInterface(serviceId);
1014
1015                 if (iUt == null) {
1016                     throw new ImsException("getSupplementaryServiceConfiguration()",
1017                             ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
1018                 }
1019
1020                 mUt = new ImsUt(iUt);
1021             } catch (RemoteException e) {
1022                 throw new ImsException("getSupplementaryServiceConfiguration()", e,
1023                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1024             }
1025         }
1026
1027         return mUt;
1028     }
1029
1030     /**
1031      * Checks if the IMS service has successfully registered to the IMS network
1032      * with the specified service & call type.
1033      *
1034      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1035      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1036      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1037      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1038      * @param callType a call type that is specified in {@link ImsCallProfile}
1039      *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
1040      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1041      *        {@link ImsCallProfile#CALL_TYPE_VT}
1042      *        {@link ImsCallProfile#CALL_TYPE_VS}
1043      * @return true if the specified service id is connected to the IMS network;
1044      *        false otherwise
1045      * @throws ImsException if calling the IMS service results in an error
1046      */
1047     public boolean isConnected(int serviceId, int serviceType, int callType)
1048             throws ImsException {
1049         checkAndThrowExceptionIfServiceUnavailable();
1050
1051         try {
1052             return mImsService.isConnected(serviceId, serviceType, callType);
1053         } catch (RemoteException e) {
1054             throw new ImsException("isServiceConnected()", e,
1055                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1056         }
1057     }
1058
1059     /**
1060      * Checks if the specified IMS service is opend.
1061      *
1062      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1063      * @return true if the specified service id is opened; false otherwise
1064      * @throws ImsException if calling the IMS service results in an error
1065      */
1066     public boolean isOpened(int serviceId) throws ImsException {
1067         checkAndThrowExceptionIfServiceUnavailable();
1068
1069         try {
1070             return mImsService.isOpened(serviceId);
1071         } catch (RemoteException e) {
1072             throw new ImsException("isOpened()", e,
1073                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1074         }
1075     }
1076
1077     /**
1078      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1079      *
1080      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1081      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1082      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1083      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1084      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1085      * @param callType a call type that is specified in {@link ImsCallProfile}
1086      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1087      *        {@link ImsCallProfile#CALL_TYPE_VT}
1088      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1089      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1090      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1091      *        {@link ImsCallProfile#CALL_TYPE_VS}
1092      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1093      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1094      * @return a {@link ImsCallProfile} object
1095      * @throws ImsException if calling the IMS service results in an error
1096      */
1097     public ImsCallProfile createCallProfile(int serviceId,
1098             int serviceType, int callType) throws ImsException {
1099         checkAndThrowExceptionIfServiceUnavailable();
1100
1101         try {
1102             return mImsService.createCallProfile(serviceId, serviceType, callType);
1103         } catch (RemoteException e) {
1104             throw new ImsException("createCallProfile()", e,
1105                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1106         }
1107     }
1108
1109     /**
1110      * Creates a {@link ImsCall} to make a call.
1111      *
1112      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1113      * @param profile a call profile to make the call
1114      *      (it contains service type, call type, media information, etc.)
1115      * @param participants participants to invite the conference call
1116      * @param listener listen to the call events from {@link ImsCall}
1117      * @return a {@link ImsCall} object
1118      * @throws ImsException if calling the IMS service results in an error
1119      */
1120     public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
1121             ImsCall.Listener listener) throws ImsException {
1122         if (DBG) {
1123             log("makeCall :: serviceId=" + serviceId
1124                     + ", profile=" + profile);
1125         }
1126
1127         checkAndThrowExceptionIfServiceUnavailable();
1128
1129         ImsCall call = new ImsCall(mContext, profile);
1130
1131         call.setListener(listener);
1132         ImsCallSession session = createCallSession(serviceId, profile);
1133
1134         if ((callees != null) && (callees.length == 1)) {
1135             call.start(session, callees[0]);
1136         } else {
1137             call.start(session, callees);
1138         }
1139
1140         return call;
1141     }
1142
1143     /**
1144      * Creates a {@link ImsCall} to take an incoming call.
1145      *
1146      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1147      * @param incomingCallIntent the incoming call broadcast intent
1148      * @param listener to listen to the call events from {@link ImsCall}
1149      * @return a {@link ImsCall} object
1150      * @throws ImsException if calling the IMS service results in an error
1151      */
1152     public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
1153             ImsCall.Listener listener) throws ImsException {
1154         if (DBG) {
1155             log("takeCall :: serviceId=" + serviceId
1156                     + ", incomingCall=" + incomingCallIntent);
1157         }
1158
1159         checkAndThrowExceptionIfServiceUnavailable();
1160
1161         if (incomingCallIntent == null) {
1162             throw new ImsException("Can't retrieve session with null intent",
1163                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1164         }
1165
1166         int incomingServiceId = getServiceId(incomingCallIntent);
1167
1168         if (serviceId != incomingServiceId) {
1169             throw new ImsException("Service id is mismatched in the incoming call intent",
1170                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1171         }
1172
1173         String callId = getCallId(incomingCallIntent);
1174
1175         if (callId == null) {
1176             throw new ImsException("Call ID missing in the incoming call intent",
1177                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1178         }
1179
1180         try {
1181             IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
1182
1183             if (session == null) {
1184                 throw new ImsException("No pending session for the call",
1185                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
1186             }
1187
1188             ImsCall call = new ImsCall(mContext, session.getCallProfile());
1189
1190             call.attachSession(new ImsCallSession(session));
1191             call.setListener(listener);
1192
1193             return call;
1194         } catch (Throwable t) {
1195             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
1196         }
1197     }
1198
1199     /**
1200      * Gets the config interface to get/set service/capability parameters.
1201      *
1202      * @return the ImsConfig instance.
1203      * @throws ImsException if getting the setting interface results in an error.
1204      */
1205     public ImsConfig getConfigInterface() throws ImsException {
1206
1207         if (mConfig == null) {
1208             checkAndThrowExceptionIfServiceUnavailable();
1209
1210             try {
1211                 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
1212                 if (config == null) {
1213                     throw new ImsException("getConfigInterface()",
1214                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
1215                 }
1216                 mConfig = new ImsConfig(config, mContext);
1217             } catch (RemoteException e) {
1218                 throw new ImsException("getConfigInterface()", e,
1219                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1220             }
1221         }
1222         if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
1223         return mConfig;
1224     }
1225
1226     public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
1227             throws ImsException {
1228
1229         checkAndThrowExceptionIfServiceUnavailable();
1230
1231         try {
1232             mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
1233         } catch (RemoteException e) {
1234             throw new ImsException("setTTYMode()", e,
1235                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1236         }
1237
1238         if (!getBooleanCarrierConfig(context,
1239                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
1240             setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
1241                     isEnhanced4gLteModeSettingEnabledByUser(context));
1242         }
1243     }
1244
1245     private ImsReasonInfo makeACopy(ImsReasonInfo imsReasonInfo) {
1246         Parcel p = Parcel.obtain();
1247         imsReasonInfo.writeToParcel(p, 0);
1248         p.setDataPosition(0);
1249         ImsReasonInfo clonedReasonInfo = ImsReasonInfo.CREATOR.createFromParcel(p);
1250         p.recycle();
1251         return clonedReasonInfo;
1252     }
1253
1254     /**
1255      * Get Recent IMS Disconnect Reasons.
1256      *
1257      * @return ArrayList of ImsReasonInfo objects. MAX size of the arraylist
1258      * is MAX_RECENT_DISCONNECT_REASONS. The objects are in the
1259      * chronological order.
1260      */
1261     public ArrayList<ImsReasonInfo> getRecentImsDisconnectReasons() {
1262         ArrayList<ImsReasonInfo> disconnectReasons = new ArrayList<>();
1263
1264         for (ImsReasonInfo reason : mRecentDisconnectReasons) {
1265             disconnectReasons.add(makeACopy(reason));
1266         }
1267         return disconnectReasons;
1268     }
1269
1270     /**
1271      * Get the boolean config from carrier config manager.
1272      *
1273      * @param context the context to get carrier service
1274      * @param key config key defined in CarrierConfigManager
1275      * @return boolean value of corresponding key.
1276      */
1277     private static boolean getBooleanCarrierConfig(Context context, String key) {
1278         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1279                 Context.CARRIER_CONFIG_SERVICE);
1280         PersistableBundle b = null;
1281         if (configManager != null) {
1282             b = configManager.getConfig();
1283         }
1284         if (b != null) {
1285             return b.getBoolean(key);
1286         } else {
1287             // Return static default defined in CarrierConfigManager.
1288             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1289         }
1290     }
1291
1292     /**
1293      * Get the int config from carrier config manager.
1294      *
1295      * @param context the context to get carrier service
1296      * @param key config key defined in CarrierConfigManager
1297      * @return integer value of corresponding key.
1298      */
1299     private static int getIntCarrierConfig(Context context, String key) {
1300         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1301                 Context.CARRIER_CONFIG_SERVICE);
1302         PersistableBundle b = null;
1303         if (configManager != null) {
1304             b = configManager.getConfig();
1305         }
1306         if (b != null) {
1307             return b.getInt(key);
1308         } else {
1309             // Return static default defined in CarrierConfigManager.
1310             return CarrierConfigManager.getDefaultConfig().getInt(key);
1311         }
1312     }
1313
1314     /**
1315      * Gets the call ID from the specified incoming call broadcast intent.
1316      *
1317      * @param incomingCallIntent the incoming call broadcast intent
1318      * @return the call ID or null if the intent does not contain it
1319      */
1320     private static String getCallId(Intent incomingCallIntent) {
1321         if (incomingCallIntent == null) {
1322             return null;
1323         }
1324
1325         return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
1326     }
1327
1328     /**
1329      * Gets the service type from the specified incoming call broadcast intent.
1330      *
1331      * @param incomingCallIntent the incoming call broadcast intent
1332      * @return the service identifier or -1 if the intent does not contain it
1333      */
1334     private static int getServiceId(Intent incomingCallIntent) {
1335         if (incomingCallIntent == null) {
1336             return (-1);
1337         }
1338
1339         return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
1340     }
1341
1342     /**
1343      * Binds the IMS service only if the service is not created.
1344      */
1345     private void checkAndThrowExceptionIfServiceUnavailable()
1346             throws ImsException {
1347         if (mImsService == null) {
1348             createImsService(true);
1349
1350             if (mImsService == null) {
1351                 throw new ImsException("Service is unavailable",
1352                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1353             }
1354         }
1355     }
1356
1357     private static String getImsServiceName(int phoneId) {
1358         // TODO: MSIM implementation needs to decide on service name as a function of phoneId
1359         return IMS_SERVICE;
1360     }
1361
1362     /**
1363      * Binds the IMS service to make/receive the call.
1364      */
1365     private void createImsService(boolean checkService) {
1366         if (checkService) {
1367             IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
1368
1369             if (binder == null) {
1370                 return;
1371             }
1372         }
1373
1374         IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
1375
1376         if (b != null) {
1377             try {
1378                 b.linkToDeath(mDeathRecipient, 0);
1379             } catch (RemoteException e) {
1380             }
1381         }
1382
1383         mImsService = IImsService.Stub.asInterface(b);
1384     }
1385
1386     /**
1387      * Creates a {@link ImsCallSession} with the specified call profile.
1388      * Use other methods, if applicable, instead of interacting with
1389      * {@link ImsCallSession} directly.
1390      *
1391      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1392      * @param profile a call profile to make the call
1393      */
1394     private ImsCallSession createCallSession(int serviceId,
1395             ImsCallProfile profile) throws ImsException {
1396         try {
1397             return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
1398         } catch (RemoteException e) {
1399             return null;
1400         }
1401     }
1402
1403     private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1404             ImsConnectionStateListener listener) {
1405         ImsRegistrationListenerProxy proxy =
1406                 new ImsRegistrationListenerProxy(serviceClass, listener);
1407         return proxy;
1408     }
1409
1410     private static void log(String s) {
1411         Rlog.d(TAG, s);
1412     }
1413
1414     private static void loge(String s) {
1415         Rlog.e(TAG, s);
1416     }
1417
1418     private static void loge(String s, Throwable t) {
1419         Rlog.e(TAG, s, t);
1420     }
1421
1422     /**
1423      * Used for turning on IMS.if its off already
1424      */
1425     private void turnOnIms() throws ImsException {
1426         checkAndThrowExceptionIfServiceUnavailable();
1427
1428         try {
1429             mImsService.turnOnIms(mPhoneId);
1430         } catch (RemoteException e) {
1431             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1432         }
1433     }
1434
1435     private boolean isImsTurnOffAllowed() {
1436         return isTurnOffImsAllowedByPlatform(mContext)
1437                 && (!isWfcEnabledByPlatform(mContext)
1438                 || !isWfcEnabledByUser(mContext));
1439     }
1440
1441     private void setLteFeatureValues(boolean turnOn) {
1442         log("setLteFeatureValues: " + turnOn);
1443         try {
1444             ImsConfig config = getConfigInterface();
1445             if (config != null) {
1446                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
1447                         TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
1448
1449                 if (isVtEnabledByPlatform(mContext)) {
1450                     boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
1451                             CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1452                     boolean enableViLte = turnOn && isVtEnabledByUser(mContext) &&
1453                             (ignoreDataEnabledChanged || isDataEnabled());
1454                     config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1455                             TelephonyManager.NETWORK_TYPE_LTE,
1456                             enableViLte ? 1 : 0,
1457                             mImsConfigListener);
1458                 }
1459             }
1460         } catch (ImsException e) {
1461             loge("setLteFeatureValues: exception ", e);
1462         }
1463     }
1464
1465     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
1466         checkAndThrowExceptionIfServiceUnavailable();
1467
1468         // if turnOn: first set feature values then call turnOnIms()
1469         // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
1470         // allowed, first call turnOffIms() then set feature values
1471         if (turnOn) {
1472             setLteFeatureValues(turnOn);
1473             log("setAdvanced4GMode: turnOnIms");
1474             turnOnIms();
1475         } else {
1476             if (isImsTurnOffAllowed()) {
1477                 log("setAdvanced4GMode: turnOffIms");
1478                 turnOffIms();
1479             }
1480             setLteFeatureValues(turnOn);
1481         }
1482     }
1483
1484     /**
1485      * Used for turning off IMS completely in order to make the device CSFB'ed.
1486      * Once turned off, all calls will be over CS.
1487      */
1488     private void turnOffIms() throws ImsException {
1489         checkAndThrowExceptionIfServiceUnavailable();
1490
1491         try {
1492             mImsService.turnOffIms(mPhoneId);
1493         } catch (RemoteException e) {
1494             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1495         }
1496     }
1497
1498     private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
1499         if (reason == null) return;
1500         while (mRecentDisconnectReasons.size() >= MAX_RECENT_DISCONNECT_REASONS) {
1501             mRecentDisconnectReasons.removeFirst();
1502         }
1503         mRecentDisconnectReasons.addLast(reason);
1504     }
1505
1506     /**
1507      * Death recipient class for monitoring IMS service.
1508      */
1509     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1510         @Override
1511         public void binderDied() {
1512             mImsService = null;
1513             mUt = null;
1514             mConfig = null;
1515             mEcbm = null;
1516             mMultiEndpoint = null;
1517
1518             if (mContext != null) {
1519                 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
1520                 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
1521                 mContext.sendBroadcast(new Intent(intent));
1522             }
1523         }
1524     }
1525
1526     /**
1527      * Adapter class for {@link IImsRegistrationListener}.
1528      */
1529     private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1530         private int mServiceClass;
1531         private ImsConnectionStateListener mListener;
1532
1533         public ImsRegistrationListenerProxy(int serviceClass,
1534                 ImsConnectionStateListener listener) {
1535             mServiceClass = serviceClass;
1536             mListener = listener;
1537         }
1538
1539         public boolean isSameProxy(int serviceClass) {
1540             return (mServiceClass == serviceClass);
1541         }
1542
1543         @Deprecated
1544         public void registrationConnected() {
1545             if (DBG) {
1546                 log("registrationConnected ::");
1547             }
1548
1549             if (mListener != null) {
1550                 mListener.onImsConnected(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
1551             }
1552         }
1553
1554         @Deprecated
1555         public void registrationProgressing() {
1556             if (DBG) {
1557                 log("registrationProgressing ::");
1558             }
1559
1560             if (mListener != null) {
1561                 mListener.onImsProgressing(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
1562             }
1563         }
1564
1565         @Override
1566         public void registrationConnectedWithRadioTech(int imsRadioTech) {
1567             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1568             //       values in ServiceState.java.
1569             if (DBG) {
1570                 log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
1571             }
1572
1573             if (mListener != null) {
1574                 mListener.onImsConnected(imsRadioTech);
1575             }
1576         }
1577
1578         @Override
1579         public void registrationProgressingWithRadioTech(int imsRadioTech) {
1580             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1581             //       values in ServiceState.java.
1582             if (DBG) {
1583                 log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
1584             }
1585
1586             if (mListener != null) {
1587                 mListener.onImsProgressing(imsRadioTech);
1588             }
1589         }
1590
1591         @Override
1592         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1593             if (DBG) {
1594                 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1595             }
1596
1597             addToRecentDisconnectReasons(imsReasonInfo);
1598
1599             if (mListener != null) {
1600                 mListener.onImsDisconnected(imsReasonInfo);
1601             }
1602         }
1603
1604         @Override
1605         public void registrationResumed() {
1606             if (DBG) {
1607                 log("registrationResumed ::");
1608             }
1609
1610             if (mListener != null) {
1611                 mListener.onImsResumed();
1612             }
1613         }
1614
1615         @Override
1616         public void registrationSuspended() {
1617             if (DBG) {
1618                 log("registrationSuspended ::");
1619             }
1620
1621             if (mListener != null) {
1622                 mListener.onImsSuspended();
1623             }
1624         }
1625
1626         @Override
1627         public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1628             log("registrationServiceCapabilityChanged :: serviceClass=" +
1629                     serviceClass + ", event=" + event);
1630
1631             if (mListener != null) {
1632                 mListener.onImsConnected(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
1633             }
1634         }
1635
1636         @Override
1637         public void registrationFeatureCapabilityChanged(int serviceClass,
1638                 int[] enabledFeatures, int[] disabledFeatures) {
1639             log("registrationFeatureCapabilityChanged :: serviceClass=" +
1640                     serviceClass);
1641             if (mListener != null) {
1642                 mListener.onFeatureCapabilityChanged(serviceClass,
1643                         enabledFeatures, disabledFeatures);
1644             }
1645         }
1646
1647         @Override
1648         public void voiceMessageCountUpdate(int count) {
1649             log("voiceMessageCountUpdate :: count=" + count);
1650
1651             if (mListener != null) {
1652                 mListener.onVoiceMessageCountChanged(count);
1653             }
1654         }
1655
1656         @Override
1657         public void registrationAssociatedUriChanged(Uri[] uris) {
1658             if (DBG) log("registrationAssociatedUriChanged ::");
1659
1660             if (mListener != null) {
1661                 mListener.registrationAssociatedUriChanged(uris);
1662             }
1663         }
1664
1665         @Override
1666         public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
1667             if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
1668                     ", imsReasonInfo=" + imsReasonInfo);
1669
1670             if (mListener != null) {
1671                 mListener.onRegistrationChangeFailed(targetAccessTech, imsReasonInfo);
1672             }
1673         }
1674     }
1675
1676     /**
1677      * Gets the ECBM interface to request ECBM exit.
1678      *
1679      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1680      * @return the ECBM interface instance
1681      * @throws ImsException if getting the ECBM interface results in an error
1682      */
1683     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1684         if (mEcbm == null) {
1685             checkAndThrowExceptionIfServiceUnavailable();
1686
1687             try {
1688                 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
1689
1690                 if (iEcbm == null) {
1691                     throw new ImsException("getEcbmInterface()",
1692                             ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1693                 }
1694                 mEcbm = new ImsEcbm(iEcbm);
1695             } catch (RemoteException e) {
1696                 throw new ImsException("getEcbmInterface()", e,
1697                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1698             }
1699         }
1700         return mEcbm;
1701     }
1702
1703     /**
1704      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
1705      *
1706      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1707      * @return the multi-endpoint interface instance
1708      * @throws ImsException if getting the multi-endpoint interface results in an error
1709      */
1710     public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
1711         if (mMultiEndpoint == null) {
1712             checkAndThrowExceptionIfServiceUnavailable();
1713
1714             try {
1715                 IImsMultiEndpoint iImsMultiEndpoint = mImsService.getMultiEndpointInterface(
1716                         serviceId);
1717
1718                 if (iImsMultiEndpoint == null) {
1719                     throw new ImsException("getMultiEndpointInterface()",
1720                             ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
1721                 }
1722                 mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
1723             } catch (RemoteException e) {
1724                 throw new ImsException("getMultiEndpointInterface()", e,
1725                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1726             }
1727         }
1728         return mMultiEndpoint;
1729     }
1730
1731     /**
1732      * Resets ImsManager settings back to factory defaults.
1733      *
1734      * @hide
1735      */
1736     public static void factoryReset(Context context) {
1737         // Set VoLTE to default
1738         android.provider.Settings.Global.putInt(context.getContentResolver(),
1739                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
1740                 ImsConfig.FeatureValueConstants.ON);
1741
1742         // Set VoWiFi to default
1743         android.provider.Settings.Global.putInt(context.getContentResolver(),
1744                 android.provider.Settings.Global.WFC_IMS_ENABLED,
1745                 getBooleanCarrierConfig(context,
1746                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
1747                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1748
1749         // Set VoWiFi mode to default
1750         android.provider.Settings.Global.putInt(context.getContentResolver(),
1751                 android.provider.Settings.Global.WFC_IMS_MODE,
1752                 getIntCarrierConfig(context,
1753                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
1754
1755         // Set VoWiFi roaming to default
1756         android.provider.Settings.Global.putInt(context.getContentResolver(),
1757                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1758                 getBooleanCarrierConfig(context,
1759                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
1760                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1761
1762         // Set VT to default
1763         android.provider.Settings.Global.putInt(context.getContentResolver(),
1764                 android.provider.Settings.Global.VT_IMS_ENABLED,
1765                 ImsConfig.FeatureValueConstants.ON);
1766
1767         // Push settings to ImsConfig
1768         ImsManager.updateImsServiceConfig(context,
1769                 SubscriptionManager.getDefaultVoicePhoneId(), true);
1770     }
1771
1772     private boolean isDataEnabled() {
1773         return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
1774     }
1775
1776     /**
1777      * Set data enabled/disabled flag.
1778      * @param enabled True if data is enabled, otherwise disabled.
1779      */
1780     public void setDataEnabled(boolean enabled) {
1781         log("setDataEnabled: " + enabled);
1782         SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
1783     }
1784
1785     private boolean isVolteProvisioned() {
1786         return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
1787     }
1788
1789     private void setVolteProvisionedProperty(boolean provisioned) {
1790         SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1791     }
1792
1793     private boolean isWfcProvisioned() {
1794         return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
1795     }
1796
1797     private void setWfcProvisionedProperty(boolean provisioned) {
1798         SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1799     }
1800
1801     private boolean isVtProvisioned() {
1802         return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
1803     }
1804
1805     private void setVtProvisionedProperty(boolean provisioned) {
1806         SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1807     }
1808
1809     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1810         pw.println("ImsManager:");
1811         pw.println("  mPhoneId = " + mPhoneId);
1812         pw.println("  mConfigUpdated = " + mConfigUpdated);
1813         pw.println("  mImsService = " + mImsService);
1814         pw.println("  mDataEnabled = " + isDataEnabled());
1815         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(mContext,
1816                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
1817
1818         pw.println("  isGbaValid = " + isGbaValid(mContext));
1819         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
1820         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled(mContext));
1821
1822         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatform(mContext));
1823         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice(mContext));
1824         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
1825                 isEnhanced4gLteModeSettingEnabledByUser(mContext));
1826         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatform(mContext));
1827         pw.println("  isVtEnabledByUser = " + isVtEnabledByUser(mContext));
1828
1829         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatform(mContext));
1830         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUser(mContext));
1831         pw.println("  getWfcMode = " + getWfcMode(mContext));
1832         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser(mContext));
1833
1834         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDevice(mContext));
1835         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice(mContext));
1836         pw.flush();
1837     }
1838 }