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