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