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                 TelephonyManager tm = (TelephonyManager) context
475                         .getSystemService(Context.TELEPHONY_SERVICE);
476                 setWfcModeInternal(context, enabled
477                         // Choose wfc mode per current roaming preference
478                         ? getWfcMode(context, tm.isNetworkRoaming())
479                         // Force IMS to register over LTE when turning off WFC
480                         : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
481             } catch (ImsException e) {
482                 loge("setWfcSetting(): ", e);
483             }
484         }
485     }
486
487     /**
488      * Returns the user configuration of WFC preference setting
489      */
490     public static int getWfcMode(Context context) {
491         int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
492                 android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
493                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
494         if (DBG) log("getWfcMode - setting=" + setting);
495         return setting;
496     }
497
498     /**
499      * Change persistent WFC preference setting
500      */
501     public static void setWfcMode(Context context, int wfcMode) {
502         if (DBG) log("setWfcMode - setting=" + wfcMode);
503         android.provider.Settings.Global.putInt(context.getContentResolver(),
504                 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
505
506         setWfcModeInternal(context, wfcMode);
507     }
508
509     /**
510      * Returns the user configuration of WFC preference setting
511      *
512      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
513      */
514     public static int getWfcMode(Context context, boolean roaming) {
515         int setting = 0;
516         if (!roaming) {
517             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
518                     android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
519                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
520             if (DBG) log("getWfcMode - setting=" + setting);
521         } else {
522             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
523                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
524                     getIntCarrierConfig(context,
525                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
526             if (DBG) log("getWfcMode (roaming) - setting=" + setting);
527         }
528         return setting;
529     }
530
531     /**
532      * Change persistent WFC preference setting
533      *
534      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
535      */
536     public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
537         if (!roaming) {
538             if (DBG) log("setWfcMode - setting=" + wfcMode);
539             android.provider.Settings.Global.putInt(context.getContentResolver(),
540                     android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
541         } else {
542             if (DBG) log("setWfcMode (roaming) - setting=" + wfcMode);
543             android.provider.Settings.Global.putInt(context.getContentResolver(),
544                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
545         }
546
547         TelephonyManager tm = (TelephonyManager)
548                 context.getSystemService(Context.TELEPHONY_SERVICE);
549         if (roaming == tm.isNetworkRoaming()) {
550             setWfcModeInternal(context, wfcMode);
551         }
552     }
553
554     private static void setWfcModeInternal(Context context, int wfcMode) {
555         final ImsManager imsManager = ImsManager.getInstance(context,
556                 SubscriptionManager.getDefaultVoicePhoneId());
557         if (imsManager != null) {
558             final int value = wfcMode;
559             Thread thread = new Thread(new Runnable() {
560                 public void run() {
561                     try {
562                         imsManager.getConfigInterface().setProvisionedValue(
563                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
564                                 value);
565                     } catch (ImsException e) {
566                         // do nothing
567                     }
568                 }
569             });
570             thread.start();
571         }
572     }
573
574     /**
575      * Returns the user configuration of WFC roaming setting
576      */
577     public static boolean isWfcRoamingEnabledByUser(Context context) {
578         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
579                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
580                 getBooleanCarrierConfig(context,
581                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
582                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
583         return (enabled == 1) ? true : false;
584     }
585
586     /**
587      * Change persistent WFC roaming enabled setting
588      */
589     public static void setWfcRoamingSetting(Context context, boolean enabled) {
590         android.provider.Settings.Global.putInt(context.getContentResolver(),
591                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
592                 enabled ? ImsConfig.FeatureValueConstants.ON
593                         : ImsConfig.FeatureValueConstants.OFF);
594
595         setWfcRoamingSettingInternal(context, enabled);
596     }
597
598     private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
599         final ImsManager imsManager = ImsManager.getInstance(context,
600                 SubscriptionManager.getDefaultVoicePhoneId());
601         if (imsManager != null) {
602             final int value = enabled
603                     ? ImsConfig.FeatureValueConstants.ON
604                     : ImsConfig.FeatureValueConstants.OFF;
605             Thread thread = new Thread(new Runnable() {
606                 public void run() {
607                     try {
608                         imsManager.getConfigInterface().setProvisionedValue(
609                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
610                                 value);
611                     } catch (ImsException e) {
612                         // do nothing
613                     }
614                 }
615             });
616             thread.start();
617         }
618     }
619
620     /**
621      * Returns a platform configuration for WFC which may override the user
622      * setting. Note: WFC presumes that VoLTE is enabled (these are
623      * configuration settings which must be done correctly).
624      */
625     public static boolean isWfcEnabledByPlatform(Context context) {
626         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
627                 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
628             return true;
629         }
630
631         return
632                context.getResources().getBoolean(
633                        com.android.internal.R.bool.config_device_wfc_ims_available) &&
634                getBooleanCarrierConfig(context,
635                        CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
636                isGbaValid(context);
637     }
638
639     /**
640      * If carrier requires that IMS is only available if GBA capable SIM is used,
641      * then this function checks GBA bit in EF IST.
642      *
643      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
644      */
645     private static boolean isGbaValid(Context context) {
646         if (getBooleanCarrierConfig(context,
647                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
648             final TelephonyManager telephonyManager = TelephonyManager.getDefault();
649             String efIst = telephonyManager.getIsimIst();
650             if (efIst == null) {
651                 loge("ISF is NULL");
652                 return true;
653             }
654             boolean result = efIst != null && efIst.length() > 1 &&
655                     (0x02 & (byte)efIst.charAt(1)) != 0;
656             if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
657             return result;
658         }
659         return true;
660     }
661
662     /**
663      * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
664      *
665      * We cannot register receiver in ImsManager because this would lead to resource leak.
666      * ImsManager can be created in different processes and it is not notified when that process
667      * is about to be terminated.
668      *
669      * @hide
670      * */
671     public static void onProvisionedValueChanged(Context context, int item, String value) {
672         if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
673         ImsManager mgr = ImsManager.getInstance(context,
674                 SubscriptionManager.getDefaultVoicePhoneId());
675
676         switch (item) {
677             case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
678                 mgr.setVolteProvisionedProperty(value.equals("1"));
679                 if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
680                 break;
681
682             case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
683                 mgr.setWfcProvisionedProperty(value.equals("1"));
684                 if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
685                 break;
686
687             case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
688                 mgr.setVtProvisionedProperty(value.equals("1"));
689                 if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
690                 break;
691
692         }
693     }
694
695     private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Void> {
696         @Override
697         protected Void doInBackground(Void... params) {
698             // disable on any error
699             setVolteProvisionedProperty(false);
700             setWfcProvisionedProperty(false);
701             setVtProvisionedProperty(false);
702
703             try {
704                 ImsConfig config = getConfigInterface();
705                 if (config != null) {
706                     setVolteProvisionedProperty(getProvisionedBool(config,
707                             ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
708                     if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
709
710                     setWfcProvisionedProperty(getProvisionedBool(config,
711                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
712                     if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
713
714                     setVtProvisionedProperty(getProvisionedBool(config,
715                             ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
716                     if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
717
718                 }
719             } catch (ImsException ie) {
720                 Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
721             }
722
723             return null;
724         }
725
726         private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
727             return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
728         }
729     }
730
731     /** Asynchronously get VoLTE, WFC, VT provisioning statuses */
732     private void updateProvisionedValues() {
733         if (getBooleanCarrierConfig(mContext,
734                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
735
736             new AsyncUpdateProvisionedValues().execute();
737         }
738     }
739
740     /**
741      * Sync carrier config and user settings with ImsConfig.
742      *
743      * @param context for the manager object
744      * @param phoneId phone id
745      * @param force update
746      */
747     public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
748         if (!force) {
749             if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
750                 log("updateImsServiceConfig: SIM not ready");
751                 // Don't disable IMS if SIM is not ready
752                 return;
753             }
754         }
755
756         final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
757         if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
758             try {
759                 imsManager.updateProvisionedValues();
760
761                 // TODO: Extend ImsConfig API and set all feature values in single function call.
762
763                 // Note: currently the order of updates is set to produce different order of
764                 // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
765                 // differentiate this code path from vendor code perspective.
766                 boolean isImsUsed = imsManager.updateVolteFeatureValue();
767                 isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
768                 isImsUsed |= imsManager.updateVideoCallFeatureValue();
769
770                 if (isImsUsed || !isTurnOffImsAllowedByPlatform(context)) {
771                     // Turn on IMS if it is used.
772                     // Also, if turning off is not allowed for current carrier,
773                     // we need to turn IMS on because it might be turned off before
774                     // phone switched to current carrier.
775                     log("updateImsServiceConfig: turnOnIms");
776                     imsManager.turnOnIms();
777                 } else {
778                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
779                     log("updateImsServiceConfig: turnOffIms");
780                     imsManager.turnOffIms();
781                 }
782
783                 imsManager.mConfigUpdated = true;
784             } catch (ImsException e) {
785                 loge("updateImsServiceConfig: ", e);
786                 imsManager.mConfigUpdated = false;
787             }
788         }
789     }
790
791     /**
792      * Update VoLTE config
793      * @return whether feature is On
794      * @throws ImsException
795      */
796     private boolean updateVolteFeatureValue() throws ImsException {
797         boolean available = isVolteEnabledByPlatform(mContext);
798         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
799         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
800         boolean isFeatureOn = available && enabled && isNonTty;
801
802         log("updateVolteFeatureValue: available = " + available
803                 + ", enabled = " + enabled
804                 + ", nonTTY = " + isNonTty);
805
806         getConfigInterface().setFeatureValue(
807                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
808                 TelephonyManager.NETWORK_TYPE_LTE,
809                 isFeatureOn ?
810                         ImsConfig.FeatureValueConstants.ON :
811                         ImsConfig.FeatureValueConstants.OFF,
812                 mImsConfigListener);
813
814         return isFeatureOn;
815     }
816
817     /**
818      * Update video call over LTE config
819      * @return whether feature is On
820      * @throws ImsException
821      */
822     private boolean updateVideoCallFeatureValue() throws ImsException {
823         boolean available = isVtEnabledByPlatform(mContext);
824         boolean enabled = isVtEnabledByUser(mContext);
825         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
826         boolean isDataEnabled = isDataEnabled();
827         boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
828                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
829
830         boolean isFeatureOn = available && enabled && isNonTty
831                 && (ignoreDataEnabledChanged || isDataEnabled);
832
833         log("updateVideoCallFeatureValue: available = " + available
834                 + ", enabled = " + enabled
835                 + ", nonTTY = " + isNonTty
836                 + ", data enabled = " + isDataEnabled);
837
838         getConfigInterface().setFeatureValue(
839                 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
840                 TelephonyManager.NETWORK_TYPE_LTE,
841                 isFeatureOn ?
842                         ImsConfig.FeatureValueConstants.ON :
843                         ImsConfig.FeatureValueConstants.OFF,
844                 mImsConfigListener);
845
846         return isFeatureOn;
847     }
848
849     /**
850      * Update WFC config
851      * @return whether feature is On
852      * @throws ImsException
853      */
854     private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
855         boolean isNetworkRoaming = TelephonyManager.getDefault().isNetworkRoaming();
856         boolean available = isWfcEnabledByPlatform(mContext);
857         boolean enabled = isWfcEnabledByUser(mContext);
858         int mode = getWfcMode(mContext, isNetworkRoaming);
859         boolean roaming = isWfcRoamingEnabledByUser(mContext);
860         boolean isFeatureOn = available && enabled;
861
862         log("updateWfcFeatureAndProvisionedValues: available = " + available
863                 + ", enabled = " + enabled
864                 + ", mode = " + mode
865                 + ", roaming = " + roaming);
866
867         getConfigInterface().setFeatureValue(
868                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
869                 TelephonyManager.NETWORK_TYPE_IWLAN,
870                 isFeatureOn ?
871                         ImsConfig.FeatureValueConstants.ON :
872                         ImsConfig.FeatureValueConstants.OFF,
873                 mImsConfigListener);
874
875         if (!isFeatureOn) {
876             mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
877             roaming = false;
878         }
879         setWfcModeInternal(mContext, mode);
880         setWfcRoamingSettingInternal(mContext, roaming);
881
882         return isFeatureOn;
883     }
884
885     /**
886      * Do NOT use this directly, instead use {@link #getInstance}.
887      */
888     @VisibleForTesting
889     public ImsManager(Context context, int phoneId) {
890         mContext = context;
891         mPhoneId = phoneId;
892         mConfigDynamicBind = mContext.getResources().getBoolean(
893                 com.android.internal.R.bool.config_dynamic_bind_ims);
894         createImsService();
895     }
896
897     /**
898      * @return Whether or not ImsManager is configured to Dynamically bind or not to support legacy
899      * devices.
900      */
901     public boolean isDynamicBinding() {
902         return mConfigDynamicBind;
903     }
904
905     /*
906      * Returns a flag indicating whether the IMS service is available.
907      */
908     public boolean isServiceAvailable() {
909         if (mImsServiceProxy == null) {
910             createImsService();
911         }
912         // mImsServiceProxy will always create an ImsServiceProxy.
913         return mImsServiceProxy.isBinderAlive();
914     }
915
916     public void setImsConfigListener(ImsConfigListener listener) {
917         mImsConfigListener = listener;
918     }
919
920     public void addNotifyStatusChangedCallback(ImsServiceProxy.INotifyStatusChanged c) {
921         if (c != null) {
922             mStatusCallbacks.add(c);
923         }
924     }
925
926     /**
927      * Opens the IMS service for making calls and/or receiving generic IMS calls.
928      * The caller may make subsquent calls through {@link #makeCall}.
929      * The IMS service will register the device to the operator's network with the credentials
930      * (from ISIM) periodically in order to receive calls from the operator's network.
931      * When the IMS service receives a new call, it will send out an intent with
932      * the provided action string.
933      * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
934      *
935      * @param serviceClass a service class specified in {@link ImsServiceClass}
936      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
937      * @param incomingCallPendingIntent When an incoming call is received,
938      *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
939      *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
940      *        as the result code and the intent to fill in the call ID; It cannot be null
941      * @param listener To listen to IMS registration events; It cannot be null
942      * @return identifier (greater than 0) for the specified service
943      * @throws NullPointerException if {@code incomingCallPendingIntent}
944      *      or {@code listener} is null
945      * @throws ImsException if calling the IMS service results in an error
946      * @see #getCallId
947      * @see #getImsSessionId
948      */
949     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
950             ImsConnectionStateListener listener) throws ImsException {
951         checkAndThrowExceptionIfServiceUnavailable();
952
953         if (incomingCallPendingIntent == null) {
954             throw new NullPointerException("incomingCallPendingIntent can't be null");
955         }
956
957         if (listener == null) {
958             throw new NullPointerException("listener can't be null");
959         }
960
961         int result = 0;
962
963         try {
964             result = mImsServiceProxy.startSession(incomingCallPendingIntent,
965                     createRegistrationListenerProxy(serviceClass, listener));
966         } catch (RemoteException e) {
967             throw new ImsException("open()", e,
968                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
969         }
970
971         if (result <= 0) {
972             // If the return value is a minus value,
973             // it means that an error occurred in the service.
974             // So, it needs to convert to the reason code specified in ImsReasonInfo.
975             throw new ImsException("open()", (result * (-1)));
976         }
977
978         return result;
979     }
980
981     /**
982      * Adds registration listener to the IMS service.
983      *
984      * @param serviceClass a service class specified in {@link ImsServiceClass}
985      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
986      * @param listener To listen to IMS registration events; It cannot be null
987      * @throws NullPointerException if {@code listener} is null
988      * @throws ImsException if calling the IMS service results in an error
989      */
990     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
991             throws ImsException {
992         checkAndThrowExceptionIfServiceUnavailable();
993
994         if (listener == null) {
995             throw new NullPointerException("listener can't be null");
996         }
997
998         try {
999             ImsRegistrationListenerProxy p = createRegistrationListenerProxy(serviceClass,
1000                     listener);
1001             mRegistrationListeners.add(p);
1002             mImsServiceProxy.addRegistrationListener(p);
1003         } catch (RemoteException e) {
1004             throw new ImsException("addRegistrationListener()", e,
1005                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1006         }
1007     }
1008
1009     /**
1010      * Removes the registration listener from the IMS service.
1011      *
1012      * @param listener Previously registered listener that will be removed. Can not be null.
1013      * @throws NullPointerException if {@code listener} is null
1014      * @throws ImsException if calling the IMS service results in an error
1015      * instead.
1016      */
1017     public void removeRegistrationListener(ImsConnectionStateListener listener)
1018             throws ImsException {
1019         checkAndThrowExceptionIfServiceUnavailable();
1020
1021         if (listener == null) {
1022             throw new NullPointerException("listener can't be null");
1023         }
1024
1025         try {
1026             Optional<ImsRegistrationListenerProxy> optionalProxy = mRegistrationListeners.stream()
1027                     .filter(l -> listener.equals(l.mListener)).findFirst();
1028             if(optionalProxy.isPresent()) {
1029                 ImsRegistrationListenerProxy p = optionalProxy.get();
1030                 mRegistrationListeners.remove(p);
1031                 mImsServiceProxy.removeRegistrationListener(p);
1032             }
1033         } catch (RemoteException e) {
1034             throw new ImsException("removeRegistrationListener()", e,
1035                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1036         }
1037     }
1038
1039     /**
1040      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
1041      * All the resources that were allocated to the service are also released.
1042      *
1043      * @param sessionId a session id to be closed which is obtained from {@link ImsManager#open}
1044      * @throws ImsException if calling the IMS service results in an error
1045      */
1046     public void close(int sessionId) throws ImsException {
1047         checkAndThrowExceptionIfServiceUnavailable();
1048
1049         try {
1050             mImsServiceProxy.endSession(sessionId);
1051         } catch (RemoteException e) {
1052             throw new ImsException("close()", e,
1053                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1054         } finally {
1055             mUt = null;
1056             mConfig = null;
1057             mEcbm = null;
1058             mMultiEndpoint = null;
1059         }
1060     }
1061
1062     /**
1063      * Gets the configuration interface to provision / withdraw the supplementary service settings.
1064      *
1065      * @return the Ut interface instance
1066      * @throws ImsException if getting the Ut interface results in an error
1067      */
1068     public ImsUtInterface getSupplementaryServiceConfiguration()
1069             throws ImsException {
1070         // FIXME: manage the multiple Ut interfaces based on the session id
1071         if (mUt == null || !mImsServiceProxy.isBinderAlive()) {
1072             checkAndThrowExceptionIfServiceUnavailable();
1073
1074             try {
1075                 IImsUt iUt = mImsServiceProxy.getUtInterface();
1076
1077                 if (iUt == null) {
1078                     throw new ImsException("getSupplementaryServiceConfiguration()",
1079                             ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
1080                 }
1081
1082                 mUt = new ImsUt(iUt);
1083             } catch (RemoteException e) {
1084                 throw new ImsException("getSupplementaryServiceConfiguration()", e,
1085                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1086             }
1087         }
1088
1089         return mUt;
1090     }
1091
1092     /**
1093      * Checks if the IMS service has successfully registered to the IMS network
1094      * with the specified service & call type.
1095      *
1096      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1097      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1098      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1099      * @param callType a call type that is specified in {@link ImsCallProfile}
1100      *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
1101      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1102      *        {@link ImsCallProfile#CALL_TYPE_VT}
1103      *        {@link ImsCallProfile#CALL_TYPE_VS}
1104      * @return true if the specified service id is connected to the IMS network;
1105      *        false otherwise
1106      * @throws ImsException if calling the IMS service results in an error
1107      */
1108     public boolean isConnected(int serviceType, int callType)
1109             throws ImsException {
1110         checkAndThrowExceptionIfServiceUnavailable();
1111
1112         try {
1113             return mImsServiceProxy.isConnected(serviceType, callType);
1114         } catch (RemoteException e) {
1115             throw new ImsException("isServiceConnected()", e,
1116                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1117         }
1118     }
1119
1120     /**
1121      * Checks if the specified IMS service is opend.
1122      *
1123      * @return true if the specified service id is opened; false otherwise
1124      * @throws ImsException if calling the IMS service results in an error
1125      */
1126     public boolean isOpened() throws ImsException {
1127         checkAndThrowExceptionIfServiceUnavailable();
1128
1129         try {
1130             return mImsServiceProxy.isOpened();
1131         } catch (RemoteException e) {
1132             throw new ImsException("isOpened()", e,
1133                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1134         }
1135     }
1136
1137     /**
1138      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1139      *
1140      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1141      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1142      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1143      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1144      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1145      * @param callType a call type that is specified in {@link ImsCallProfile}
1146      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1147      *        {@link ImsCallProfile#CALL_TYPE_VT}
1148      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1149      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1150      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1151      *        {@link ImsCallProfile#CALL_TYPE_VS}
1152      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1153      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1154      * @return a {@link ImsCallProfile} object
1155      * @throws ImsException if calling the IMS service results in an error
1156      */
1157     public ImsCallProfile createCallProfile(int sessionId, int serviceType, int callType)
1158             throws ImsException {
1159         checkAndThrowExceptionIfServiceUnavailable();
1160
1161         try {
1162             return mImsServiceProxy.createCallProfile(sessionId, serviceType, callType);
1163         } catch (RemoteException e) {
1164             throw new ImsException("createCallProfile()", e,
1165                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1166         }
1167     }
1168
1169     /**
1170      * Creates a {@link ImsCall} to make a call.
1171      *
1172      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1173      * @param profile a call profile to make the call
1174      *      (it contains service type, call type, media information, etc.)
1175      * @param participants participants to invite the conference call
1176      * @param listener listen to the call events from {@link ImsCall}
1177      * @return a {@link ImsCall} object
1178      * @throws ImsException if calling the IMS service results in an error
1179      */
1180     public ImsCall makeCall(int sessionId, ImsCallProfile profile, String[] callees,
1181             ImsCall.Listener listener) throws ImsException {
1182         if (DBG) {
1183             log("makeCall :: sessionId=" + sessionId
1184                     + ", profile=" + profile);
1185         }
1186
1187         checkAndThrowExceptionIfServiceUnavailable();
1188
1189         ImsCall call = new ImsCall(mContext, profile);
1190
1191         call.setListener(listener);
1192         ImsCallSession session = createCallSession(sessionId, profile);
1193
1194         if ((callees != null) && (callees.length == 1)) {
1195             call.start(session, callees[0]);
1196         } else {
1197             call.start(session, callees);
1198         }
1199
1200         return call;
1201     }
1202
1203     /**
1204      * Creates a {@link ImsCall} to take an incoming call.
1205      *
1206      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1207      * @param incomingCallIntent the incoming call broadcast intent
1208      * @param listener to listen to the call events from {@link ImsCall}
1209      * @return a {@link ImsCall} object
1210      * @throws ImsException if calling the IMS service results in an error
1211      */
1212     public ImsCall takeCall(int sessionId, Intent incomingCallIntent,
1213             ImsCall.Listener listener) throws ImsException {
1214         if (DBG) {
1215             log("takeCall :: sessionId=" + sessionId
1216                     + ", incomingCall=" + incomingCallIntent);
1217         }
1218
1219         checkAndThrowExceptionIfServiceUnavailable();
1220
1221         if (incomingCallIntent == null) {
1222             throw new ImsException("Can't retrieve session with null intent",
1223                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1224         }
1225
1226         int incomingServiceId = getImsSessionId(incomingCallIntent);
1227
1228         if (sessionId != incomingServiceId) {
1229             throw new ImsException("Service id is mismatched in the incoming call intent",
1230                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1231         }
1232
1233         String callId = getCallId(incomingCallIntent);
1234
1235         if (callId == null) {
1236             throw new ImsException("Call ID missing in the incoming call intent",
1237                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1238         }
1239
1240         try {
1241             IImsCallSession session = mImsServiceProxy.getPendingCallSession(sessionId, callId);
1242
1243             if (session == null) {
1244                 throw new ImsException("No pending session for the call",
1245                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
1246             }
1247
1248             ImsCall call = new ImsCall(mContext, session.getCallProfile());
1249
1250             call.attachSession(new ImsCallSession(session));
1251             call.setListener(listener);
1252
1253             return call;
1254         } catch (Throwable t) {
1255             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
1256         }
1257     }
1258
1259     /**
1260      * Gets the config interface to get/set service/capability parameters.
1261      *
1262      * @return the ImsConfig instance.
1263      * @throws ImsException if getting the setting interface results in an error.
1264      */
1265     public ImsConfig getConfigInterface() throws ImsException {
1266
1267         if (mConfig == null || !mImsServiceProxy.isBinderAlive()) {
1268             checkAndThrowExceptionIfServiceUnavailable();
1269
1270             try {
1271                 IImsConfig config = mImsServiceProxy.getConfigInterface();
1272                 if (config == null) {
1273                     throw new ImsException("getConfigInterface()",
1274                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
1275                 }
1276                 mConfig = new ImsConfig(config, mContext);
1277             } catch (RemoteException e) {
1278                 throw new ImsException("getConfigInterface()", e,
1279                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1280             }
1281         }
1282         if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
1283         return mConfig;
1284     }
1285
1286     public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete)
1287             throws ImsException {
1288
1289         checkAndThrowExceptionIfServiceUnavailable();
1290
1291         try {
1292             mImsServiceProxy.setUiTTYMode(uiTtyMode, onComplete);
1293         } catch (RemoteException e) {
1294             throw new ImsException("setTTYMode()", e,
1295                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1296         }
1297
1298         if (!getBooleanCarrierConfig(context,
1299                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
1300             setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
1301                     isEnhanced4gLteModeSettingEnabledByUser(context));
1302         }
1303     }
1304
1305     private ImsReasonInfo makeACopy(ImsReasonInfo imsReasonInfo) {
1306         Parcel p = Parcel.obtain();
1307         imsReasonInfo.writeToParcel(p, 0);
1308         p.setDataPosition(0);
1309         ImsReasonInfo clonedReasonInfo = ImsReasonInfo.CREATOR.createFromParcel(p);
1310         p.recycle();
1311         return clonedReasonInfo;
1312     }
1313
1314     /**
1315      * Get Recent IMS Disconnect Reasons.
1316      *
1317      * @return ArrayList of ImsReasonInfo objects. MAX size of the arraylist
1318      * is MAX_RECENT_DISCONNECT_REASONS. The objects are in the
1319      * chronological order.
1320      */
1321     public ArrayList<ImsReasonInfo> getRecentImsDisconnectReasons() {
1322         ArrayList<ImsReasonInfo> disconnectReasons = new ArrayList<>();
1323
1324         for (ImsReasonInfo reason : mRecentDisconnectReasons) {
1325             disconnectReasons.add(makeACopy(reason));
1326         }
1327         return disconnectReasons;
1328     }
1329     
1330     public int getImsServiceStatus() throws ImsException {
1331         checkAndThrowExceptionIfServiceUnavailable();
1332
1333         return mImsServiceProxy.getFeatureStatus();
1334     }
1335
1336     /**
1337      * Get the boolean config from carrier config manager.
1338      *
1339      * @param context the context to get carrier service
1340      * @param key config key defined in CarrierConfigManager
1341      * @return boolean value of corresponding key.
1342      */
1343     private static boolean getBooleanCarrierConfig(Context context, String key) {
1344         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1345                 Context.CARRIER_CONFIG_SERVICE);
1346         PersistableBundle b = null;
1347         if (configManager != null) {
1348             b = configManager.getConfig();
1349         }
1350         if (b != null) {
1351             return b.getBoolean(key);
1352         } else {
1353             // Return static default defined in CarrierConfigManager.
1354             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1355         }
1356     }
1357
1358     /**
1359      * Get the int config from carrier config manager.
1360      *
1361      * @param context the context to get carrier service
1362      * @param key config key defined in CarrierConfigManager
1363      * @return integer value of corresponding key.
1364      */
1365     private static int getIntCarrierConfig(Context context, String key) {
1366         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1367                 Context.CARRIER_CONFIG_SERVICE);
1368         PersistableBundle b = null;
1369         if (configManager != null) {
1370             b = configManager.getConfig();
1371         }
1372         if (b != null) {
1373             return b.getInt(key);
1374         } else {
1375             // Return static default defined in CarrierConfigManager.
1376             return CarrierConfigManager.getDefaultConfig().getInt(key);
1377         }
1378     }
1379
1380     /**
1381      * Gets the call ID from the specified incoming call broadcast intent.
1382      *
1383      * @param incomingCallIntent the incoming call broadcast intent
1384      * @return the call ID or null if the intent does not contain it
1385      */
1386     private static String getCallId(Intent incomingCallIntent) {
1387         if (incomingCallIntent == null) {
1388             return null;
1389         }
1390
1391         return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
1392     }
1393
1394     /**
1395      * Gets the service type from the specified incoming call broadcast intent.
1396      *
1397      * @param incomingCallIntent the incoming call broadcast intent
1398      * @return the session identifier or -1 if the intent does not contain it
1399      */
1400     private static int getImsSessionId(Intent incomingCallIntent) {
1401         if (incomingCallIntent == null) {
1402             return (-1);
1403         }
1404
1405         return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
1406     }
1407
1408     /**
1409      * Binds the IMS service only if the service is not created.
1410      */
1411     private void checkAndThrowExceptionIfServiceUnavailable()
1412             throws ImsException {
1413         if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
1414             createImsService();
1415
1416             if (mImsServiceProxy == null) {
1417                 throw new ImsException("Service is unavailable",
1418                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1419             }
1420         }
1421     }
1422
1423     /**
1424      * Binds the IMS service to make/receive the call. Supports two methods of exposing an
1425      * ImsService:
1426      * 1) com.android.ims.ImsService implementation in ServiceManager (deprecated).
1427      * 2) android.telephony.ims.ImsService implementation through ImsResolver.
1428      */
1429     private void createImsService() {
1430         if (!mConfigDynamicBind) {
1431             // Old method of binding
1432             Rlog.i(TAG, "Creating ImsService using ServiceManager");
1433             mImsServiceProxy = getServiceProxyCompat();
1434         } else {
1435             Rlog.i(TAG, "Creating ImsService using ImsResolver");
1436             mImsServiceProxy = getServiceProxy();
1437         }
1438     }
1439
1440     // Deprecated method of binding with the ImsService defined in the ServiceManager.
1441     private ImsServiceProxyCompat getServiceProxyCompat() {
1442         IBinder binder = ServiceManager.checkService(IMS_SERVICE);
1443
1444         if (binder != null) {
1445             try {
1446                 binder.linkToDeath(mDeathRecipient, 0);
1447             } catch (RemoteException e) {
1448             }
1449         }
1450
1451         return new ImsServiceProxyCompat(mPhoneId, binder);
1452     }
1453
1454     // New method of binding with the ImsResolver
1455     private ImsServiceProxy getServiceProxy() {
1456         TelephonyManager tm = (TelephonyManager)
1457                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1458         ImsServiceProxy serviceProxy = new ImsServiceProxy(mPhoneId, ImsFeature.MMTEL);
1459         // Returns null if the service is not available.
1460         IImsServiceController b = tm.getImsServiceControllerAndListen(mPhoneId,
1461                 ImsFeature.MMTEL, serviceProxy.getListener());
1462         if (b != null) {
1463             serviceProxy.setBinder(b.asBinder());
1464             serviceProxy.setStatusCallback(() -> mStatusCallbacks.forEach(
1465                             ImsServiceProxy.INotifyStatusChanged::notifyStatusChanged));
1466             // Trigger the cache to be updated for feature status.
1467             serviceProxy.getFeatureStatus();
1468         } else {
1469             Rlog.w(TAG, "getServiceProxy: b is null! Phone Id: " + mPhoneId);
1470         }
1471         return serviceProxy;
1472     }
1473
1474     /**
1475      * Creates a {@link ImsCallSession} with the specified call profile.
1476      * Use other methods, if applicable, instead of interacting with
1477      * {@link ImsCallSession} directly.
1478      *
1479      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1480      * @param profile a call profile to make the call
1481      */
1482     private ImsCallSession createCallSession(int serviceId,
1483             ImsCallProfile profile) throws ImsException {
1484         try {
1485             return new ImsCallSession(mImsServiceProxy.createCallSession(serviceId, profile, null));
1486         } catch (RemoteException e) {
1487             return null;
1488         }
1489     }
1490
1491     private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
1492             ImsConnectionStateListener listener) {
1493         ImsRegistrationListenerProxy proxy =
1494                 new ImsRegistrationListenerProxy(serviceClass, listener);
1495         return proxy;
1496     }
1497
1498     private static void log(String s) {
1499         Rlog.d(TAG, s);
1500     }
1501
1502     private static void loge(String s) {
1503         Rlog.e(TAG, s);
1504     }
1505
1506     private static void loge(String s, Throwable t) {
1507         Rlog.e(TAG, s, t);
1508     }
1509
1510     /**
1511      * Used for turning on IMS.if its off already
1512      */
1513     private void turnOnIms() throws ImsException {
1514         checkAndThrowExceptionIfServiceUnavailable();
1515
1516         try {
1517             mImsServiceProxy.turnOnIms();
1518         } catch (RemoteException e) {
1519             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1520         }
1521     }
1522
1523     private boolean isImsTurnOffAllowed() {
1524         return isTurnOffImsAllowedByPlatform(mContext)
1525                 && (!isWfcEnabledByPlatform(mContext)
1526                 || !isWfcEnabledByUser(mContext));
1527     }
1528
1529     private void setLteFeatureValues(boolean turnOn) {
1530         log("setLteFeatureValues: " + turnOn);
1531         try {
1532             ImsConfig config = getConfigInterface();
1533             if (config != null) {
1534                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
1535                         TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
1536
1537                 if (isVtEnabledByPlatform(mContext)) {
1538                     boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
1539                             CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1540                     boolean enableViLte = turnOn && isVtEnabledByUser(mContext) &&
1541                             (ignoreDataEnabledChanged || isDataEnabled());
1542                     config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1543                             TelephonyManager.NETWORK_TYPE_LTE,
1544                             enableViLte ? 1 : 0,
1545                             mImsConfigListener);
1546                 }
1547             }
1548         } catch (ImsException e) {
1549             loge("setLteFeatureValues: exception ", e);
1550         }
1551     }
1552
1553     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
1554         checkAndThrowExceptionIfServiceUnavailable();
1555
1556         // if turnOn: first set feature values then call turnOnIms()
1557         // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
1558         // allowed, first call turnOffIms() then set feature values
1559         if (turnOn) {
1560             setLteFeatureValues(turnOn);
1561             log("setAdvanced4GMode: turnOnIms");
1562             turnOnIms();
1563         } else {
1564             if (isImsTurnOffAllowed()) {
1565                 log("setAdvanced4GMode: turnOffIms");
1566                 turnOffIms();
1567             }
1568             setLteFeatureValues(turnOn);
1569         }
1570     }
1571
1572     /**
1573      * Used for turning off IMS completely in order to make the device CSFB'ed.
1574      * Once turned off, all calls will be over CS.
1575      */
1576     private void turnOffIms() throws ImsException {
1577         checkAndThrowExceptionIfServiceUnavailable();
1578
1579         try {
1580             mImsServiceProxy.turnOffIms();
1581         } catch (RemoteException e) {
1582             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1583         }
1584     }
1585
1586     private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
1587         if (reason == null) return;
1588         while (mRecentDisconnectReasons.size() >= MAX_RECENT_DISCONNECT_REASONS) {
1589             mRecentDisconnectReasons.removeFirst();
1590         }
1591         mRecentDisconnectReasons.addLast(reason);
1592     }
1593
1594     /**
1595      * Death recipient class for monitoring IMS service.
1596      */
1597     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
1598         @Override
1599         public void binderDied() {
1600             mImsServiceProxy = null;
1601             mUt = null;
1602             mConfig = null;
1603             mEcbm = null;
1604             mMultiEndpoint = null;
1605         }
1606     }
1607
1608     /**
1609      * Adapter class for {@link IImsRegistrationListener}.
1610      */
1611     private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
1612         private int mServiceClass;
1613         private ImsConnectionStateListener mListener;
1614
1615         public ImsRegistrationListenerProxy(int serviceClass,
1616                 ImsConnectionStateListener listener) {
1617             mServiceClass = serviceClass;
1618             mListener = listener;
1619         }
1620
1621         public boolean isSameProxy(int serviceClass) {
1622             return (mServiceClass == serviceClass);
1623         }
1624
1625         @Deprecated
1626         public void registrationConnected() {
1627             if (DBG) {
1628                 log("registrationConnected ::");
1629             }
1630
1631             if (mListener != null) {
1632                 mListener.onImsConnected();
1633             }
1634         }
1635
1636         @Deprecated
1637         public void registrationProgressing() {
1638             if (DBG) {
1639                 log("registrationProgressing ::");
1640             }
1641
1642             if (mListener != null) {
1643                 mListener.onImsProgressing();
1644             }
1645         }
1646
1647         @Override
1648         public void registrationConnectedWithRadioTech(int imsRadioTech) {
1649             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1650             //       values in ServiceState.java.
1651             if (DBG) {
1652                 log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
1653             }
1654
1655             if (mListener != null) {
1656                 mListener.onImsConnected();
1657                 mListener.onImsConnected(imsRadioTech);
1658             }
1659         }
1660
1661         @Override
1662         public void registrationProgressingWithRadioTech(int imsRadioTech) {
1663             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
1664             //       values in ServiceState.java.
1665             if (DBG) {
1666                 log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
1667             }
1668
1669             if (mListener != null) {
1670                 mListener.onImsProgressing();
1671             }
1672         }
1673
1674         @Override
1675         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
1676             if (DBG) {
1677                 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
1678             }
1679
1680             addToRecentDisconnectReasons(imsReasonInfo);
1681
1682             if (mListener != null) {
1683                 mListener.onImsDisconnected(imsReasonInfo);
1684             }
1685         }
1686
1687         @Override
1688         public void registrationResumed() {
1689             if (DBG) {
1690                 log("registrationResumed ::");
1691             }
1692
1693             if (mListener != null) {
1694                 mListener.onImsResumed();
1695             }
1696         }
1697
1698         @Override
1699         public void registrationSuspended() {
1700             if (DBG) {
1701                 log("registrationSuspended ::");
1702             }
1703
1704             if (mListener != null) {
1705                 mListener.onImsSuspended();
1706             }
1707         }
1708
1709         @Override
1710         public void registrationServiceCapabilityChanged(int serviceClass, int event) {
1711             log("registrationServiceCapabilityChanged :: serviceClass=" +
1712                     serviceClass + ", event=" + event);
1713
1714             if (mListener != null) {
1715                 mListener.onImsConnected();
1716             }
1717         }
1718
1719         @Override
1720         public void registrationFeatureCapabilityChanged(int serviceClass,
1721                 int[] enabledFeatures, int[] disabledFeatures) {
1722             log("registrationFeatureCapabilityChanged :: serviceClass=" +
1723                     serviceClass);
1724             if (mListener != null) {
1725                 mListener.onFeatureCapabilityChanged(serviceClass,
1726                         enabledFeatures, disabledFeatures);
1727             }
1728         }
1729
1730         @Override
1731         public void voiceMessageCountUpdate(int count) {
1732             log("voiceMessageCountUpdate :: count=" + count);
1733
1734             if (mListener != null) {
1735                 mListener.onVoiceMessageCountChanged(count);
1736             }
1737         }
1738
1739         @Override
1740         public void registrationAssociatedUriChanged(Uri[] uris) {
1741             if (DBG) log("registrationAssociatedUriChanged ::");
1742
1743             if (mListener != null) {
1744                 mListener.registrationAssociatedUriChanged(uris);
1745             }
1746         }
1747
1748         @Override
1749         public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
1750             if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
1751                     ", imsReasonInfo=" + imsReasonInfo);
1752
1753             if (mListener != null) {
1754                 mListener.onRegistrationChangeFailed(targetAccessTech, imsReasonInfo);
1755             }
1756         }
1757     }
1758
1759     /**
1760      * Gets the ECBM interface to request ECBM exit.
1761      *
1762      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1763      * @return the ECBM interface instance
1764      * @throws ImsException if getting the ECBM interface results in an error
1765      */
1766     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
1767         if (mEcbm == null || !mImsServiceProxy.isBinderAlive()) {
1768             checkAndThrowExceptionIfServiceUnavailable();
1769
1770             try {
1771                 IImsEcbm iEcbm = mImsServiceProxy.getEcbmInterface();
1772
1773                 if (iEcbm == null) {
1774                     throw new ImsException("getEcbmInterface()",
1775                             ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
1776                 }
1777                 mEcbm = new ImsEcbm(iEcbm);
1778             } catch (RemoteException e) {
1779                 throw new ImsException("getEcbmInterface()", e,
1780                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1781             }
1782         }
1783         return mEcbm;
1784     }
1785
1786     /**
1787      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
1788      *
1789      * @param serviceId a service id which is obtained from {@link ImsManager#open}
1790      * @return the multi-endpoint interface instance
1791      * @throws ImsException if getting the multi-endpoint interface results in an error
1792      */
1793     public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
1794         if (mMultiEndpoint == null || !mImsServiceProxy.isBinderAlive()) {
1795             checkAndThrowExceptionIfServiceUnavailable();
1796
1797             try {
1798                 IImsMultiEndpoint iImsMultiEndpoint = mImsServiceProxy.getMultiEndpointInterface();
1799
1800                 if (iImsMultiEndpoint == null) {
1801                     throw new ImsException("getMultiEndpointInterface()",
1802                             ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
1803                 }
1804                 mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
1805             } catch (RemoteException e) {
1806                 throw new ImsException("getMultiEndpointInterface()", e,
1807                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1808             }
1809         }
1810         return mMultiEndpoint;
1811     }
1812
1813     /**
1814      * Resets ImsManager settings back to factory defaults.
1815      *
1816      * @hide
1817      */
1818     public static void factoryReset(Context context) {
1819         // Set VoLTE to default
1820         android.provider.Settings.Global.putInt(context.getContentResolver(),
1821                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
1822                 ImsConfig.FeatureValueConstants.ON);
1823
1824         // Set VoWiFi to default
1825         android.provider.Settings.Global.putInt(context.getContentResolver(),
1826                 android.provider.Settings.Global.WFC_IMS_ENABLED,
1827                 getBooleanCarrierConfig(context,
1828                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
1829                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1830
1831         // Set VoWiFi mode to default
1832         android.provider.Settings.Global.putInt(context.getContentResolver(),
1833                 android.provider.Settings.Global.WFC_IMS_MODE,
1834                 getIntCarrierConfig(context,
1835                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
1836
1837         // Set VoWiFi roaming to default
1838         android.provider.Settings.Global.putInt(context.getContentResolver(),
1839                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1840                 getBooleanCarrierConfig(context,
1841                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
1842                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1843
1844         // Set VT to default
1845         android.provider.Settings.Global.putInt(context.getContentResolver(),
1846                 android.provider.Settings.Global.VT_IMS_ENABLED,
1847                 ImsConfig.FeatureValueConstants.ON);
1848
1849         // Push settings to ImsConfig
1850         ImsManager.updateImsServiceConfig(context,
1851                 SubscriptionManager.getDefaultVoicePhoneId(), true);
1852     }
1853
1854     private boolean isDataEnabled() {
1855         return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
1856     }
1857
1858     /**
1859      * Set data enabled/disabled flag.
1860      * @param enabled True if data is enabled, otherwise disabled.
1861      */
1862     public void setDataEnabled(boolean enabled) {
1863         log("setDataEnabled: " + enabled);
1864         SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
1865     }
1866
1867     private boolean isVolteProvisioned() {
1868         return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
1869     }
1870
1871     private void setVolteProvisionedProperty(boolean provisioned) {
1872         SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1873     }
1874
1875     private boolean isWfcProvisioned() {
1876         return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
1877     }
1878
1879     private void setWfcProvisionedProperty(boolean provisioned) {
1880         SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1881     }
1882
1883     private boolean isVtProvisioned() {
1884         return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
1885     }
1886
1887     private void setVtProvisionedProperty(boolean provisioned) {
1888         SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
1889     }
1890
1891     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1892         pw.println("ImsManager:");
1893         pw.println("  mPhoneId = " + mPhoneId);
1894         pw.println("  mConfigUpdated = " + mConfigUpdated);
1895         pw.println("  mImsServiceProxy = " + mImsServiceProxy);
1896         pw.println("  mDataEnabled = " + isDataEnabled());
1897         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(mContext,
1898                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
1899
1900         pw.println("  isGbaValid = " + isGbaValid(mContext));
1901         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
1902         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled(mContext));
1903
1904         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatform(mContext));
1905         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice(mContext));
1906         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
1907                 isEnhanced4gLteModeSettingEnabledByUser(mContext));
1908         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatform(mContext));
1909         pw.println("  isVtEnabledByUser = " + isVtEnabledByUser(mContext));
1910
1911         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatform(mContext));
1912         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUser(mContext));
1913         pw.println("  getWfcMode = " + getWfcMode(mContext));
1914         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser(mContext));
1915
1916         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDevice(mContext));
1917         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice(mContext));
1918         pw.flush();
1919     }
1920 }