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