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