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