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