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