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