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