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