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.content.Context;
21import android.content.Intent;
22import android.net.Uri;
23import android.os.AsyncTask;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.Looper;
27import android.os.Message;
28import android.os.Parcel;
29import android.os.PersistableBundle;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.SystemProperties;
33import android.provider.Settings;
34import android.telecom.TelecomManager;
35import android.telephony.CarrierConfigManager;
36import android.telephony.Rlog;
37import android.telephony.ServiceState;
38import android.telephony.SubscriptionManager;
39import android.telephony.TelephonyManager;
40import android.telephony.ims.ImsServiceProxy;
41import android.telephony.ims.ImsServiceProxyCompat;
42import android.telephony.ims.feature.ImsFeature;
43import android.util.Log;
44
45import com.android.ims.internal.IImsCallSession;
46import com.android.ims.internal.IImsConfig;
47import com.android.ims.internal.IImsEcbm;
48import com.android.ims.internal.IImsMultiEndpoint;
49import com.android.ims.internal.IImsRegistrationListener;
50import com.android.ims.internal.IImsServiceController;
51import com.android.ims.internal.IImsUt;
52import com.android.ims.internal.ImsCallSession;
53import com.android.ims.internal.IImsConfig;
54import com.android.internal.annotations.VisibleForTesting;
55import com.android.internal.telephony.ExponentialBackoff;
56
57import java.io.FileDescriptor;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60import java.util.HashMap;
61import java.util.concurrent.ConcurrentLinkedDeque;
62import java.util.HashSet;
63import java.util.Optional;
64import java.util.Set;
65
66/**
67 * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
68 * the operator's IMS network. This class is the starting point for any IMS actions.
69 * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
70 * <p>The APIs in this class allows you to:</p>
71 *
72 * @hide
73 */
74public class ImsManager {
75
76    /*
77     * Debug flag to override configuration flag
78     */
79    public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
80    public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
81    public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
82    public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
83    public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
84    public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
85    public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
86    public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
87
88    /**
89     * For accessing the IMS related service.
90     * Internal use only.
91     * @hide
92     */
93    private static final String IMS_SERVICE = "ims";
94
95    /**
96     * The result code to be sent back with the incoming call {@link PendingIntent}.
97     * @see #open(PendingIntent, ImsConnectionStateListener)
98     */
99    public static final int INCOMING_CALL_RESULT_CODE = 101;
100
101    /**
102     * Key to retrieve the call ID from an incoming call intent.
103     * @see #open(PendingIntent, ImsConnectionStateListener)
104     */
105    public static final String EXTRA_CALL_ID = "android:imsCallID";
106
107    /**
108     * Action to broadcast when ImsService is up.
109     * Internal use only.
110     * @deprecated
111     * @hide
112     */
113    public static final String ACTION_IMS_SERVICE_UP =
114            "com.android.ims.IMS_SERVICE_UP";
115
116    /**
117     * Action to broadcast when ImsService is down.
118     * Internal use only.
119     * @deprecated
120     * @hide
121     */
122    public static final String ACTION_IMS_SERVICE_DOWN =
123            "com.android.ims.IMS_SERVICE_DOWN";
124
125    /**
126     * Action to broadcast when ImsService registration fails.
127     * Internal use only.
128     * @hide
129     */
130    public static final String ACTION_IMS_REGISTRATION_ERROR =
131            "com.android.ims.REGISTRATION_ERROR";
132
133    /**
134     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
135     * A long value; the phone ID corresponding to the IMS service coming up or down.
136     * Internal use only.
137     * @hide
138     */
139    public static final String EXTRA_PHONE_ID = "android:phone_id";
140
141    /**
142     * Action for the incoming call intent for the Phone app.
143     * Internal use only.
144     * @hide
145     */
146    public static final String ACTION_IMS_INCOMING_CALL =
147            "com.android.ims.IMS_INCOMING_CALL";
148
149    /**
150     * Part of the ACTION_IMS_INCOMING_CALL intents.
151     * An integer value; service identifier obtained from {@link ImsManager#open}.
152     * Internal use only.
153     * @hide
154     */
155    public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
156
157    /**
158     * Part of the ACTION_IMS_INCOMING_CALL intents.
159     * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
160     * The value "true" indicates that the incoming call is for USSD.
161     * Internal use only.
162     * @hide
163     */
164    public static final String EXTRA_USSD = "android:ussd";
165
166    /**
167     * Part of the ACTION_IMS_INCOMING_CALL intents.
168     * A boolean value; Flag to indicate whether the call is an unknown
169     * dialing call. Such calls are originated by sending commands (like
170     * AT commands) directly to modem without Android involvement.
171     * Even though they are not incoming calls, they are propagated
172     * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
173     * Internal use only.
174     * @hide
175     */
176    public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
177
178    private static final String TAG = "ImsManager";
179    private static final boolean DBG = true;
180
181    private static HashMap<Integer, ImsManager> sImsManagerInstances =
182            new HashMap<Integer, ImsManager>();
183
184    private Context mContext;
185    private CarrierConfigManager mConfigManager;
186    private int mPhoneId;
187    private final boolean mConfigDynamicBind;
188    private ImsServiceProxyCompat mImsServiceProxy = null;
189    private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
190    // Ut interface for the supplementary service configuration
191    private ImsUt mUt = null;
192    // Interface to get/set ims config items
193    private ImsConfig mConfig = null;
194    private boolean mConfigUpdated = false;
195
196    private ImsConfigListener mImsConfigListener;
197
198    // ECBM interface
199    private ImsEcbm mEcbm = null;
200
201    private ImsMultiEndpoint mMultiEndpoint = null;
202
203    private Set<ImsServiceProxy.INotifyStatusChanged> mStatusCallbacks = new HashSet<>();
204
205    // Keep track of the ImsRegistrationListenerProxys that have been created so that we can
206    // remove them from the ImsService.
207    private final Set<ImsConnectionStateListener> mRegistrationListeners = new HashSet<>();
208
209    private final ImsRegistrationListenerProxy mRegistrationListenerProxy =
210            new ImsRegistrationListenerProxy();
211
212    // When true, we have registered the mRegistrationListenerProxy with the ImsService. Don't do
213    // it again.
214    private boolean mHasRegisteredForProxy = false;
215    private final Object mHasRegisteredLock = new Object();
216
217    // SystemProperties used as cache
218    private static final String VOLTE_PROVISIONED_PROP = "net.lte.ims.volte.provisioned";
219    private static final String WFC_PROVISIONED_PROP = "net.lte.ims.wfc.provisioned";
220    private static final String VT_PROVISIONED_PROP = "net.lte.ims.vt.provisioned";
221    // Flag indicating data enabled or not. This flag should be in sync with
222    // DcTracker.isDataEnabled(). The flag will be set later during boot up.
223    private static final String DATA_ENABLED_PROP = "net.lte.ims.data.enabled";
224
225    public static final String TRUE = "true";
226    public static final String FALSE = "false";
227
228    // mRecentDisconnectReasons stores the last 16 disconnect reasons
229    private static final int MAX_RECENT_DISCONNECT_REASONS = 16;
230    private ConcurrentLinkedDeque<ImsReasonInfo> mRecentDisconnectReasons =
231            new ConcurrentLinkedDeque<>();
232
233    // Exponential backoff for provisioning cache update. May be null for instances of ImsManager
234    // that are not on a thread supporting a looper.
235    private ExponentialBackoff mProvisionBackoff;
236    // Initial Provisioning check delay in ms
237    private static final long BACKOFF_INITIAL_DELAY_MS = 500;
238    // Max Provisioning check delay in ms (5 Minutes)
239    private static final long BACKOFF_MAX_DELAY_MS = 300000;
240    // Multiplier for exponential delay
241    private static final int BACKOFF_MULTIPLIER = 2;
242
243
244    /**
245     * Gets a manager instance.
246     *
247     * @param context application context for creating the manager object
248     * @param phoneId the phone ID for the IMS Service
249     * @return the manager instance corresponding to the phoneId
250     */
251    public static ImsManager getInstance(Context context, int phoneId) {
252        synchronized (sImsManagerInstances) {
253            if (sImsManagerInstances.containsKey(phoneId)) {
254                ImsManager m = sImsManagerInstances.get(phoneId);
255                // May be null for some tests
256                if (m != null) {
257                    m.connectIfServiceIsAvailable();
258                }
259                return m;
260            }
261
262            ImsManager mgr = new ImsManager(context, phoneId);
263            sImsManagerInstances.put(phoneId, mgr);
264
265            return mgr;
266        }
267    }
268
269    /**
270     * Returns the user configuration of Enhanced 4G LTE Mode setting.
271     *
272     * @deprecated Doesn't support MSIM devices. Use
273     * {@link #isEnhanced4gLteModeSettingEnabledByUserForSlot} instead.
274     */
275    public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
276        // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
277        // If user changes SIM from editable mode to uneditable mode, need to return true.
278        if (!getBooleanCarrierConfig(context,
279                    CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
280            return true;
281        }
282        int enabled = android.provider.Settings.Global.getInt(
283                context.getContentResolver(),
284                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
285                ImsConfig.FeatureValueConstants.ON);
286        return (enabled == 1) ? true : false;
287    }
288
289    /**
290     * Returns the user configuration of Enhanced 4G LTE Mode setting for slot.
291     */
292    public boolean isEnhanced4gLteModeSettingEnabledByUserForSlot() {
293        // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
294        // If user changes SIM from editable mode to uneditable mode, need to return true.
295        if (!getBooleanCarrierConfigForSlot(
296                CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
297            return true;
298        }
299        int enabled = android.provider.Settings.Global.getInt(
300                mContext.getContentResolver(),
301                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
302                ImsConfig.FeatureValueConstants.ON);
303        return (enabled == 1);
304    }
305
306    /**
307     * Change persistent Enhanced 4G LTE Mode setting.
308     *
309     * @deprecated Doesn't support MSIM devices. Use {@link #setEnhanced4gLteModeSettingForSlot}
310     * instead.
311     */
312    public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
313        int value = enabled ? 1 : 0;
314        android.provider.Settings.Global.putInt(
315                context.getContentResolver(),
316                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
317
318        if (isNonTtyOrTtyOnVolteEnabled(context)) {
319            ImsManager imsManager = ImsManager.getInstance(context,
320                    SubscriptionManager.getDefaultVoicePhoneId());
321            if (imsManager != null) {
322                try {
323                    imsManager.setAdvanced4GMode(enabled);
324                } catch (ImsException ie) {
325                    // do nothing
326                }
327            }
328        }
329    }
330
331    /**
332     * Change persistent Enhanced 4G LTE Mode setting. If the the option is not editable
333     * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), this method will
334     * always set the setting to true.
335     *
336     */
337    public void setEnhanced4gLteModeSettingForSlot(boolean enabled) {
338        // If false, we must always keep advanced 4G mode set to true (1).
339        int value = getBooleanCarrierConfigForSlot(
340                CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) ? (enabled ? 1: 0) : 1;
341
342        try {
343            int prevSetting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
344                    android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED);
345            if (prevSetting == value) {
346                // Don't trigger setAdvanced4GMode if the setting hasn't changed.
347                return;
348            }
349        } catch (Settings.SettingNotFoundException e) {
350            // Setting doesn't exist yet, so set it below.
351        }
352
353        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
354                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
355        if (isNonTtyOrTtyOnVolteEnabledForSlot()) {
356            try {
357                setAdvanced4GMode(enabled);
358            } catch (ImsException ie) {
359                // do nothing
360            }
361        }
362    }
363
364    /**
365     * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
366     * supported.
367     * @deprecated Does not support MSIM devices. Please use
368     * {@link #isNonTtyOrTtyOnVolteEnabledForSlot} instead.
369     */
370    public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
371        if (getBooleanCarrierConfig(context,
372                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
373            return true;
374        }
375
376        TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
377        if (tm == null) {
378            Log.w(TAG, "isNonTtyOrTtyOnVolteEnabled: telecom not available");
379            return true;
380        }
381        return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
382    }
383
384    /**
385     * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
386     * supported on a per slot basis.
387     */
388    public boolean isNonTtyOrTtyOnVolteEnabledForSlot() {
389        if (getBooleanCarrierConfigForSlot(
390                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
391            return true;
392        }
393
394        TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
395        if (tm == null) {
396            Log.w(TAG, "isNonTtyOrTtyOnVolteEnabledForSlot: telecom not available");
397            return true;
398        }
399        return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
400    }
401
402    /**
403     * Returns a platform configuration for VoLTE which may override the user setting.
404     * @deprecated Does not support MSIM devices. Please use
405     * {@link #isVolteEnabledByPlatformForSlot()} instead.
406     */
407    public static boolean isVolteEnabledByPlatform(Context context) {
408        if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
409                PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
410            return true;
411        }
412
413        return context.getResources().getBoolean(
414                com.android.internal.R.bool.config_device_volte_available)
415                && getBooleanCarrierConfig(context,
416                        CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
417                && isGbaValid(context);
418    }
419
420    /**
421     * Returns a platform configuration for VoLTE which may override the user setting on a per Slot
422     * basis.
423     */
424    public boolean isVolteEnabledByPlatformForSlot() {
425        if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
426                PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
427            return true;
428        }
429
430        return mContext.getResources().getBoolean(
431                com.android.internal.R.bool.config_device_volte_available)
432                && getBooleanCarrierConfigForSlot(
433                        CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
434                && isGbaValidForSlot();
435    }
436
437    /**
438     * Indicates whether VoLTE is provisioned on device.
439     *
440     * @deprecated Does not support MSIM devices. Please use
441     * {@link #isVolteProvisionedOnDeviceForSlot()} instead.
442     */
443    public static boolean isVolteProvisionedOnDevice(Context context) {
444        if (getBooleanCarrierConfig(context,
445                    CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
446            ImsManager mgr = ImsManager.getInstance(context,
447                    SubscriptionManager.getDefaultVoicePhoneId());
448            if (mgr != null) {
449                return mgr.isVolteProvisioned();
450            }
451        }
452
453        return true;
454    }
455
456    /**
457     * Indicates whether VoLTE is provisioned on this slot.
458     */
459    public boolean isVolteProvisionedOnDeviceForSlot() {
460        if (getBooleanCarrierConfigForSlot(
461                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
462            return isVolteProvisioned();
463        }
464
465        return true;
466    }
467
468    /**
469     * Indicates whether VoWifi is provisioned on device.
470     *
471     * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
472     * provisioned on device, this method returns false.
473     *
474     * @deprecated Does not support MSIM devices. Please use
475     * {@link #isWfcProvisionedOnDeviceForSlot()} instead.
476     */
477    public static boolean isWfcProvisionedOnDevice(Context context) {
478        if (getBooleanCarrierConfig(context,
479                CarrierConfigManager.KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL)) {
480            if (!isVolteProvisionedOnDevice(context)) {
481                return false;
482            }
483        }
484
485        if (getBooleanCarrierConfig(context,
486                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
487            ImsManager mgr = ImsManager.getInstance(context,
488                    SubscriptionManager.getDefaultVoicePhoneId());
489            if (mgr != null) {
490                return mgr.isWfcProvisioned();
491            }
492        }
493
494        return true;
495    }
496
497    /**
498     * Indicates whether VoWifi is provisioned on slot.
499     *
500     * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
501     * provisioned on device, this method returns false.
502     */
503    public boolean isWfcProvisionedOnDeviceForSlot() {
504        if (getBooleanCarrierConfigForSlot(
505                CarrierConfigManager.KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL)) {
506            if (!isVolteProvisionedOnDeviceForSlot()) {
507                return false;
508            }
509        }
510
511        if (getBooleanCarrierConfigForSlot(
512                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
513            return isWfcProvisioned();
514        }
515
516        return true;
517    }
518
519    /**
520     * Indicates whether VT is provisioned on device
521     *
522     * @deprecated Does not support MSIM devices. Please use
523     * {@link #isVtProvisionedOnDeviceForSlot()} instead.
524     */
525    public static boolean isVtProvisionedOnDevice(Context context) {
526        if (getBooleanCarrierConfig(context,
527                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
528            ImsManager mgr = ImsManager.getInstance(context,
529                    SubscriptionManager.getDefaultVoicePhoneId());
530            if (mgr != null) {
531                return mgr.isVtProvisioned();
532            }
533        }
534
535        return true;
536    }
537
538    /**
539     * Indicates whether VT is provisioned on slot.
540     */
541    public boolean isVtProvisionedOnDeviceForSlot() {
542        if (getBooleanCarrierConfigForSlot(
543                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
544            return isVtProvisioned();
545        }
546
547        return true;
548    }
549
550    /**
551     * Returns a platform configuration for VT which may override the user setting.
552     *
553     * Note: VT presumes that VoLTE is enabled (these are configuration settings
554     * which must be done correctly).
555     *
556     * @deprecated Does not support MSIM devices. Please use
557     * {@link #isVtEnabledByPlatformForSlot()} instead.
558     */
559    public static boolean isVtEnabledByPlatform(Context context) {
560        if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
561                PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
562            return true;
563        }
564
565        return
566                context.getResources().getBoolean(
567                        com.android.internal.R.bool.config_device_vt_available) &&
568                getBooleanCarrierConfig(context,
569                        CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
570                isGbaValid(context);
571    }
572
573    /**
574     * Returns a platform configuration for VT which may override the user setting.
575     *
576     * Note: VT presumes that VoLTE is enabled (these are configuration settings
577     * which must be done correctly).
578     */
579    public boolean isVtEnabledByPlatformForSlot() {
580        if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
581                PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
582            return true;
583        }
584
585        return mContext.getResources().getBoolean(
586                com.android.internal.R.bool.config_device_vt_available) &&
587                getBooleanCarrierConfigForSlot(
588                        CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
589                isGbaValidForSlot();
590    }
591
592    /**
593     * Returns the user configuration of VT setting
594     * @deprecated Does not support MSIM devices. Please use
595     * {@link #isVtEnabledByUserForSlot()} instead.
596     */
597    public static boolean isVtEnabledByUser(Context context) {
598        int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
599                android.provider.Settings.Global.VT_IMS_ENABLED,
600                ImsConfig.FeatureValueConstants.ON);
601        return (enabled == 1) ? true : false;
602    }
603
604    /**
605     * Returns the user configuration of VT setting per slot.
606     */
607    public boolean isVtEnabledByUserForSlot() {
608        int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
609                android.provider.Settings.Global.VT_IMS_ENABLED,
610                ImsConfig.FeatureValueConstants.ON);
611        return (enabled == 1);
612    }
613
614    /**
615     * Change persistent VT enabled setting
616     *
617     * @deprecated Does not support MSIM devices. Please use
618     * {@link #setVtSettingForSlot} instead.
619     */
620    public static void setVtSetting(Context context, boolean enabled) {
621        int value = enabled ? 1 : 0;
622        android.provider.Settings.Global.putInt(context.getContentResolver(),
623                android.provider.Settings.Global.VT_IMS_ENABLED, value);
624
625        ImsManager imsManager = ImsManager.getInstance(context,
626                SubscriptionManager.getDefaultVoicePhoneId());
627        if (imsManager != null) {
628            try {
629                ImsConfig config = imsManager.getConfigInterface();
630                config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
631                        TelephonyManager.NETWORK_TYPE_LTE,
632                        enabled ? ImsConfig.FeatureValueConstants.ON
633                                : ImsConfig.FeatureValueConstants.OFF,
634                        imsManager.mImsConfigListener);
635
636                if (enabled) {
637                    log("setVtSetting() : turnOnIms");
638                    imsManager.turnOnIms();
639                } else if (isTurnOffImsAllowedByPlatform(context)
640                        && (!isVolteEnabledByPlatform(context)
641                        || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
642                    log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
643                    imsManager.turnOffIms();
644                }
645            } catch (ImsException e) {
646                loge("setVtSetting(): ", e);
647            }
648        }
649    }
650
651    /**
652     * Change persistent VT enabled setting for slot.
653     */
654    public void setVtSettingForSlot(boolean enabled) {
655        int value = enabled ? 1 : 0;
656        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
657                android.provider.Settings.Global.VT_IMS_ENABLED, value);
658
659        try {
660            ImsConfig config = getConfigInterface();
661            config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
662                    TelephonyManager.NETWORK_TYPE_LTE,
663                    enabled ? ImsConfig.FeatureValueConstants.ON
664                            : ImsConfig.FeatureValueConstants.OFF,
665                    mImsConfigListener);
666
667            if (enabled) {
668                log("setVtSettingForSlot() : turnOnIms");
669                turnOnIms();
670            } else if (isVolteEnabledByPlatformForSlot()
671                    && (!isVolteEnabledByPlatformForSlot()
672                    || !isEnhanced4gLteModeSettingEnabledByUserForSlot())) {
673                log("setVtSettingForSlot() : imsServiceAllowTurnOff -> turnOffIms");
674                turnOffIms();
675            }
676        } catch (ImsException e) {
677            loge("setVtSettingForSlot(): ", e);
678        }
679    }
680
681    /**
682     * Returns whether turning off ims is allowed by platform.
683     * The platform property may override the carrier config.
684     *
685     * @deprecated Does not support MSIM devices. Please use
686     * {@link #isTurnOffImsAllowedByPlatformForSlot} instead.
687     */
688    private static boolean isTurnOffImsAllowedByPlatform(Context context) {
689        if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
690                PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
691            return true;
692        }
693        return getBooleanCarrierConfig(context,
694                CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
695    }
696
697    /**
698     * Returns whether turning off ims is allowed by platform.
699     * The platform property may override the carrier config.
700     */
701    private boolean isTurnOffImsAllowedByPlatformForSlot() {
702        if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
703                PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
704            return true;
705        }
706        return getBooleanCarrierConfigForSlot(
707                CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
708    }
709
710    /**
711     * Returns the user configuration of WFC setting
712     *
713     * @deprecated Does not support MSIM devices. Please use
714     * {@link #isTurnOffImsAllowedByPlatformForSlot} instead.
715     */
716    public static boolean isWfcEnabledByUser(Context context) {
717        int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
718                android.provider.Settings.Global.WFC_IMS_ENABLED,
719                getBooleanCarrierConfig(context,
720                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
721                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
722        return (enabled == 1) ? true : false;
723    }
724
725    /**
726     * Returns the user configuration of WFC setting for slot.
727     */
728    public boolean isWfcEnabledByUserForSlot() {
729        int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
730                android.provider.Settings.Global.WFC_IMS_ENABLED,
731                getBooleanCarrierConfigForSlot(
732                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
733                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
734        return enabled == 1;
735    }
736
737    /**
738     * Change persistent WFC enabled setting.
739     * @deprecated Does not support MSIM devices. Please use
740     * {@link #setWfcSettingForSlot} instead.
741     */
742    public static void setWfcSetting(Context context, boolean enabled) {
743        int value = enabled ? 1 : 0;
744        android.provider.Settings.Global.putInt(context.getContentResolver(),
745                android.provider.Settings.Global.WFC_IMS_ENABLED, value);
746
747        ImsManager imsManager = ImsManager.getInstance(context,
748                SubscriptionManager.getDefaultVoicePhoneId());
749        if (imsManager != null) {
750            try {
751                ImsConfig config = imsManager.getConfigInterface();
752                config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
753                        TelephonyManager.NETWORK_TYPE_IWLAN,
754                        enabled ? ImsConfig.FeatureValueConstants.ON
755                                : ImsConfig.FeatureValueConstants.OFF,
756                        imsManager.mImsConfigListener);
757
758                if (enabled) {
759                    log("setWfcSetting() : turnOnIms");
760                    imsManager.turnOnIms();
761                } else if (isTurnOffImsAllowedByPlatform(context)
762                        && (!isVolteEnabledByPlatform(context)
763                        || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
764                    log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
765                    imsManager.turnOffIms();
766                }
767
768                TelephonyManager tm = (TelephonyManager) context
769                        .getSystemService(Context.TELEPHONY_SERVICE);
770                setWfcModeInternal(context, enabled
771                        // Choose wfc mode per current roaming preference
772                        ? getWfcMode(context, tm.isNetworkRoaming())
773                        // Force IMS to register over LTE when turning off WFC
774                        : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
775            } catch (ImsException e) {
776                loge("setWfcSetting(): ", e);
777            }
778        }
779    }
780
781    /**
782     * Change persistent WFC enabled setting for slot.
783     */
784    public void setWfcSettingForSlot(boolean enabled) {
785        int value = enabled ? 1 : 0;
786        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
787                android.provider.Settings.Global.WFC_IMS_ENABLED, value);
788
789        setWfcNonPersistentForSlot(enabled, getWfcModeForSlot());
790    }
791
792    /**
793     * Non-persistently change WFC enabled setting and WFC mode for slot
794     *
795     * @param wfcMode The WFC preference if WFC is enabled
796     */
797    public void setWfcNonPersistentForSlot(boolean enabled, int wfcMode) {
798        int imsFeatureValue =
799                enabled ? ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF;
800        // Force IMS to register over LTE when turning off WFC
801        int imsWfcModeFeatureValue =
802                enabled ? wfcMode : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
803
804        try {
805            ImsConfig config = getConfigInterface();
806            config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
807                    TelephonyManager.NETWORK_TYPE_IWLAN,
808                    imsFeatureValue,
809                    mImsConfigListener);
810
811            if (enabled) {
812                log("setWfcSettingForSlot() : turnOnIms");
813                turnOnIms();
814            } else if (isTurnOffImsAllowedByPlatformForSlot()
815                    && (!isVolteEnabledByPlatformForSlot()
816                    || !isEnhanced4gLteModeSettingEnabledByUserForSlot())) {
817                log("setWfcSettingForSlot() : imsServiceAllowTurnOff -> turnOffIms");
818                turnOffIms();
819            }
820
821            setWfcModeInternalForSlot(imsWfcModeFeatureValue);
822        } catch (ImsException e) {
823            loge("setWfcSettingForSlot(): ", e);
824        }
825    }
826
827    /**
828     * Returns the user configuration of WFC preference setting.
829     *
830     * @deprecated Doesn't support MSIM devices. Use {@link #getWfcModeForSlot} instead.
831     */
832    public static int getWfcMode(Context context) {
833        int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
834                android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
835                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
836        if (DBG) log("getWfcMode - setting=" + setting);
837        return setting;
838    }
839
840    /**
841     * Returns the user configuration of WFC preference setting
842     */
843    public int getWfcModeForSlot() {
844        int setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
845                android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfigForSlot(
846                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
847        if (DBG) log("getWfcMode - setting=" + setting);
848        return setting;
849    }
850
851    /**
852     * Change persistent WFC preference setting.
853     *
854     * @deprecated Doesn't support MSIM devices. Use {@link #setWfcModeForSlot} instead.
855     */
856    public static void setWfcMode(Context context, int wfcMode) {
857        if (DBG) log("setWfcMode - setting=" + wfcMode);
858        android.provider.Settings.Global.putInt(context.getContentResolver(),
859                android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
860
861        setWfcModeInternal(context, wfcMode);
862    }
863
864    /**
865     * Change persistent WFC preference setting for slot.
866     */
867    public void setWfcModeForSlot(int wfcMode) {
868        if (DBG) log("setWfcModeForSlot - setting=" + wfcMode);
869        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
870                android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
871
872        setWfcModeInternalForSlot(wfcMode);
873    }
874
875    /**
876     * Returns the user configuration of WFC preference setting
877     *
878     * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
879     *
880     * @deprecated Doesn't support MSIM devices. Use {@link #getWfcModeForSlot} instead.
881     */
882    public static int getWfcMode(Context context, boolean roaming) {
883        int setting = 0;
884        if (!roaming) {
885            setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
886                    android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
887                            CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
888            if (DBG) log("getWfcMode - setting=" + setting);
889        } else {
890            setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
891                    android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
892                    getIntCarrierConfig(context,
893                            CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
894            if (DBG) log("getWfcMode (roaming) - setting=" + setting);
895        }
896        return setting;
897    }
898
899    /**
900     * Returns the user configuration of WFC preference setting for slot
901     *
902     * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
903     */
904    public int getWfcModeForSlot(boolean roaming) {
905        int setting = 0;
906        if (!roaming) {
907            setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
908                    android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfigForSlot(
909                            CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
910            if (DBG) log("getWfcModeForSlot - setting=" + setting);
911        } else {
912            setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
913                    android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
914                    getIntCarrierConfigForSlot(
915                            CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
916            if (DBG) log("getWfcModeForSlot (roaming) - setting=" + setting);
917        }
918        return setting;
919    }
920
921    /**
922     * Change persistent WFC preference setting
923     *
924     * @param roaming {@code false} for home network setting, {@code true} for roaming setting
925     *
926     * @deprecated Doesn't support MSIM devices. Please use {@link #setWfcModeForSlot} instead.
927     */
928    public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
929        if (!roaming) {
930            if (DBG) log("setWfcMode - setting=" + wfcMode);
931            android.provider.Settings.Global.putInt(context.getContentResolver(),
932                    android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
933        } else {
934            if (DBG) log("setWfcMode (roaming) - setting=" + wfcMode);
935            android.provider.Settings.Global.putInt(context.getContentResolver(),
936                    android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
937        }
938
939        TelephonyManager tm = (TelephonyManager)
940                context.getSystemService(Context.TELEPHONY_SERVICE);
941        if (roaming == tm.isNetworkRoaming()) {
942            setWfcModeInternal(context, wfcMode);
943        }
944    }
945
946    /**
947     * Change persistent WFC preference setting
948     *
949     * @param roaming {@code false} for home network setting, {@code true} for roaming setting
950     */
951    public void setWfcModeForSlot(int wfcMode, boolean roaming) {
952        if (!roaming) {
953            if (DBG) log("setWfcModeForSlot - setting=" + wfcMode);
954            android.provider.Settings.Global.putInt(mContext.getContentResolver(),
955                    android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
956        } else {
957            if (DBG) log("setWfcModeForSlot (roaming) - setting=" + wfcMode);
958            android.provider.Settings.Global.putInt(mContext.getContentResolver(),
959                    android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
960        }
961
962        int[] subIds = SubscriptionManager.getSubId(mPhoneId);
963        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
964        if (subIds != null && subIds.length >= 1) {
965            subId = subIds[0];
966        }
967        TelephonyManager tm = (TelephonyManager)
968                mContext.getSystemService(Context.TELEPHONY_SERVICE);
969        if (roaming == tm.isNetworkRoaming(subId)) {
970            setWfcModeInternalForSlot(wfcMode);
971        }
972    }
973
974    private static void setWfcModeInternal(Context context, int wfcMode) {
975        final ImsManager imsManager = ImsManager.getInstance(context,
976                SubscriptionManager.getDefaultVoicePhoneId());
977        if (imsManager != null) {
978            final int value = wfcMode;
979            Thread thread = new Thread(new Runnable() {
980                public void run() {
981                    try {
982                        imsManager.getConfigInterface().setProvisionedValue(
983                                ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
984                                value);
985                    } catch (ImsException e) {
986                        // do nothing
987                    }
988                }
989            });
990            thread.start();
991        }
992    }
993
994    private void setWfcModeInternalForSlot(int wfcMode) {
995        final int value = wfcMode;
996        Thread thread = new Thread(() -> {
997                try {
998                    getConfigInterface().setProvisionedValue(
999                            ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
1000                            value);
1001                } catch (ImsException e) {
1002                    // do nothing
1003                }
1004        });
1005        thread.start();
1006    }
1007
1008    /**
1009     * Returns the user configuration of WFC roaming setting
1010     *
1011     * @deprecated Does not support MSIM devices. Please use
1012     * {@link #isWfcRoamingEnabledByUserForSlot} instead.
1013     */
1014    public static boolean isWfcRoamingEnabledByUser(Context context) {
1015        int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
1016                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1017                getBooleanCarrierConfig(context,
1018                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
1019                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1020        return (enabled == 1) ? true : false;
1021    }
1022
1023    /**
1024     * Returns the user configuration of WFC roaming setting for slot
1025     */
1026    public boolean isWfcRoamingEnabledByUserForSlot() {
1027        int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
1028                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1029                getBooleanCarrierConfigForSlot(
1030                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
1031                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
1032        return (enabled == 1);
1033    }
1034
1035    /**
1036     * Change persistent WFC roaming enabled setting
1037     */
1038    public static void setWfcRoamingSetting(Context context, boolean enabled) {
1039        android.provider.Settings.Global.putInt(context.getContentResolver(),
1040                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1041                enabled ? ImsConfig.FeatureValueConstants.ON
1042                        : ImsConfig.FeatureValueConstants.OFF);
1043
1044        final ImsManager imsManager = ImsManager.getInstance(context,
1045                SubscriptionManager.getDefaultVoicePhoneId());
1046        if (imsManager != null) {
1047            imsManager.setWfcRoamingSettingInternal(enabled);
1048        }
1049    }
1050
1051    /**
1052     * Change persistent WFC roaming enabled setting
1053     */
1054    public void setWfcRoamingSettingForSlot(boolean enabled) {
1055        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
1056                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
1057                enabled ? ImsConfig.FeatureValueConstants.ON
1058                        : ImsConfig.FeatureValueConstants.OFF);
1059
1060        setWfcRoamingSettingInternal(enabled);
1061    }
1062
1063    private void setWfcRoamingSettingInternal(boolean enabled) {
1064        final int value = enabled
1065                ? ImsConfig.FeatureValueConstants.ON
1066                : ImsConfig.FeatureValueConstants.OFF;
1067        Thread thread = new Thread(() -> {
1068                try {
1069                    getConfigInterface().setProvisionedValue(
1070                            ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
1071                            value);
1072                } catch (ImsException e) {
1073                    // do nothing
1074                }
1075        });
1076        thread.start();
1077    }
1078
1079    /**
1080     * Returns a platform configuration for WFC which may override the user
1081     * setting. Note: WFC presumes that VoLTE is enabled (these are
1082     * configuration settings which must be done correctly).
1083     *
1084     * @deprecated Doesn't work for MSIM devices. Use {@link #isWfcEnabledByPlatformForSlot}
1085     * instead.
1086     */
1087    public static boolean isWfcEnabledByPlatform(Context context) {
1088        if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
1089                PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
1090            return true;
1091        }
1092
1093        return
1094               context.getResources().getBoolean(
1095                       com.android.internal.R.bool.config_device_wfc_ims_available) &&
1096               getBooleanCarrierConfig(context,
1097                       CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1098               isGbaValid(context);
1099    }
1100
1101    /**
1102     * Returns a platform configuration for WFC which may override the user
1103     * setting per slot. Note: WFC presumes that VoLTE is enabled (these are
1104     * configuration settings which must be done correctly).
1105     */
1106    public boolean isWfcEnabledByPlatformForSlot() {
1107        if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
1108                PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
1109            return true;
1110        }
1111
1112        return mContext.getResources().getBoolean(
1113                com.android.internal.R.bool.config_device_wfc_ims_available) &&
1114                getBooleanCarrierConfigForSlot(
1115                        CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1116                isGbaValidForSlot();
1117    }
1118
1119    /**
1120     * If carrier requires that IMS is only available if GBA capable SIM is used,
1121     * then this function checks GBA bit in EF IST.
1122     *
1123     * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1124     *
1125     * @deprecated Use {@link #isGbaValidForSlot} instead
1126     */
1127    private static boolean isGbaValid(Context context) {
1128        if (getBooleanCarrierConfig(context,
1129                CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1130            final TelephonyManager telephonyManager = TelephonyManager.getDefault();
1131            String efIst = telephonyManager.getIsimIst();
1132            if (efIst == null) {
1133                loge("ISF is NULL");
1134                return true;
1135            }
1136            boolean result = efIst != null && efIst.length() > 1 &&
1137                    (0x02 & (byte)efIst.charAt(1)) != 0;
1138            if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
1139            return result;
1140        }
1141        return true;
1142    }
1143
1144    /**
1145     * If carrier requires that IMS is only available if GBA capable SIM is used,
1146     * then this function checks GBA bit in EF IST.
1147     *
1148     * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1149     */
1150    private boolean isGbaValidForSlot() {
1151        if (getBooleanCarrierConfigForSlot(
1152                CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1153            final TelephonyManager telephonyManager = TelephonyManager.getDefault();
1154            String efIst = telephonyManager.getIsimIst();
1155            if (efIst == null) {
1156                loge("isGbaValidForSlot - ISF is NULL");
1157                return true;
1158            }
1159            boolean result = efIst != null && efIst.length() > 1 &&
1160                    (0x02 & (byte)efIst.charAt(1)) != 0;
1161            if (DBG) log("isGbaValidForSlot - GBA capable=" + result + ", ISF=" + efIst);
1162            return result;
1163        }
1164        return true;
1165    }
1166
1167    /**
1168     * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
1169     *
1170     * We cannot register receiver in ImsManager because this would lead to resource leak.
1171     * ImsManager can be created in different processes and it is not notified when that process
1172     * is about to be terminated.
1173     *
1174     * @hide
1175     * */
1176    public static void onProvisionedValueChanged(Context context, int item, String value) {
1177        if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
1178        ImsManager mgr = ImsManager.getInstance(context,
1179                SubscriptionManager.getDefaultVoicePhoneId());
1180
1181        switch (item) {
1182            case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
1183                mgr.setVolteProvisionedProperty(value.equals("1"));
1184                if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
1185                break;
1186
1187            case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
1188                mgr.setWfcProvisionedProperty(value.equals("1"));
1189                if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
1190                break;
1191
1192            case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
1193                mgr.setVtProvisionedProperty(value.equals("1"));
1194                if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
1195                break;
1196
1197        }
1198    }
1199
1200    private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Boolean> {
1201        @Override
1202        protected Boolean doInBackground(Void... params) {
1203            // disable on any error
1204            setVolteProvisionedProperty(false);
1205            setWfcProvisionedProperty(false);
1206            setVtProvisionedProperty(false);
1207
1208            try {
1209                ImsConfig config = getConfigInterface();
1210                if (config != null) {
1211                    setVolteProvisionedProperty(getProvisionedBool(config,
1212                            ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
1213                    if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
1214
1215                    setWfcProvisionedProperty(getProvisionedBool(config,
1216                            ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
1217                    if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
1218
1219                    setVtProvisionedProperty(getProvisionedBool(config,
1220                            ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
1221                    if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
1222
1223                }
1224            } catch (ImsException ie) {
1225                Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
1226                return false;
1227            }
1228
1229            return true;
1230        }
1231
1232        @Override
1233        protected void onPostExecute(Boolean completed) {
1234            if (mProvisionBackoff == null) {
1235                return;
1236            }
1237            if (!completed) {
1238                mProvisionBackoff.notifyFailed();
1239            } else {
1240                mProvisionBackoff.stop();
1241            }
1242        }
1243
1244        /**
1245         * Will return with config value or throw an ImsException if we receive an error from
1246         * ImsConfig for that value.
1247         */
1248        private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
1249            int value = config.getProvisionedValue(item);
1250            if (value == ImsConfig.FeatureValueConstants.ERROR) {
1251                throw new ImsException("getProvisionedBool failed with error for item: " + item,
1252                        ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
1253            }
1254            return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
1255        }
1256    }
1257
1258    // used internally only, use #updateProvisionedValues instead.
1259    private void handleUpdateProvisionedValues() {
1260        if (getBooleanCarrierConfigForSlot(
1261                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
1262
1263            new AsyncUpdateProvisionedValues().execute();
1264        }
1265    }
1266
1267    /**
1268     * Asynchronously get VoLTE, WFC, VT provisioning statuses. If ImsConfig is not available, we
1269     * will retry with exponential backoff.
1270     */
1271    private void updateProvisionedValues() {
1272        // Start trying to receive provisioning status after BACKOFF_INITIAL_DELAY_MS.
1273        if (mProvisionBackoff != null) {
1274            mProvisionBackoff.start();
1275        } else {
1276            // bypass and launch async thread once without backoff.
1277            handleUpdateProvisionedValues();
1278        }
1279    }
1280
1281    /**
1282     * Sync carrier config and user settings with ImsConfig.
1283     *
1284     * @param context for the manager object
1285     * @param phoneId phone id
1286     * @param force update
1287     *
1288     * @deprecated Doesn't support MSIM devices. Use {@link #updateImsServiceConfigForSlot} instead.
1289     */
1290    public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
1291        if (!force) {
1292            if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
1293                log("updateImsServiceConfig: SIM not ready");
1294                // Don't disable IMS if SIM is not ready
1295                return;
1296            }
1297        }
1298
1299        final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
1300        if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
1301            try {
1302                imsManager.updateProvisionedValues();
1303
1304                // TODO: Extend ImsConfig API and set all feature values in single function call.
1305
1306                // Note: currently the order of updates is set to produce different order of
1307                // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
1308                // differentiate this code path from vendor code perspective.
1309                boolean isImsUsed = imsManager.updateVolteFeatureValue();
1310                isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
1311                isImsUsed |= imsManager.updateVideoCallFeatureValue();
1312
1313                if (isImsUsed || !isTurnOffImsAllowedByPlatform(context)) {
1314                    // Turn on IMS if it is used.
1315                    // Also, if turning off is not allowed for current carrier,
1316                    // we need to turn IMS on because it might be turned off before
1317                    // phone switched to current carrier.
1318                    log("updateImsServiceConfig: turnOnIms");
1319                    imsManager.turnOnIms();
1320                } else {
1321                    // Turn off IMS if it is not used AND turning off is allowed for carrier.
1322                    log("updateImsServiceConfig: turnOffIms");
1323                    imsManager.turnOffIms();
1324                }
1325
1326                imsManager.mConfigUpdated = true;
1327            } catch (ImsException e) {
1328                loge("updateImsServiceConfig: ", e);
1329                imsManager.mConfigUpdated = false;
1330            }
1331        }
1332    }
1333
1334    /**
1335     * Sync carrier config and user settings with ImsConfig.
1336     *
1337     * @param context for the manager object
1338     * @param phoneId phone id
1339     * @param force update
1340     */
1341    public void updateImsServiceConfigForSlot(boolean force) {
1342        if (!force) {
1343            if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
1344                log("updateImsServiceConfigForSlot: SIM not ready");
1345                // Don't disable IMS if SIM is not ready
1346                return;
1347            }
1348        }
1349
1350        if (!mConfigUpdated || force) {
1351            try {
1352                updateProvisionedValues();
1353
1354                // TODO: Extend ImsConfig API and set all feature values in single function call.
1355
1356                // Note: currently the order of updates is set to produce different order of
1357                // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
1358                // differentiate this code path from vendor code perspective.
1359                boolean isImsUsed = updateVolteFeatureValue();
1360                isImsUsed |= updateWfcFeatureAndProvisionedValues();
1361                isImsUsed |= updateVideoCallFeatureValue();
1362
1363                if (isImsUsed || !isTurnOffImsAllowedByPlatformForSlot()) {
1364                    // Turn on IMS if it is used.
1365                    // Also, if turning off is not allowed for current carrier,
1366                    // we need to turn IMS on because it might be turned off before
1367                    // phone switched to current carrier.
1368                    log("updateImsServiceConfigForSlot: turnOnIms");
1369                    turnOnIms();
1370                } else {
1371                    // Turn off IMS if it is not used AND turning off is allowed for carrier.
1372                    log("updateImsServiceConfigForSlot: turnOffIms");
1373                    turnOffIms();
1374                }
1375
1376                mConfigUpdated = true;
1377            } catch (ImsException e) {
1378                loge("updateImsServiceConfigForSlot: ", e);
1379                mConfigUpdated = false;
1380            }
1381        }
1382    }
1383
1384    /**
1385     * Update VoLTE config
1386     * @return whether feature is On
1387     * @throws ImsException
1388     */
1389    private boolean updateVolteFeatureValue() throws ImsException {
1390        boolean available = isVolteEnabledByPlatformForSlot();
1391        boolean enabled = isEnhanced4gLteModeSettingEnabledByUserForSlot();
1392        boolean isNonTty = isNonTtyOrTtyOnVolteEnabledForSlot();
1393        boolean isFeatureOn = available && enabled && isNonTty;
1394
1395        log("updateVolteFeatureValue: available = " + available
1396                + ", enabled = " + enabled
1397                + ", nonTTY = " + isNonTty);
1398
1399        getConfigInterface().setFeatureValue(
1400                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
1401                TelephonyManager.NETWORK_TYPE_LTE,
1402                isFeatureOn ?
1403                        ImsConfig.FeatureValueConstants.ON :
1404                        ImsConfig.FeatureValueConstants.OFF,
1405                mImsConfigListener);
1406
1407        return isFeatureOn;
1408    }
1409
1410    /**
1411     * Update video call over LTE config
1412     * @return whether feature is On
1413     * @throws ImsException
1414     */
1415    private boolean updateVideoCallFeatureValue() throws ImsException {
1416        boolean available = isVtEnabledByPlatformForSlot();
1417        boolean enabled = isVtEnabledByUserForSlot();
1418        boolean isNonTty = isNonTtyOrTtyOnVolteEnabledForSlot();
1419        boolean isDataEnabled = isDataEnabled();
1420        boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
1421                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1422
1423        boolean isFeatureOn = available && enabled && isNonTty
1424                && (ignoreDataEnabledChanged || isDataEnabled);
1425
1426        log("updateVideoCallFeatureValue: available = " + available
1427                + ", enabled = " + enabled
1428                + ", nonTTY = " + isNonTty
1429                + ", data enabled = " + isDataEnabled);
1430
1431        getConfigInterface().setFeatureValue(
1432                ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1433                TelephonyManager.NETWORK_TYPE_LTE,
1434                isFeatureOn ?
1435                        ImsConfig.FeatureValueConstants.ON :
1436                        ImsConfig.FeatureValueConstants.OFF,
1437                mImsConfigListener);
1438
1439        return isFeatureOn;
1440    }
1441
1442    /**
1443     * Update WFC config
1444     * @return whether feature is On
1445     * @throws ImsException
1446     */
1447    private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
1448        boolean isNetworkRoaming = TelephonyManager.getDefault().isNetworkRoaming();
1449        boolean available = isWfcEnabledByPlatformForSlot();
1450        boolean enabled = isWfcEnabledByUserForSlot();
1451        int mode = getWfcModeForSlot(isNetworkRoaming);
1452        boolean roaming = isWfcRoamingEnabledByUserForSlot();
1453        boolean isFeatureOn = available && enabled;
1454
1455        log("updateWfcFeatureAndProvisionedValues: available = " + available
1456                + ", enabled = " + enabled
1457                + ", mode = " + mode
1458                + ", roaming = " + roaming);
1459
1460        getConfigInterface().setFeatureValue(
1461                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
1462                TelephonyManager.NETWORK_TYPE_IWLAN,
1463                isFeatureOn ?
1464                        ImsConfig.FeatureValueConstants.ON :
1465                        ImsConfig.FeatureValueConstants.OFF,
1466                mImsConfigListener);
1467
1468        if (!isFeatureOn) {
1469            mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
1470            roaming = false;
1471        }
1472        setWfcModeInternal(mContext, mode);
1473        setWfcRoamingSettingInternal(roaming);
1474
1475        return isFeatureOn;
1476    }
1477
1478    /**
1479     * Do NOT use this directly, instead use {@link #getInstance}.
1480     */
1481    @VisibleForTesting
1482    public ImsManager(Context context, int phoneId) {
1483        mContext = context;
1484        mPhoneId = phoneId;
1485        mConfigDynamicBind = mContext.getResources().getBoolean(
1486                com.android.internal.R.bool.config_dynamic_bind_ims);
1487        mConfigManager = (CarrierConfigManager) context.getSystemService(
1488                Context.CARRIER_CONFIG_SERVICE);
1489        if (Looper.getMainLooper() != null) {
1490            mProvisionBackoff = new ExponentialBackoff(BACKOFF_INITIAL_DELAY_MS,
1491                    BACKOFF_MAX_DELAY_MS, BACKOFF_MULTIPLIER,
1492                    new Handler(Looper.getMainLooper()), this::handleUpdateProvisionedValues);
1493        }
1494        createImsService();
1495    }
1496
1497    /**
1498     * @return Whether or not ImsManager is configured to Dynamically bind or not to support legacy
1499     * devices.
1500     */
1501    public boolean isDynamicBinding() {
1502        return mConfigDynamicBind;
1503    }
1504
1505    /*
1506     * Returns a flag indicating whether the IMS service is available. If it is not available,
1507     * it will try to connect before reporting failure.
1508     */
1509    public boolean isServiceAvailable() {
1510        connectIfServiceIsAvailable();
1511        // mImsServiceProxy will always create an ImsServiceProxy.
1512        return mImsServiceProxy.isBinderAlive();
1513    }
1514
1515    /**
1516     * If the service is available, try to reconnect.
1517     */
1518    public void connectIfServiceIsAvailable() {
1519        if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
1520            createImsService();
1521        }
1522    }
1523
1524    public void setImsConfigListener(ImsConfigListener listener) {
1525        mImsConfigListener = listener;
1526    }
1527
1528
1529    /**
1530     * Adds a callback for status changed events if the binder is already available. If it is not,
1531     * this method will throw an ImsException.
1532     */
1533    public void addNotifyStatusChangedCallbackIfAvailable(ImsServiceProxy.INotifyStatusChanged c)
1534            throws ImsException {
1535        if (!mImsServiceProxy.isBinderAlive()) {
1536            throw new ImsException("Binder is not active!",
1537                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1538        }
1539        if (c != null) {
1540            mStatusCallbacks.add(c);
1541        }
1542    }
1543
1544    /**
1545     * Opens the IMS service for making calls and/or receiving generic IMS calls.
1546     * The caller may make subsquent calls through {@link #makeCall}.
1547     * The IMS service will register the device to the operator's network with the credentials
1548     * (from ISIM) periodically in order to receive calls from the operator's network.
1549     * When the IMS service receives a new call, it will send out an intent with
1550     * the provided action string.
1551     * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
1552     *
1553     * @param serviceClass a service class specified in {@link ImsServiceClass}
1554     *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
1555     * @param incomingCallPendingIntent When an incoming call is received,
1556     *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
1557     *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
1558     *        as the result code and the intent to fill in the call ID; It cannot be null
1559     * @param listener To listen to IMS registration events; It cannot be null
1560     * @return identifier (greater than 0) for the specified service
1561     * @throws NullPointerException if {@code incomingCallPendingIntent}
1562     *      or {@code listener} is null
1563     * @throws ImsException if calling the IMS service results in an error
1564     * @see #getCallId
1565     * @see #getImsSessionId
1566     */
1567    public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
1568            ImsConnectionStateListener listener) throws ImsException {
1569        checkAndThrowExceptionIfServiceUnavailable();
1570
1571        if (incomingCallPendingIntent == null) {
1572            throw new NullPointerException("incomingCallPendingIntent can't be null");
1573        }
1574
1575        if (listener == null) {
1576            throw new NullPointerException("listener can't be null");
1577        }
1578
1579        int result = 0;
1580
1581        try {
1582            // Register a stub implementation of the ImsRegistrationListener. There is the
1583            // possibility that if we use the real implementation of the ImsRegistrationListener,
1584            // it will be added twice.
1585            // TODO: Remove ImsRegistrationListener from startSession API (b/62588776)
1586            result = mImsServiceProxy.startSession(incomingCallPendingIntent,
1587                    new ImsRegistrationListenerBase());
1588            addRegistrationListener(listener);
1589            log("open: Session started and registration listener added.");
1590        } catch (RemoteException e) {
1591            throw new ImsException("open()", e,
1592                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1593        }
1594
1595        if (result <= 0) {
1596            // If the return value is a minus value,
1597            // it means that an error occurred in the service.
1598            // So, it needs to convert to the reason code specified in ImsReasonInfo.
1599            throw new ImsException("open()", (result * (-1)));
1600        }
1601
1602        return result;
1603    }
1604
1605    /**
1606     * Adds registration listener to the IMS service.
1607     *
1608     * @param serviceClass a service class specified in {@link ImsServiceClass}
1609     *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
1610     * @param listener To listen to IMS registration events; It cannot be null
1611     * @throws NullPointerException if {@code listener} is null
1612     * @throws ImsException if calling the IMS service results in an error
1613     *
1614     * @deprecated Use {@link #addRegistrationListener(ImsConnectionStateListener)} instead.
1615     */
1616    public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
1617            throws ImsException {
1618        addRegistrationListener(listener);
1619    }
1620
1621    /**
1622     * Adds registration listener to the IMS service.
1623     *
1624     * @param listener To listen to IMS registration events; It cannot be null
1625     * @throws NullPointerException if {@code listener} is null
1626     * @throws ImsException if calling the IMS service results in an error
1627     */
1628    public void addRegistrationListener(ImsConnectionStateListener listener)
1629            throws ImsException {
1630
1631        if (listener == null) {
1632            throw new NullPointerException("listener can't be null");
1633        }
1634        // We only want this Proxy registered once.
1635        synchronized (mHasRegisteredLock) {
1636            if (!mHasRegisteredForProxy) {
1637                try {
1638                    checkAndThrowExceptionIfServiceUnavailable();
1639                    mImsServiceProxy.addRegistrationListener(mRegistrationListenerProxy);
1640                    log("RegistrationListenerProxy registered.");
1641                    // Only record if there isn't a RemoteException.
1642                    mHasRegisteredForProxy = true;
1643                } catch (RemoteException e) {
1644                    throw new ImsException("addRegistrationListener()", e,
1645                            ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1646                }
1647            }
1648        }
1649        synchronized (mRegistrationListeners) {
1650            log("Local registration listener added: " + listener);
1651            mRegistrationListeners.add(listener);
1652        }
1653    }
1654
1655    /**
1656     * Removes the registration listener from the IMS service.
1657     *
1658     * @param listener Previously registered listener that will be removed. Can not be null.
1659     * @throws NullPointerException if {@code listener} is null
1660     * @throws ImsException if calling the IMS service results in an error
1661     * instead.
1662     */
1663    public void removeRegistrationListener(ImsConnectionStateListener listener)
1664            throws ImsException {
1665        if (listener == null) {
1666            throw new NullPointerException("listener can't be null");
1667        }
1668
1669        synchronized (mRegistrationListeners) {
1670            log("Local registration listener removed: " + listener);
1671            mRegistrationListeners.remove(listener);
1672        }
1673    }
1674
1675    /**
1676     * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
1677     * All the resources that were allocated to the service are also released.
1678     *
1679     * @param sessionId a session id to be closed which is obtained from {@link ImsManager#open}
1680     * @throws ImsException if calling the IMS service results in an error
1681     */
1682    public void close(int sessionId) throws ImsException {
1683        checkAndThrowExceptionIfServiceUnavailable();
1684
1685        try {
1686            mImsServiceProxy.endSession(sessionId);
1687        } catch (RemoteException e) {
1688            throw new ImsException("close()", e,
1689                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1690        } finally {
1691            mUt = null;
1692            mConfig = null;
1693            mEcbm = null;
1694            mMultiEndpoint = null;
1695        }
1696    }
1697
1698    /**
1699     * Gets the configuration interface to provision / withdraw the supplementary service settings.
1700     *
1701     * @return the Ut interface instance
1702     * @throws ImsException if getting the Ut interface results in an error
1703     */
1704    public ImsUtInterface getSupplementaryServiceConfiguration()
1705            throws ImsException {
1706        // FIXME: manage the multiple Ut interfaces based on the session id
1707        if (mUt != null && mUt.isBinderAlive()) {
1708            return mUt;
1709        }
1710
1711        checkAndThrowExceptionIfServiceUnavailable();
1712        try {
1713            IImsUt iUt = mImsServiceProxy.getUtInterface();
1714
1715            if (iUt == null) {
1716                throw new ImsException("getSupplementaryServiceConfiguration()",
1717                        ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
1718            }
1719
1720            mUt = new ImsUt(iUt);
1721        } catch (RemoteException e) {
1722            throw new ImsException("getSupplementaryServiceConfiguration()", e,
1723                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1724        }
1725        return mUt;
1726    }
1727
1728    /**
1729     * Checks if the IMS service has successfully registered to the IMS network
1730     * with the specified service & call type.
1731     *
1732     * @param serviceType a service type that is specified in {@link ImsCallProfile}
1733     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1734     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1735     * @param callType a call type that is specified in {@link ImsCallProfile}
1736     *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
1737     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1738     *        {@link ImsCallProfile#CALL_TYPE_VT}
1739     *        {@link ImsCallProfile#CALL_TYPE_VS}
1740     * @return true if the specified service id is connected to the IMS network;
1741     *        false otherwise
1742     * @throws ImsException if calling the IMS service results in an error
1743     */
1744    public boolean isConnected(int serviceType, int callType)
1745            throws ImsException {
1746        checkAndThrowExceptionIfServiceUnavailable();
1747
1748        try {
1749            return mImsServiceProxy.isConnected(serviceType, callType);
1750        } catch (RemoteException e) {
1751            throw new ImsException("isServiceConnected()", e,
1752                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1753        }
1754    }
1755
1756    /**
1757     * Checks if the specified IMS service is opend.
1758     *
1759     * @return true if the specified service id is opened; false otherwise
1760     * @throws ImsException if calling the IMS service results in an error
1761     */
1762    public boolean isOpened() throws ImsException {
1763        checkAndThrowExceptionIfServiceUnavailable();
1764
1765        try {
1766            return mImsServiceProxy.isOpened();
1767        } catch (RemoteException e) {
1768            throw new ImsException("isOpened()", e,
1769                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1770        }
1771    }
1772
1773    /**
1774     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1775     *
1776     * @param sessionId a session id which is obtained from {@link ImsManager#open}
1777     * @param serviceType a service type that is specified in {@link ImsCallProfile}
1778     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1779     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1780     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1781     * @param callType a call type that is specified in {@link ImsCallProfile}
1782     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1783     *        {@link ImsCallProfile#CALL_TYPE_VT}
1784     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1785     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1786     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1787     *        {@link ImsCallProfile#CALL_TYPE_VS}
1788     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1789     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1790     * @return a {@link ImsCallProfile} object
1791     * @throws ImsException if calling the IMS service results in an error
1792     */
1793    public ImsCallProfile createCallProfile(int sessionId, int serviceType, int callType)
1794            throws ImsException {
1795        checkAndThrowExceptionIfServiceUnavailable();
1796
1797        try {
1798            return mImsServiceProxy.createCallProfile(sessionId, serviceType, callType);
1799        } catch (RemoteException e) {
1800            throw new ImsException("createCallProfile()", e,
1801                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1802        }
1803    }
1804
1805    /**
1806     * Creates a {@link ImsCall} to make a call.
1807     *
1808     * @param sessionId a session id which is obtained from {@link ImsManager#open}
1809     * @param profile a call profile to make the call
1810     *      (it contains service type, call type, media information, etc.)
1811     * @param participants participants to invite the conference call
1812     * @param listener listen to the call events from {@link ImsCall}
1813     * @return a {@link ImsCall} object
1814     * @throws ImsException if calling the IMS service results in an error
1815     */
1816    public ImsCall makeCall(int sessionId, ImsCallProfile profile, String[] callees,
1817            ImsCall.Listener listener) throws ImsException {
1818        if (DBG) {
1819            log("makeCall :: sessionId=" + sessionId
1820                    + ", profile=" + profile);
1821        }
1822
1823        checkAndThrowExceptionIfServiceUnavailable();
1824
1825        ImsCall call = new ImsCall(mContext, profile);
1826
1827        call.setListener(listener);
1828        ImsCallSession session = createCallSession(sessionId, profile);
1829
1830        if ((callees != null) && (callees.length == 1)) {
1831            call.start(session, callees[0]);
1832        } else {
1833            call.start(session, callees);
1834        }
1835
1836        return call;
1837    }
1838
1839    /**
1840     * Creates a {@link ImsCall} to take an incoming call.
1841     *
1842     * @param sessionId a session id which is obtained from {@link ImsManager#open}
1843     * @param incomingCallIntent the incoming call broadcast intent
1844     * @param listener to listen to the call events from {@link ImsCall}
1845     * @return a {@link ImsCall} object
1846     * @throws ImsException if calling the IMS service results in an error
1847     */
1848    public ImsCall takeCall(int sessionId, Intent incomingCallIntent,
1849            ImsCall.Listener listener) throws ImsException {
1850        if (DBG) {
1851            log("takeCall :: sessionId=" + sessionId
1852                    + ", incomingCall=" + incomingCallIntent);
1853        }
1854
1855        checkAndThrowExceptionIfServiceUnavailable();
1856
1857        if (incomingCallIntent == null) {
1858            throw new ImsException("Can't retrieve session with null intent",
1859                    ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1860        }
1861
1862        int incomingServiceId = getImsSessionId(incomingCallIntent);
1863
1864        if (sessionId != incomingServiceId) {
1865            throw new ImsException("Service id is mismatched in the incoming call intent",
1866                    ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1867        }
1868
1869        String callId = getCallId(incomingCallIntent);
1870
1871        if (callId == null) {
1872            throw new ImsException("Call ID missing in the incoming call intent",
1873                    ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1874        }
1875
1876        try {
1877            IImsCallSession session = mImsServiceProxy.getPendingCallSession(sessionId, callId);
1878
1879            if (session == null) {
1880                throw new ImsException("No pending session for the call",
1881                        ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
1882            }
1883
1884            ImsCall call = new ImsCall(mContext, session.getCallProfile());
1885
1886            call.attachSession(new ImsCallSession(session));
1887            call.setListener(listener);
1888
1889            return call;
1890        } catch (Throwable t) {
1891            throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
1892        }
1893    }
1894
1895    /**
1896     * Gets the config interface to get/set service/capability parameters.
1897     *
1898     * @return the ImsConfig instance.
1899     * @throws ImsException if getting the setting interface results in an error.
1900     */
1901    public ImsConfig getConfigInterface() throws ImsException {
1902        if (mConfig != null && mConfig.isBinderAlive()) {
1903            return mConfig;
1904        }
1905
1906        checkAndThrowExceptionIfServiceUnavailable();
1907        try {
1908            IImsConfig config = mImsServiceProxy.getConfigInterface();
1909            if (config == null) {
1910                throw new ImsException("getConfigInterface()",
1911                        ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
1912            }
1913            mConfig = new ImsConfig(config, mContext);
1914        } catch (RemoteException e) {
1915            throw new ImsException("getConfigInterface()", e,
1916                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1917        }
1918        return mConfig;
1919    }
1920
1921    /**
1922     * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
1923     */
1924    public void setTtyMode(int ttyMode) throws ImsException {
1925        if (!getBooleanCarrierConfigForSlot(
1926                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
1927            setAdvanced4GMode((ttyMode == TelecomManager.TTY_MODE_OFF) &&
1928                    isEnhanced4gLteModeSettingEnabledByUserForSlot());
1929        }
1930    }
1931
1932    /**
1933     * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
1934     * settings screen.
1935     */
1936    public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete)
1937            throws ImsException {
1938
1939        checkAndThrowExceptionIfServiceUnavailable();
1940
1941        try {
1942            mImsServiceProxy.setUiTTYMode(uiTtyMode, onComplete);
1943        } catch (RemoteException e) {
1944            throw new ImsException("setTTYMode()", e,
1945                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1946        }
1947    }
1948
1949    private ImsReasonInfo makeACopy(ImsReasonInfo imsReasonInfo) {
1950        Parcel p = Parcel.obtain();
1951        imsReasonInfo.writeToParcel(p, 0);
1952        p.setDataPosition(0);
1953        ImsReasonInfo clonedReasonInfo = ImsReasonInfo.CREATOR.createFromParcel(p);
1954        p.recycle();
1955        return clonedReasonInfo;
1956    }
1957
1958    /**
1959     * Get Recent IMS Disconnect Reasons.
1960     *
1961     * @return ArrayList of ImsReasonInfo objects. MAX size of the arraylist
1962     * is MAX_RECENT_DISCONNECT_REASONS. The objects are in the
1963     * chronological order.
1964     */
1965    public ArrayList<ImsReasonInfo> getRecentImsDisconnectReasons() {
1966        ArrayList<ImsReasonInfo> disconnectReasons = new ArrayList<>();
1967
1968        for (ImsReasonInfo reason : mRecentDisconnectReasons) {
1969            disconnectReasons.add(makeACopy(reason));
1970        }
1971        return disconnectReasons;
1972    }
1973
1974    public int getImsServiceStatus() throws ImsException {
1975        return mImsServiceProxy.getFeatureStatus();
1976    }
1977
1978    /**
1979     * Get the boolean config from carrier config manager.
1980     *
1981     * @param context the context to get carrier service
1982     * @param key config key defined in CarrierConfigManager
1983     * @return boolean value of corresponding key.
1984     *
1985     * @deprecated Does not support MSIM devices. Use
1986     * {@link #getBooleanCarrierConfigForSlot(Context, String)} instead.
1987     */
1988    private static boolean getBooleanCarrierConfig(Context context, String key) {
1989        CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1990                Context.CARRIER_CONFIG_SERVICE);
1991        PersistableBundle b = null;
1992        if (configManager != null) {
1993            b = configManager.getConfig();
1994        }
1995        if (b != null) {
1996            return b.getBoolean(key);
1997        } else {
1998            // Return static default defined in CarrierConfigManager.
1999            return CarrierConfigManager.getDefaultConfig().getBoolean(key);
2000        }
2001    }
2002
2003    /**
2004     * Get the boolean config from carrier config manager.
2005     *
2006     * @param key config key defined in CarrierConfigManager
2007     * @return boolean value of corresponding key.
2008     */
2009    private boolean getBooleanCarrierConfigForSlot(String key) {
2010        int[] subIds = SubscriptionManager.getSubId(mPhoneId);
2011        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
2012        if (subIds != null && subIds.length >= 1) {
2013            subId = subIds[0];
2014        }
2015        PersistableBundle b = null;
2016        if (mConfigManager != null) {
2017            // If an invalid subId is used, this bundle will contain default values.
2018            b = mConfigManager.getConfigForSubId(subId);
2019        }
2020        if (b != null) {
2021            return b.getBoolean(key);
2022        } else {
2023            // Return static default defined in CarrierConfigManager.
2024            return CarrierConfigManager.getDefaultConfig().getBoolean(key);
2025        }
2026    }
2027
2028    /**
2029     * Get the int config from carrier config manager.
2030     *
2031     * @param context the context to get carrier service
2032     * @param key config key defined in CarrierConfigManager
2033     * @return integer value of corresponding key.
2034     *
2035     * @deprecated Doesn't support MSIM devices. Use {@link #getIntCarrierConfigForSlot} instead.
2036     */
2037    private static int getIntCarrierConfig(Context context, String key) {
2038        CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
2039                Context.CARRIER_CONFIG_SERVICE);
2040        PersistableBundle b = null;
2041        if (configManager != null) {
2042            b = configManager.getConfig();
2043        }
2044        if (b != null) {
2045            return b.getInt(key);
2046        } else {
2047            // Return static default defined in CarrierConfigManager.
2048            return CarrierConfigManager.getDefaultConfig().getInt(key);
2049        }
2050    }
2051
2052    /**
2053     * Get the int config from carrier config manager.
2054     *
2055     * @param key config key defined in CarrierConfigManager
2056     * @return integer value of corresponding key.
2057     */
2058    private int getIntCarrierConfigForSlot(String key) {
2059        int[] subIds = SubscriptionManager.getSubId(mPhoneId);
2060        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
2061        if (subIds != null && subIds.length >= 1) {
2062            subId = subIds[0];
2063        }
2064        PersistableBundle b = null;
2065        if (mConfigManager != null) {
2066            // If an invalid subId is used, this bundle will contain default values.
2067            b = mConfigManager.getConfigForSubId(subId);
2068        }
2069        if (b != null) {
2070            return b.getInt(key);
2071        } else {
2072            // Return static default defined in CarrierConfigManager.
2073            return CarrierConfigManager.getDefaultConfig().getInt(key);
2074        }
2075    }
2076
2077    /**
2078     * Gets the call ID from the specified incoming call broadcast intent.
2079     *
2080     * @param incomingCallIntent the incoming call broadcast intent
2081     * @return the call ID or null if the intent does not contain it
2082     */
2083    private static String getCallId(Intent incomingCallIntent) {
2084        if (incomingCallIntent == null) {
2085            return null;
2086        }
2087
2088        return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
2089    }
2090
2091    /**
2092     * Gets the service type from the specified incoming call broadcast intent.
2093     *
2094     * @param incomingCallIntent the incoming call broadcast intent
2095     * @return the session identifier or -1 if the intent does not contain it
2096     */
2097    private static int getImsSessionId(Intent incomingCallIntent) {
2098        if (incomingCallIntent == null) {
2099            return (-1);
2100        }
2101
2102        return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
2103    }
2104
2105    /**
2106     * Checks to see if the ImsService Binder is connected. If it is not, we try to create the
2107     * connection again.
2108     */
2109    private void checkAndThrowExceptionIfServiceUnavailable()
2110            throws ImsException {
2111        if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
2112            createImsService();
2113
2114            if (mImsServiceProxy == null) {
2115                throw new ImsException("Service is unavailable",
2116                        ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2117            }
2118        }
2119    }
2120
2121    /**
2122     * Binds the IMS service to make/receive the call. Supports two methods of exposing an
2123     * ImsService:
2124     * 1) com.android.ims.ImsService implementation in ServiceManager (deprecated).
2125     * 2) android.telephony.ims.ImsService implementation through ImsResolver.
2126     */
2127    private void createImsService() {
2128        if (!mConfigDynamicBind) {
2129            // Old method of binding
2130            Rlog.i(TAG, "Creating ImsService using ServiceManager");
2131            mImsServiceProxy = getServiceProxyCompat();
2132        } else {
2133            Rlog.i(TAG, "Creating ImsService using ImsResolver");
2134            mImsServiceProxy = getServiceProxy();
2135        }
2136        // We have created a new ImsService connection, signal for re-registration
2137        synchronized (mHasRegisteredLock) {
2138            mHasRegisteredForProxy = false;
2139        }
2140    }
2141
2142    // Deprecated method of binding with the ImsService defined in the ServiceManager.
2143    private ImsServiceProxyCompat getServiceProxyCompat() {
2144        IBinder binder = ServiceManager.checkService(IMS_SERVICE);
2145
2146        if (binder != null) {
2147            try {
2148                binder.linkToDeath(mDeathRecipient, 0);
2149            } catch (RemoteException e) {
2150            }
2151        }
2152
2153        return new ImsServiceProxyCompat(mPhoneId, binder);
2154    }
2155
2156    // New method of binding with the ImsResolver
2157    private ImsServiceProxy getServiceProxy() {
2158        TelephonyManager tm = (TelephonyManager)
2159                mContext.getSystemService(Context.TELEPHONY_SERVICE);
2160        ImsServiceProxy serviceProxy = new ImsServiceProxy(mPhoneId, ImsFeature.MMTEL);
2161        serviceProxy.setStatusCallback(() ->  mStatusCallbacks.forEach(
2162                ImsServiceProxy.INotifyStatusChanged::notifyStatusChanged));
2163        // Returns null if the service is not available.
2164        IImsServiceController b = tm.getImsServiceControllerAndListen(mPhoneId,
2165                ImsFeature.MMTEL, serviceProxy.getListener());
2166        if (b != null) {
2167            serviceProxy.setBinder(b.asBinder());
2168            // Trigger the cache to be updated for feature status.
2169            serviceProxy.getFeatureStatus();
2170        } else {
2171            Rlog.w(TAG, "getServiceProxy: b is null! Phone Id: " + mPhoneId);
2172        }
2173        return serviceProxy;
2174    }
2175
2176    /**
2177     * Creates a {@link ImsCallSession} with the specified call profile.
2178     * Use other methods, if applicable, instead of interacting with
2179     * {@link ImsCallSession} directly.
2180     *
2181     * @param serviceId a service id which is obtained from {@link ImsManager#open}
2182     * @param profile a call profile to make the call
2183     */
2184    private ImsCallSession createCallSession(int serviceId,
2185            ImsCallProfile profile) throws ImsException {
2186        try {
2187            // Throws an exception if the ImsService Feature is not ready to accept commands.
2188            return new ImsCallSession(mImsServiceProxy.createCallSession(serviceId, profile, null));
2189        } catch (RemoteException e) {
2190            Rlog.w(TAG, "CreateCallSession: Error, remote exception: " + e.getMessage());
2191            throw new ImsException("createCallSession()", e,
2192                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2193
2194        }
2195    }
2196
2197    private static void log(String s) {
2198        Rlog.d(TAG, s);
2199    }
2200
2201    private static void loge(String s) {
2202        Rlog.e(TAG, s);
2203    }
2204
2205    private static void loge(String s, Throwable t) {
2206        Rlog.e(TAG, s, t);
2207    }
2208
2209    /**
2210     * Used for turning on IMS.if its off already
2211     */
2212    private void turnOnIms() throws ImsException {
2213        checkAndThrowExceptionIfServiceUnavailable();
2214
2215        try {
2216            mImsServiceProxy.turnOnIms();
2217        } catch (RemoteException e) {
2218            throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2219        }
2220    }
2221
2222    private boolean isImsTurnOffAllowed() {
2223        return isTurnOffImsAllowedByPlatformForSlot()
2224                && (!isWfcEnabledByPlatformForSlot()
2225                || !isWfcEnabledByUserForSlot());
2226    }
2227
2228    private void setLteFeatureValues(boolean turnOn) {
2229        log("setLteFeatureValues: " + turnOn);
2230        try {
2231            ImsConfig config = getConfigInterface();
2232            if (config != null) {
2233                config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
2234                        TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
2235
2236                if (isVolteEnabledByPlatformForSlot()) {
2237                    boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
2238                            CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
2239                    boolean enableViLte = turnOn && isVtEnabledByUserForSlot() &&
2240                            (ignoreDataEnabledChanged || isDataEnabled());
2241                    config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
2242                            TelephonyManager.NETWORK_TYPE_LTE,
2243                            enableViLte ? 1 : 0,
2244                            mImsConfigListener);
2245                }
2246            }
2247        } catch (ImsException e) {
2248            loge("setLteFeatureValues: exception ", e);
2249        }
2250    }
2251
2252    private void setAdvanced4GMode(boolean turnOn) throws ImsException {
2253        checkAndThrowExceptionIfServiceUnavailable();
2254
2255        // if turnOn: first set feature values then call turnOnIms()
2256        // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
2257        // allowed, first call turnOffIms() then set feature values
2258        if (turnOn) {
2259            setLteFeatureValues(turnOn);
2260            log("setAdvanced4GMode: turnOnIms");
2261            turnOnIms();
2262        } else {
2263            if (isImsTurnOffAllowed()) {
2264                log("setAdvanced4GMode: turnOffIms");
2265                turnOffIms();
2266            }
2267            setLteFeatureValues(turnOn);
2268        }
2269    }
2270
2271    /**
2272     * Used for turning off IMS completely in order to make the device CSFB'ed.
2273     * Once turned off, all calls will be over CS.
2274     */
2275    private void turnOffIms() throws ImsException {
2276        checkAndThrowExceptionIfServiceUnavailable();
2277
2278        try {
2279            mImsServiceProxy.turnOffIms();
2280        } catch (RemoteException e) {
2281            throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2282        }
2283    }
2284
2285    private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
2286        if (reason == null) return;
2287        while (mRecentDisconnectReasons.size() >= MAX_RECENT_DISCONNECT_REASONS) {
2288            mRecentDisconnectReasons.removeFirst();
2289        }
2290        mRecentDisconnectReasons.addLast(reason);
2291    }
2292
2293    /**
2294     * Death recipient class for monitoring IMS service.
2295     */
2296    private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
2297        @Override
2298        public void binderDied() {
2299            mImsServiceProxy = null;
2300            mUt = null;
2301            mConfig = null;
2302            mEcbm = null;
2303            mMultiEndpoint = null;
2304        }
2305    }
2306
2307    /**
2308     * Stub implementation of the Registration listener that provides no functionality.
2309     */
2310    private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub {
2311
2312        @Override
2313        public void registrationConnected() throws RemoteException {
2314        }
2315
2316        @Override
2317        public void registrationProgressing() throws RemoteException {
2318        }
2319
2320        @Override
2321        public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
2322        }
2323
2324        @Override
2325        public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
2326        }
2327
2328        @Override
2329        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
2330        }
2331
2332        @Override
2333        public void registrationResumed() throws RemoteException {
2334        }
2335
2336        @Override
2337        public void registrationSuspended() throws RemoteException {
2338        }
2339
2340        @Override
2341        public void registrationServiceCapabilityChanged(int serviceClass, int event)
2342                throws RemoteException {
2343        }
2344
2345        @Override
2346        public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
2347                int[] disabledFeatures) throws RemoteException {
2348        }
2349
2350        @Override
2351        public void voiceMessageCountUpdate(int count) throws RemoteException {
2352        }
2353
2354        @Override
2355        public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
2356        }
2357
2358        @Override
2359        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
2360                throws RemoteException {
2361        }
2362    }
2363
2364    /**
2365     * Adapter class for {@link IImsRegistrationListener}.
2366     */
2367    private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
2368
2369        @Deprecated
2370        public void registrationConnected() {
2371            if (DBG) {
2372                log("registrationConnected ::");
2373            }
2374
2375            synchronized (mRegistrationListeners) {
2376                mRegistrationListeners.forEach(l -> l.onImsConnected(
2377                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
2378            }
2379        }
2380
2381        @Deprecated
2382        public void registrationProgressing() {
2383            if (DBG) {
2384                log("registrationProgressing ::");
2385            }
2386
2387            synchronized (mRegistrationListeners) {
2388                mRegistrationListeners.forEach(l -> l.onImsProgressing(
2389                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
2390            }
2391        }
2392
2393        @Override
2394        public void registrationConnectedWithRadioTech(int imsRadioTech) {
2395            // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
2396            //       values in ServiceState.java.
2397            if (DBG) {
2398                log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
2399            }
2400
2401            synchronized (mRegistrationListeners) {
2402                mRegistrationListeners.forEach(l -> l.onImsConnected(imsRadioTech));
2403            }
2404        }
2405
2406        @Override
2407        public void registrationProgressingWithRadioTech(int imsRadioTech) {
2408            // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
2409            //       values in ServiceState.java.
2410            if (DBG) {
2411                log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
2412            }
2413
2414            synchronized (mRegistrationListeners) {
2415                mRegistrationListeners.forEach(l -> l.onImsProgressing(imsRadioTech));
2416            }
2417        }
2418
2419        @Override
2420        public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
2421            if (DBG) {
2422                log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
2423            }
2424
2425            addToRecentDisconnectReasons(imsReasonInfo);
2426            synchronized (mRegistrationListeners) {
2427                mRegistrationListeners.forEach(l -> l.onImsDisconnected(imsReasonInfo));
2428            }
2429        }
2430
2431        @Override
2432        public void registrationResumed() {
2433            if (DBG) {
2434                log("registrationResumed ::");
2435            }
2436
2437            synchronized (mRegistrationListeners) {
2438                mRegistrationListeners.forEach(ImsConnectionStateListener::onImsResumed);
2439            }
2440        }
2441
2442        @Override
2443        public void registrationSuspended() {
2444            if (DBG) {
2445                log("registrationSuspended ::");
2446            }
2447
2448            synchronized (mRegistrationListeners) {
2449                mRegistrationListeners.forEach(ImsConnectionStateListener::onImsSuspended);
2450            }
2451        }
2452
2453        @Override
2454        public void registrationServiceCapabilityChanged(int serviceClass, int event) {
2455            log("registrationServiceCapabilityChanged :: serviceClass=" +
2456                    serviceClass + ", event=" + event);
2457
2458            synchronized (mRegistrationListeners) {
2459                mRegistrationListeners.forEach(l -> l.onImsConnected(
2460                        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
2461            }
2462        }
2463
2464        @Override
2465        public void registrationFeatureCapabilityChanged(int serviceClass,
2466                int[] enabledFeatures, int[] disabledFeatures) {
2467            log("registrationFeatureCapabilityChanged :: serviceClass=" +
2468                    serviceClass);
2469
2470            synchronized (mRegistrationListeners) {
2471                mRegistrationListeners.forEach(l -> l.onFeatureCapabilityChanged(serviceClass,
2472                        enabledFeatures, disabledFeatures));
2473            }
2474        }
2475
2476        @Override
2477        public void voiceMessageCountUpdate(int count) {
2478            log("voiceMessageCountUpdate :: count=" + count);
2479
2480            synchronized (mRegistrationListeners) {
2481                mRegistrationListeners.forEach(l -> l.onVoiceMessageCountChanged(count));
2482            }
2483        }
2484
2485        @Override
2486        public void registrationAssociatedUriChanged(Uri[] uris) {
2487            if (DBG) log("registrationAssociatedUriChanged ::");
2488
2489            synchronized (mRegistrationListeners) {
2490                mRegistrationListeners.forEach(l -> l.registrationAssociatedUriChanged(uris));
2491            }
2492        }
2493
2494        @Override
2495        public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
2496            if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
2497                    ", imsReasonInfo=" + imsReasonInfo);
2498
2499            synchronized (mRegistrationListeners) {
2500                mRegistrationListeners.forEach(l -> l.onRegistrationChangeFailed(targetAccessTech,
2501                        imsReasonInfo));
2502            }
2503        }
2504    }
2505
2506    /**
2507     * Gets the ECBM interface to request ECBM exit.
2508     *
2509     * @param serviceId a service id which is obtained from {@link ImsManager#open}
2510     * @return the ECBM interface instance
2511     * @throws ImsException if getting the ECBM interface results in an error
2512     */
2513    public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
2514        if (mEcbm != null && mEcbm.isBinderAlive()) {
2515            return mEcbm;
2516        }
2517
2518        checkAndThrowExceptionIfServiceUnavailable();
2519        try {
2520            IImsEcbm iEcbm = mImsServiceProxy.getEcbmInterface();
2521
2522            if (iEcbm == null) {
2523                throw new ImsException("getEcbmInterface()",
2524                        ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
2525            }
2526            mEcbm = new ImsEcbm(iEcbm);
2527        } catch (RemoteException e) {
2528            throw new ImsException("getEcbmInterface()", e,
2529                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2530        }
2531        return mEcbm;
2532    }
2533
2534    /**
2535     * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
2536     *
2537     * @param serviceId a service id which is obtained from {@link ImsManager#open}
2538     * @return the multi-endpoint interface instance
2539     * @throws ImsException if getting the multi-endpoint interface results in an error
2540     */
2541    public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
2542        if (mMultiEndpoint != null && mMultiEndpoint.isBinderAlive()) {
2543            return mMultiEndpoint;
2544        }
2545
2546        checkAndThrowExceptionIfServiceUnavailable();
2547        try {
2548            IImsMultiEndpoint iImsMultiEndpoint = mImsServiceProxy.getMultiEndpointInterface();
2549
2550            if (iImsMultiEndpoint == null) {
2551                throw new ImsException("getMultiEndpointInterface()",
2552                        ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
2553            }
2554            mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
2555        } catch (RemoteException e) {
2556            throw new ImsException("getMultiEndpointInterface()", e,
2557                    ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2558        }
2559
2560        return mMultiEndpoint;
2561    }
2562
2563    /**
2564     * Resets ImsManager settings back to factory defaults.
2565     *
2566     * @deprecated Doesn't support MSIM devices. Use {@link #factoryResetSlot()} instead.
2567     *
2568     * @hide
2569     */
2570    public static void factoryReset(Context context) {
2571        // Set VoLTE to default
2572        android.provider.Settings.Global.putInt(context.getContentResolver(),
2573                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
2574                ImsConfig.FeatureValueConstants.ON);
2575
2576        // Set VoWiFi to default
2577        android.provider.Settings.Global.putInt(context.getContentResolver(),
2578                android.provider.Settings.Global.WFC_IMS_ENABLED,
2579                getBooleanCarrierConfig(context,
2580                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
2581                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2582
2583        // Set VoWiFi mode to default
2584        android.provider.Settings.Global.putInt(context.getContentResolver(),
2585                android.provider.Settings.Global.WFC_IMS_MODE,
2586                getIntCarrierConfig(context,
2587                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
2588
2589        // Set VoWiFi roaming to default
2590        android.provider.Settings.Global.putInt(context.getContentResolver(),
2591                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
2592                getBooleanCarrierConfig(context,
2593                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
2594                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2595
2596        // Set VT to default
2597        android.provider.Settings.Global.putInt(context.getContentResolver(),
2598                android.provider.Settings.Global.VT_IMS_ENABLED,
2599                ImsConfig.FeatureValueConstants.ON);
2600
2601        // Push settings to ImsConfig
2602        ImsManager.updateImsServiceConfig(context,
2603                SubscriptionManager.getDefaultVoicePhoneId(), true);
2604    }
2605
2606    /**
2607     * Resets ImsManager settings back to factory defaults.
2608     *
2609     * @hide
2610     */
2611    public void factoryResetSlot() {
2612        // Set VoLTE to default
2613        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2614                android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
2615                ImsConfig.FeatureValueConstants.ON);
2616
2617        // Set VoWiFi to default
2618        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2619                android.provider.Settings.Global.WFC_IMS_ENABLED,
2620                getBooleanCarrierConfigForSlot(
2621                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
2622                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2623
2624        // Set VoWiFi mode to default
2625        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2626                android.provider.Settings.Global.WFC_IMS_MODE,
2627                getIntCarrierConfigForSlot(
2628                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
2629
2630        // Set VoWiFi roaming to default
2631        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2632                android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
2633                getBooleanCarrierConfigForSlot(
2634                        CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
2635                        ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2636
2637        // Set VT to default
2638        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2639                android.provider.Settings.Global.VT_IMS_ENABLED,
2640                ImsConfig.FeatureValueConstants.ON);
2641
2642        // Push settings to ImsConfig
2643        updateImsServiceConfigForSlot(true);
2644    }
2645
2646    private boolean isDataEnabled() {
2647        return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
2648    }
2649
2650    /**
2651     * Set data enabled/disabled flag.
2652     * @param enabled True if data is enabled, otherwise disabled.
2653     */
2654    public void setDataEnabled(boolean enabled) {
2655        log("setDataEnabled: " + enabled);
2656        SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
2657    }
2658
2659    private boolean isVolteProvisioned() {
2660        return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
2661    }
2662
2663    private void setVolteProvisionedProperty(boolean provisioned) {
2664        SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2665    }
2666
2667    private boolean isWfcProvisioned() {
2668        return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
2669    }
2670
2671    private void setWfcProvisionedProperty(boolean provisioned) {
2672        SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2673    }
2674
2675    private boolean isVtProvisioned() {
2676        return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
2677    }
2678
2679    private void setVtProvisionedProperty(boolean provisioned) {
2680        SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2681    }
2682
2683    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2684        pw.println("ImsManager:");
2685        pw.println("  mPhoneId = " + mPhoneId);
2686        pw.println("  mConfigUpdated = " + mConfigUpdated);
2687        pw.println("  mImsServiceProxy = " + mImsServiceProxy);
2688        pw.println("  mDataEnabled = " + isDataEnabled());
2689        pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(mContext,
2690                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
2691
2692        pw.println("  isGbaValid = " + isGbaValidForSlot());
2693        pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
2694        pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabledForSlot());
2695
2696        pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatformForSlot());
2697        pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDeviceForSlot());
2698        pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
2699                isEnhanced4gLteModeSettingEnabledByUserForSlot());
2700        pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatformForSlot());
2701        pw.println("  isVtEnabledByUser = " + isVtEnabledByUserForSlot());
2702
2703        pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatformForSlot());
2704        pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUserForSlot());
2705        pw.println("  getWfcMode = " + getWfcModeForSlot());
2706        pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUserForSlot());
2707
2708        pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDeviceForSlot());
2709        pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDeviceForSlot());
2710        pw.flush();
2711    }
2712}
2713