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