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