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