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