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              */
connectionReady(ImsManager manager)198             void connectionReady(ImsManager manager) throws ImsException;
199 
200             /**
201              * The underlying IMS implementation is unavailable and can not be used to communicate.
202              */
connectionUnavailable()203             void connectionUnavailable();
204         }
205 
206         @VisibleForTesting
207         public interface RetryTimeout {
get()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 
Connector(Context context, int phoneId, Listener listener)272         public Connector(Context context, int phoneId, Listener listener) {
273             mContext = context;
274             mPhoneId = phoneId;
275             mListener = listener;
276         }
277 
Connector(Context context, int phoneId, Listener listener, Looper looper)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          */
connect()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          */
disconnect()301         public void disconnect() {
302             removeCallbacks(mGetServiceRunnable);
303             synchronized (mLock) {
304                 if (mImsManager != null) {
305                     mImsManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
306                 }
307             }
308             notifyNotReady();
309         }
310 
retryGetImsService()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 
getImsService()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 
notifyReady()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 
notifyNotReady()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      */
getInstance(Context context, int phoneId)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      */
isEnhanced4gLteModeSettingEnabledByUser(Context context)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      */
isEnhanced4gLteModeSettingEnabledByUser()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      */
setEnhanced4gLteModeSetting(Context context, boolean enabled)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      */
setEnhanced4gLteModeSetting(boolean enabled)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      */
isNonTtyOrTtyOnVolteEnabled(Context context)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      */
isNonTtyOrTtyOnVolteEnabled()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      */
isVolteEnabledByPlatform(Context context)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      */
isVolteEnabledByPlatform()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      */
isVolteProvisionedOnDevice(Context context)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      */
isVolteProvisionedOnDevice()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      */
isWfcProvisionedOnDevice(Context context)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      */
isWfcProvisionedOnDevice()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      */
isVtProvisionedOnDevice(Context context)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      */
isVtProvisionedOnDevice()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      */
isVtEnabledByPlatform(Context context)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      */
isVtEnabledByPlatform()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      */
isVtEnabledByUser(Context context)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      */
isVtEnabledByUser()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      */
setVtSetting(Context context, boolean enabled)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      */
setVtSetting(boolean enabled)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      */
isTurnOffImsAllowedByPlatform(Context context)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      */
isTurnOffImsAllowedByPlatform()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      */
isWfcEnabledByUser(Context context)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      */
isWfcEnabledByUser()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      */
setWfcSetting(Context context, boolean enabled)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      */
setWfcSetting(boolean enabled)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      */
setWfcNonPersistent(boolean enabled, int wfcMode)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      */
getWfcMode(Context context)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      */
getWfcMode()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      */
setWfcMode(Context context, int wfcMode)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      */
setWfcMode(int wfcMode)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      */
getWfcMode(Context context, boolean roaming)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      */
getWfcMode(boolean roaming)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      */
getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey)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      */
setWfcMode(Context context, int wfcMode, boolean roaming)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      */
setWfcMode(int wfcMode, boolean roaming)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 
getSubId()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 
setWfcModeInternal(int wfcMode)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      */
isWfcRoamingEnabledByUser(Context context)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      */
isWfcRoamingEnabledByUser()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      */
setWfcRoamingSetting(Context context, boolean enabled)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      */
setWfcRoamingSetting(boolean enabled)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 
setWfcRoamingSettingInternal(boolean enabled)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      */
isWfcEnabledByPlatform(Context context)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      */
isWfcEnabledByPlatform()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      */
isGbaValid()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      */
getProvisionedBool(ImsConfig config, int item)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      */
getProvisionedBoolNoException(int item)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      */
updateImsServiceConfig(Context context, int phoneId, boolean force)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      */
updateImsServiceConfig(boolean force)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      */
updateVolteFeatureValue()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      */
updateVideoCallFeatureValue()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      */
updateWfcFeatureAndProvisionedValues()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
ImsManager(Context context, int phoneId)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      */
isDynamicBinding()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      */
isServiceAvailable()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      */
isServiceReady()1435     public boolean isServiceReady() {
1436         connectIfServiceIsAvailable();
1437         return mMmTelFeatureConnection.isBinderReady();
1438     }
1439 
1440     /**
1441      * If the service is available, try to reconnect.
1442      */
connectIfServiceIsAvailable()1443     public void connectIfServiceIsAvailable() {
1444         if (mMmTelFeatureConnection == null || !mMmTelFeatureConnection.isBinderAlive()) {
1445             createImsService();
1446         }
1447     }
1448 
setConfigListener(ImsConfigListener listener)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
addNotifyStatusChangedCallbackIfAvailable(MmTelFeatureConnection.IFeatureUpdate c)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 
removeNotifyStatusChangedCallback(MmTelFeatureConnection.IFeatureUpdate c)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      */
open(MmTelFeature.Listener listener)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      */
addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)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      */
addRegistrationListener(ImsConnectionStateListener listener)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      */
addRegistrationCallback(ImsRegistrationImplBase.Callback callback)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      */
removeRegistrationListener(ImsRegistrationImplBase.Callback callback)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      */
addCapabilitiesCallback(ImsFeature.CapabilityCallback callback)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      */
removeRegistrationListener(ImsConnectionStateListener listener)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 
getRegistrationTech()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      */
close()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      */
getSupplementaryServiceConfiguration()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      */
createCallProfile(int serviceType, int callType)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      */
makeCall(ImsCallProfile profile, String[] callees, ImsCall.Listener listener)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      */
takeCall(IImsCallSession session, Bundle incomingCallExtras, ImsCall.Listener listener)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      */
getConfigInterface()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 
changeMmTelCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, boolean isEnabled)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 
setRttEnabled(boolean enabled)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      */
setTtyMode(int ttyMode)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      */
setUiTTYMode(Context context, int uiTtyMode, Message onComplete)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 
makeACopy(ImsReasonInfo imsReasonInfo)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      */
getRecentImsDisconnectReasons()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 
getImsServiceState()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      */
getBooleanCarrierConfig(String key)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      */
getIntCarrierConfig(String key)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      */
getCallId(Bundle incomingCallExtras)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      */
checkAndThrowExceptionIfServiceUnavailable()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      */
createImsService()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      */
createCallSession(ImsCallProfile profile)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 
log(String s)2056     private static void log(String s) {
2057         Rlog.d(TAG, s);
2058     }
2059 
loge(String s)2060     private static void loge(String s) {
2061         Rlog.e(TAG, s);
2062     }
2063 
loge(String s, Throwable t)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      */
turnOnIms()2071     private void turnOnIms() throws ImsException {
2072         TelephonyManager tm = (TelephonyManager)
2073                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
2074         tm.enableIms(mPhoneId);
2075     }
2076 
isImsTurnOffAllowed()2077     private boolean isImsTurnOffAllowed() {
2078         return isTurnOffImsAllowedByPlatform()
2079                 && (!isWfcEnabledByPlatform()
2080                 || !isWfcEnabledByUser());
2081     }
2082 
setLteFeatureValues(boolean turnOn)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 
setAdvanced4GMode(boolean turnOn)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      */
turnOffIms()2141     private void turnOffIms() throws ImsException {
2142         TelephonyManager tm = (TelephonyManager)
2143                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
2144         tm.disableIms(mPhoneId);
2145     }
2146 
addToRecentDisconnectReasons(ImsReasonInfo reason)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      */
getEcbmInterface()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 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)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 
acknowledgeSms(int token, int messageRef, int result)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 
acknowledgeSmsReport(int token, int messageRef, int result)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 
getSmsFormat()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 
setSmsListener(IImsSmsListener listener)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 
onSmsReady()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      */
shouldProcessCall(boolean isEmergency, String[] numbers)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      */
getMultiEndpointInterface()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      */
factoryReset(Context context)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      */
factoryReset()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 
isDataEnabled()2340     private boolean isDataEnabled() {
2341         return new TelephonyManager(mContext, getSubId()).isDataCapable();
2342     }
2343 
isVolteProvisioned()2344     private boolean isVolteProvisioned() {
2345         return getProvisionedBoolNoException(
2346                 ImsConfig.ConfigConstants.VLT_SETTING_ENABLED);
2347     }
2348 
isWfcProvisioned()2349     private boolean isWfcProvisioned() {
2350         return getProvisionedBoolNoException(
2351                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED);
2352     }
2353 
isVtProvisioned()2354     private boolean isVtProvisioned() {
2355         return getProvisionedBoolNoException(
2356                 ImsConfig.ConfigConstants.LVC_SETTING_ENABLED);
2357     }
2358 
booleanToPropertyString(boolean bool)2359     private static String booleanToPropertyString(boolean bool) {
2360         return bool ? "1" : "0";
2361     }
2362 
2363 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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