• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.telephony.ims.ProvisioningManager.KEY_VOIMS_OPT_IN_STATUS;
20 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
21 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
22 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
23 import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
24 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
25 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
26 
27 import android.annotation.NonNull;
28 import android.app.PendingIntent;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.pm.PackageManager;
33 import android.os.Build;
34 import android.os.Message;
35 import android.os.PersistableBundle;
36 import android.os.RemoteException;
37 import android.os.ServiceSpecificException;
38 import android.os.SystemProperties;
39 import android.provider.Settings;
40 import android.telecom.TelecomManager;
41 import android.telephony.AccessNetworkConstants;
42 import android.telephony.BinderCacheManager;
43 import android.telephony.CarrierConfigManager;
44 import android.telephony.SubscriptionManager;
45 import android.telephony.TelephonyFrameworkInitializer;
46 import android.telephony.TelephonyManager;
47 import android.telephony.ims.ImsCallProfile;
48 import android.telephony.ims.ImsCallSession;
49 import android.telephony.ims.ImsMmTelManager;
50 import android.telephony.ims.ImsReasonInfo;
51 import android.telephony.ims.ImsService;
52 import android.telephony.ims.MediaQualityStatus;
53 import android.telephony.ims.MediaThreshold;
54 import android.telephony.ims.ProvisioningManager;
55 import android.telephony.ims.RegistrationManager;
56 import android.telephony.ims.RtpHeaderExtensionType;
57 import android.telephony.ims.SrvccCall;
58 import android.telephony.ims.aidl.IImsCapabilityCallback;
59 import android.telephony.ims.aidl.IImsConfig;
60 import android.telephony.ims.aidl.IImsConfigCallback;
61 import android.telephony.ims.aidl.IImsMmTelFeature;
62 import android.telephony.ims.aidl.IImsRegistration;
63 import android.telephony.ims.aidl.IImsRegistrationCallback;
64 import android.telephony.ims.aidl.IImsSmsListener;
65 import android.telephony.ims.aidl.ISipTransport;
66 import android.telephony.ims.aidl.ISrvccStartedCallback;
67 import android.telephony.ims.feature.CapabilityChangeRequest;
68 import android.telephony.ims.feature.ImsFeature;
69 import android.telephony.ims.feature.MmTelFeature;
70 import android.telephony.ims.stub.ImsCallSessionImplBase;
71 import android.telephony.ims.stub.ImsConfigImplBase;
72 import android.telephony.ims.stub.ImsRegistrationImplBase;
73 import android.util.SparseArray;
74 
75 import com.android.ims.internal.IImsCallSession;
76 import com.android.ims.internal.IImsServiceFeatureCallback;
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.telephony.ITelephony;
79 import com.android.telephony.Rlog;
80 
81 import java.io.FileDescriptor;
82 import java.io.PrintWriter;
83 import java.util.ArrayList;
84 import java.util.Arrays;
85 import java.util.Set;
86 import java.util.concurrent.BlockingQueue;
87 import java.util.concurrent.CountDownLatch;
88 import java.util.concurrent.Executor;
89 import java.util.concurrent.Executors;
90 import java.util.concurrent.LinkedBlockingDeque;
91 import java.util.concurrent.TimeUnit;
92 import java.util.concurrent.atomic.AtomicReference;
93 import java.util.function.Consumer;
94 
95 /**
96  * Provides APIs for MMTEL IMS services, such as initiating IMS calls, and provides access to
97  * the operator's IMS network. This class is the starting point for any IMS MMTEL actions.
98  * You can acquire an instance of it with {@link #getInstance getInstance()}.
99  * {Use {@link RcsFeatureManager} for RCS services}.
100  * For internal use ONLY! Use {@link ImsMmTelManager} instead.
101  * @hide
102  */
103 public class ImsManager implements FeatureUpdates {
104 
105     /*
106      * Debug flag to override configuration flag
107      */
108     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
109     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
110     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
111     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
112     public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
113     public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
114     public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
115     public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
116 
117     /**
118      * The result code to be sent back with the incoming call {@link PendingIntent}.
119      * @see #open(MmTelFeature.Listener)
120      */
121     public static final int INCOMING_CALL_RESULT_CODE = 101;
122 
123     /**
124      * Key to retrieve the call ID from an incoming call intent. No longer used, see
125      * {@link ImsCallSessionImplBase#getCallId()}.
126      * @deprecated Not used in the framework, keeping around symbol to not break old vendor
127      * components.
128      */
129     @Deprecated
130     public static final String EXTRA_CALL_ID = "android:imsCallID";
131 
132     /**
133      * Action to broadcast when ImsService is up.
134      * Internal use only.
135      * @deprecated
136      * @hide
137      */
138     public static final String ACTION_IMS_SERVICE_UP =
139             "com.android.ims.IMS_SERVICE_UP";
140 
141     /**
142      * Action to broadcast when ImsService is down.
143      * Internal use only.
144      * @deprecated
145      * @hide
146      */
147     public static final String ACTION_IMS_SERVICE_DOWN =
148             "com.android.ims.IMS_SERVICE_DOWN";
149 
150     /**
151      * Action to broadcast when ImsService registration fails.
152      * Internal use only.
153      * @hide
154      * @deprecated use {@link android.telephony.ims.ImsManager#ACTION_WFC_IMS_REGISTRATION_ERROR}
155      * instead.
156      */
157     @Deprecated
158     public static final String ACTION_IMS_REGISTRATION_ERROR =
159             android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR;
160 
161     /**
162      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
163      * A long value; the phone ID corresponding to the IMS service coming up or down.
164      * Internal use only.
165      * @hide
166      */
167     public static final String EXTRA_PHONE_ID = "android:phone_id";
168 
169     /**
170      * Action for the incoming call intent for the Phone app.
171      * Internal use only.
172      * @hide
173      */
174     public static final String ACTION_IMS_INCOMING_CALL =
175             "com.android.ims.IMS_INCOMING_CALL";
176 
177     /**
178      * Part of the ACTION_IMS_INCOMING_CALL intents.
179      * An integer value; service identifier obtained from {@link ImsManager#open}.
180      * Internal use only.
181      * @hide
182      * @deprecated Not used in the system, keeping around to not break old vendor components.
183      */
184     @Deprecated
185     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
186 
187     /**
188      * Part of the ACTION_IMS_INCOMING_CALL intents.
189      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
190      * The value "true" indicates that the incoming call is for USSD.
191      * Internal use only.
192      * @deprecated Keeping around to not break old vendor components. Use
193      * {@link MmTelFeature#EXTRA_IS_USSD} instead.
194      * @hide
195      */
196     public static final String EXTRA_USSD = "android:ussd";
197 
198     /**
199      * Part of the ACTION_IMS_INCOMING_CALL intents.
200      * A boolean value; Flag to indicate whether the call is an unknown
201      * dialing call. Such calls are originated by sending commands (like
202      * AT commands) directly to modem without Android involvement.
203      * Even though they are not incoming calls, they are propagated
204      * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
205      * Internal use only.
206      * @deprecated Keeping around to not break old vendor components. Use
207      * {@link MmTelFeature#EXTRA_IS_UNKNOWN_CALL} instead.
208      * @hide
209      */
210     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
211 
212     private static final int SUBINFO_PROPERTY_FALSE = 0;
213 
214     private static final int SYSTEM_PROPERTY_NOT_SET = -1;
215 
216     // -1 indicates a subscriptionProperty value that is never set.
217     private static final int SUB_PROPERTY_NOT_INITIALIZED = -1;
218 
219     private static final String TAG = "ImsManager";
220     private static final boolean DBG = true;
221 
222     private static final int RESPONSE_WAIT_TIME_MS = 3000;
223 
224     private static final int[] LOCAL_IMS_CONFIG_KEYS = {
225             KEY_VOIMS_OPT_IN_STATUS
226     };
227 
228     /**
229      * Create a Lazy Executor that is not instantiated for this instance unless it is used. This
230      * is to stop threads from being started on ImsManagers that are created to do simple tasks.
231      */
232     private static class LazyExecutor implements Executor {
233         private Executor mExecutor;
234 
235         @Override
execute(Runnable runnable)236         public void execute(Runnable runnable) {
237             startExecutorIfNeeded();
238             mExecutor.execute(runnable);
239         }
240 
startExecutorIfNeeded()241         private synchronized void startExecutorIfNeeded() {
242             if (mExecutor != null) return;
243             mExecutor = Executors.newSingleThreadExecutor();
244         }
245     }
246 
247     @VisibleForTesting
248     public interface MmTelFeatureConnectionFactory {
create(Context context, int phoneId, int subId, IImsMmTelFeature feature, IImsConfig c, IImsRegistration r, ISipTransport s)249         MmTelFeatureConnection create(Context context, int phoneId, int subId,
250                 IImsMmTelFeature feature, IImsConfig c, IImsRegistration r, ISipTransport s);
251     }
252 
253     @VisibleForTesting
254     public interface SettingsProxy {
255         /** @see Settings.Secure#getInt(ContentResolver, String, int) */
getSecureIntSetting(ContentResolver cr, String name, int def)256         int getSecureIntSetting(ContentResolver cr, String name, int def);
257         /** @see Settings.Secure#putInt(ContentResolver, String, int) */
putSecureIntSetting(ContentResolver cr, String name, int value)258         boolean putSecureIntSetting(ContentResolver cr, String name, int value);
259     }
260 
261     @VisibleForTesting
262     public interface SubscriptionManagerProxy {
isValidSubscriptionId(int subId)263         boolean isValidSubscriptionId(int subId);
getSubscriptionId(int slotIndex)264         int getSubscriptionId(int slotIndex);
getDefaultVoicePhoneId()265         int getDefaultVoicePhoneId();
getIntegerSubscriptionProperty(int subId, String propKey, int defValue)266         int getIntegerSubscriptionProperty(int subId, String propKey, int defValue);
setSubscriptionProperty(int subId, String propKey, String propValue)267         void setSubscriptionProperty(int subId, String propKey, String propValue);
getActiveSubscriptionIdList()268         int[] getActiveSubscriptionIdList();
269     }
270 
271     // Default implementations, which is mocked for testing
272     private static class DefaultSettingsProxy implements SettingsProxy {
273         @Override
getSecureIntSetting(ContentResolver cr, String name, int def)274         public int getSecureIntSetting(ContentResolver cr, String name, int def) {
275             return Settings.Secure.getInt(cr, name, def);
276         }
277 
278         @Override
putSecureIntSetting(ContentResolver cr, String name, int value)279         public boolean putSecureIntSetting(ContentResolver cr, String name, int value) {
280             return Settings.Secure.putInt(cr, name, value);
281         }
282     }
283 
284     // Default implementation which is mocked to make static dependency validation easier.
285     private static class DefaultSubscriptionManagerProxy implements SubscriptionManagerProxy {
286 
287         private Context mContext;
288 
DefaultSubscriptionManagerProxy(Context context)289         public DefaultSubscriptionManagerProxy(Context context) {
290             mContext = context;
291         }
292 
293         @Override
isValidSubscriptionId(int subId)294         public boolean isValidSubscriptionId(int subId) {
295             return SubscriptionManager.isValidSubscriptionId(subId);
296         }
297 
298         @Override
getSubscriptionId(int slotIndex)299         public int getSubscriptionId(int slotIndex) {
300             return SubscriptionManager.getSubscriptionId(slotIndex);
301         }
302 
303         @Override
getDefaultVoicePhoneId()304         public int getDefaultVoicePhoneId() {
305             return SubscriptionManager.getDefaultVoicePhoneId();
306         }
307 
308         @Override
getIntegerSubscriptionProperty(int subId, String propKey, int defValue)309         public int getIntegerSubscriptionProperty(int subId, String propKey, int defValue) {
310             return SubscriptionManager.getIntegerSubscriptionProperty(subId, propKey, defValue,
311                     mContext);
312         }
313 
314         @Override
setSubscriptionProperty(int subId, String propKey, String propValue)315         public void setSubscriptionProperty(int subId, String propKey, String propValue) {
316             SubscriptionManager.setSubscriptionProperty(subId, propKey, propValue);
317         }
318 
319         @Override
getActiveSubscriptionIdList()320         public int[] getActiveSubscriptionIdList() {
321             return getSubscriptionManager().getActiveSubscriptionIdList();
322         }
323 
getSubscriptionManager()324         private SubscriptionManager getSubscriptionManager() {
325             return mContext.getSystemService(SubscriptionManager.class);
326         }
327     }
328 
329     /**
330      * Events that will be triggered as part of metrics collection.
331      */
332     public interface ImsStatsCallback {
333         /**
334          * The MmTel capabilities that are enabled have changed.
335          * @param capability The MmTel capability
336          * @param regTech The IMS registration technology associated with the capability.
337          * @param isEnabled {@code true} if the capability is enabled, {@code false} if it is
338          *                  disabled.
339          */
onEnabledMmTelCapabilitiesChanged( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int regTech, boolean isEnabled)340         void onEnabledMmTelCapabilitiesChanged(
341                 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
342                 @ImsRegistrationImplBase.ImsRegistrationTech int regTech,
343                 boolean isEnabled);
344     }
345 
346     /**
347      * Internally we will create a FeatureConnector when {@link #getInstance(Context, int)} is
348      * called to keep the MmTelFeatureConnection instance fresh as new SIM cards are
349      * inserted/removed and MmTelFeature potentially changes.
350      * <p>
351      * For efficiency purposes, there is only one ImsManager created per-slot when using
352      * {@link #getInstance(Context, int)} and the same instance is returned for multiple callers.
353      * This is due to the ImsManager being a potentially heavyweight object depending on what it is
354      * being used for.
355      */
356     private static class InstanceManager implements FeatureConnector.Listener<ImsManager> {
357         // If this is the first time connecting, wait a small amount of time in case IMS has already
358         // connected. Otherwise, ImsManager will become ready when the ImsService is connected.
359         private static final int CONNECT_TIMEOUT_MS = 50;
360 
361         private final FeatureConnector<ImsManager> mConnector;
362         private final ImsManager mImsManager;
363 
364         private final Object mLock = new Object();
365         private boolean isConnectorActive = false;
366         private CountDownLatch mConnectedLatch;
367 
InstanceManager(ImsManager manager)368         public InstanceManager(ImsManager manager) {
369             mImsManager = manager;
370             // Set a special prefix so that logs generated by getInstance are distinguishable.
371             mImsManager.mLogTagPostfix = "IM";
372 
373             ArrayList<Integer> readyFilter = new ArrayList<>();
374             readyFilter.add(ImsFeature.STATE_READY);
375             readyFilter.add(ImsFeature.STATE_INITIALIZING);
376             readyFilter.add(ImsFeature.STATE_UNAVAILABLE);
377             // Pass a reference of the ImsManager being managed into the connector, allowing it to
378             // update the internal MmTelFeatureConnection as it is being updated.
379             mConnector = new FeatureConnector<>(manager.mContext, manager.mPhoneId,
380                     (c,p) -> mImsManager, "InstanceManager", readyFilter, this,
381                     manager.getImsThreadExecutor());
382         }
383 
getInstance()384         public ImsManager getInstance() {
385             return mImsManager;
386         }
387 
reconnect()388         public void reconnect() {
389             boolean requiresReconnect = false;
390             synchronized (mLock) {
391                 if (!isConnectorActive) {
392                     requiresReconnect = true;
393                     isConnectorActive = true;
394                     mConnectedLatch = new CountDownLatch(1);
395                 }
396             }
397             if (requiresReconnect) {
398                 mConnector.connect();
399             }
400             try {
401                 // If this is during initial reconnect, let all threads wait for connect
402                 // (or timeout)
403                 if(!mConnectedLatch.await(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
404                     mImsManager.log("ImsService not up yet - timeout waiting for connection.");
405                 }
406             } catch (InterruptedException e) {
407                 // Do nothing and allow ImsService to attach behind the scenes
408             }
409         }
410 
411         @Override
connectionReady(ImsManager manager, int subId)412         public void connectionReady(ImsManager manager, int subId) {
413             synchronized (mLock) {
414                 mImsManager.logi("connectionReady, subId: " + subId);
415                 mConnectedLatch.countDown();
416             }
417         }
418 
419         @Override
connectionUnavailable(int reason)420         public void connectionUnavailable(int reason) {
421             synchronized (mLock) {
422                 mImsManager.logi("connectionUnavailable, reason: " + reason);
423                 // only need to track the connection becoming unavailable due to telephony going
424                 // down.
425                 if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
426                     isConnectorActive = false;
427                 }
428                 mConnectedLatch.countDown();
429             }
430 
431         }
432     }
433 
434     // Replaced with single-threaded executor for testing.
435     private final Executor mExecutor;
436     // Replaced With mock for testing
437     private MmTelFeatureConnectionFactory mMmTelFeatureConnectionFactory =
438             MmTelFeatureConnection::new;
439     private final SubscriptionManagerProxy mSubscriptionManagerProxy;
440     private final SettingsProxy mSettingsProxy;
441 
442     private Context mContext;
443     private CarrierConfigManager mConfigManager;
444     private int mPhoneId;
445     private AtomicReference<MmTelFeatureConnection> mMmTelConnectionRef = new AtomicReference<>();
446     // Used for debug purposes only currently
447     private boolean mConfigUpdated = false;
448     private BinderCacheManager<ITelephony> mBinderCache;
449     private ImsConfigListener mImsConfigListener;
450 
451     public static final String TRUE = "true";
452     public static final String FALSE = "false";
453     // Map of phoneId -> InstanceManager
454     private static final SparseArray<InstanceManager> IMS_MANAGER_INSTANCES = new SparseArray<>(2);
455     // Map of phoneId -> ImsStatsCallback
456     private static final SparseArray<ImsStatsCallback> IMS_STATS_CALLBACKS = new SparseArray<>(2);
457 
458     // A log prefix added to some instances of ImsManager to make it distinguishable from others.
459     // - "IM" added to ImsManager for ImsManagers created using getInstance.
460     private String mLogTagPostfix = "";
461 
462     /**
463      * Gets a manager instance and blocks for a limited period of time, connecting to the
464      * corresponding ImsService MmTelFeature if it exists.
465      * <p>
466      * If the ImsService is unavailable or becomes unavailable, the associated methods will fail and
467      * a new ImsManager will need to be requested. Instead, a {@link FeatureConnector} can be
468      * requested using {@link #getConnector}, which will notify the caller when a new ImsManager is
469      * available.
470      *
471      * @param context application context for creating the manager object
472      * @param phoneId the phone ID for the IMS Service
473      * @return the manager instance corresponding to the phoneId
474      */
475     @UnsupportedAppUsage
getInstance(Context context, int phoneId)476     public static ImsManager getInstance(Context context, int phoneId) {
477         InstanceManager instanceManager;
478         synchronized (IMS_MANAGER_INSTANCES) {
479             instanceManager = IMS_MANAGER_INSTANCES.get(phoneId);
480             if (instanceManager == null) {
481                 ImsManager m = new ImsManager(context, phoneId);
482                 instanceManager = new InstanceManager(m);
483                 IMS_MANAGER_INSTANCES.put(phoneId, instanceManager);
484             }
485         }
486         // If the ImsManager became disconnected for some reason, try to reconnect it now.
487         instanceManager.reconnect();
488         return instanceManager.getInstance();
489     }
490 
491     /**
492      * Retrieve an FeatureConnector for ImsManager, which allows a Listener to listen for when
493      * the ImsManager becomes available or unavailable due to the ImsService MmTelFeature moving to
494      * the READY state or destroyed on a specific phone modem index.
495      *
496      * @param context The Context that will be used to connect the ImsManager.
497      * @param phoneId The modem phone ID that the ImsManager will be created for.
498      * @param logPrefix The log prefix used for debugging purposes.
499      * @param listener The Listener that will deliver ImsManager updates as it becomes available.
500      * @param executor The Executor that the Listener callbacks will be called on.
501      * @return A FeatureConnector instance for generating ImsManagers as the associated
502      * MmTelFeatures become available.
503      */
getConnector(Context context, int phoneId, String logPrefix, FeatureConnector.Listener<ImsManager> listener, Executor executor)504     public static FeatureConnector<ImsManager> getConnector(Context context,
505             int phoneId, String logPrefix, FeatureConnector.Listener<ImsManager> listener,
506             Executor executor) {
507         // Only listen for the READY state from the MmTelFeature here.
508         ArrayList<Integer> readyFilter = new ArrayList<>();
509         readyFilter.add(ImsFeature.STATE_READY);
510         return new FeatureConnector<>(context, phoneId, ImsManager::new, logPrefix, readyFilter,
511                 listener, executor);
512     }
513 
isImsSupportedOnDevice(Context context)514     public static boolean isImsSupportedOnDevice(Context context) {
515         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
516     }
517 
518     /**
519      * Sets the callback that will be called when events related to IMS metric collection occur.
520      * <p>
521      * Note: Subsequent calls to this method will replace the previous stats callback.
522      */
setImsStatsCallback(int phoneId, ImsStatsCallback cb)523     public static void setImsStatsCallback(int phoneId, ImsStatsCallback cb) {
524         synchronized (IMS_STATS_CALLBACKS) {
525             if (cb == null) {
526                 IMS_STATS_CALLBACKS.remove(phoneId);
527             } else {
528                 IMS_STATS_CALLBACKS.put(phoneId, cb);
529             }
530         }
531     }
532 
533     /**
534      * @return the {@link ImsStatsCallback} instance associated with the provided phoneId or
535      * {@link null} if none currently exists.
536      */
getStatsCallback(int phoneId)537     private static ImsStatsCallback getStatsCallback(int phoneId) {
538         synchronized (IMS_STATS_CALLBACKS) {
539             return IMS_STATS_CALLBACKS.get(phoneId);
540         }
541     }
542 
543     /**
544      * Returns the user configuration of Enhanced 4G LTE Mode setting.
545      *
546      * @deprecated Doesn't support MSIM devices. Use
547      * {@link #isEnhanced4gLteModeSettingEnabledByUser()} instead.
548      */
549     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isEnhanced4gLteModeSettingEnabledByUser(Context context)550     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
551         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
552         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
553         if (mgr != null) {
554             return mgr.isEnhanced4gLteModeSettingEnabledByUser();
555         }
556         Rlog.e(TAG, "isEnhanced4gLteModeSettingEnabledByUser: ImsManager null, returning default"
557                 + " value.");
558         return false;
559     }
560 
561     /**
562      * Returns the user configuration of Enhanced 4G LTE Mode setting for slot. If the option is
563      * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false),
564      * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), the setting is
565      * not initialized, and VoIMS opt-in status disabled, this method will return default value
566      * specified by {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
567      *
568      * Note that even if the setting was set, it may no longer be editable. If this is the case we
569      * return the default value.
570      */
isEnhanced4gLteModeSettingEnabledByUser()571     public boolean isEnhanced4gLteModeSettingEnabledByUser() {
572         int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
573                 getSubId(), SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
574                 SUB_PROPERTY_NOT_INITIALIZED);
575         boolean onByDefault = getBooleanCarrierConfig(
576                 CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL);
577         boolean isUiUnEditable =
578                 !getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)
579                 || getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL);
580         boolean isSettingNotInitialized = setting == SUB_PROPERTY_NOT_INITIALIZED;
581 
582         // If Enhanced 4G LTE Mode is uneditable, hidden, not initialized and VoIMS opt-in disabled
583         // we use the default value. If VoIMS opt-in is enabled, we will always allow the user to
584         // change the IMS enabled setting.
585         if ((isUiUnEditable || isSettingNotInitialized) && !isVoImsOptInEnabled()) {
586             return onByDefault;
587         } else {
588             return (setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
589         }
590     }
591 
592     /**
593      * Change persistent Enhanced 4G LTE Mode setting.
594      *
595      * @deprecated Doesn't support MSIM devices. Use {@link #setEnhanced4gLteModeSetting(boolean)}
596      * instead.
597      */
setEnhanced4gLteModeSetting(Context context, boolean enabled)598     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
599         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
600         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
601         if (mgr != null) {
602             mgr.setEnhanced4gLteModeSetting(enabled);
603         }
604         Rlog.e(TAG, "setEnhanced4gLteModeSetting: ImsManager null, value not set.");
605     }
606 
607     /**
608      * Change persistent Enhanced 4G LTE Mode setting. If the option is not editable
609      * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false),
610      * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), and VoIMS opt-in
611      * status disabled, this method will set the setting to the default value specified by
612      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
613      */
setEnhanced4gLteModeSetting(boolean enabled)614     public void setEnhanced4gLteModeSetting(boolean enabled) {
615         if (enabled && !isVolteProvisionedOnDevice()) {
616             log("setEnhanced4gLteModeSetting: Not possible to enable VoLTE due to provisioning.");
617             return;
618         }
619         int subId = getSubId();
620         if (!isSubIdValid(subId)) {
621             loge("setEnhanced4gLteModeSetting: invalid sub id, can not set property in " +
622                     " siminfo db; subId=" + subId);
623             return;
624         }
625         // If editable=false or hidden=true, we must keep default advanced 4G mode.
626         boolean isUiUnEditable =
627                 !getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) ||
628                         getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL);
629 
630         // If VoIMS opt-in is enabled, we will always allow the user to change the IMS enabled
631         // setting.
632         if (isUiUnEditable && !isVoImsOptInEnabled()) {
633             enabled = getBooleanCarrierConfig(
634                     CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL);
635         }
636 
637         int prevSetting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(subId,
638                 SubscriptionManager.ENHANCED_4G_MODE_ENABLED, SUB_PROPERTY_NOT_INITIALIZED);
639 
640         if (prevSetting == (enabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
641                 ProvisioningManager.PROVISIONING_VALUE_DISABLED)) {
642             // No change in setting.
643             return;
644         }
645         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
646                 SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
647                 booleanToPropertyString(enabled));
648         try {
649             if (enabled) {
650                 CapabilityChangeRequest request = new CapabilityChangeRequest();
651                 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
652                 // This affects voice and video enablement
653                 updateVoiceCellFeatureValue(request, isNonTty);
654                 updateVideoCallFeatureValue(request, isNonTty);
655                 changeMmTelCapability(request);
656                 // Ensure IMS is on if this setting is enabled.
657                 turnOnIms();
658             } else {
659                 // This may trigger entire IMS interface to be disabled, so recalculate full state.
660                 reevaluateCapabilities();
661             }
662         } catch (ImsException e) {
663             loge("setEnhanced4gLteModeSetting couldn't set config: " + e);
664         }
665     }
666 
667     /**
668      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
669      * supported.
670      * @deprecated Does not support MSIM devices. Please use
671      * {@link #isNonTtyOrTtyOnVolteEnabled()} instead.
672      */
673     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isNonTtyOrTtyOnVolteEnabled(Context context)674     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
675         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
676         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
677         if (mgr != null) {
678             return mgr.isNonTtyOrTtyOnVolteEnabled();
679         }
680         Rlog.e(TAG, "isNonTtyOrTtyOnVolteEnabled: ImsManager null, returning default value.");
681         return false;
682     }
683 
684     /**
685      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
686      * supported on a per slot basis.
687      */
isNonTtyOrTtyOnVolteEnabled()688     public boolean isNonTtyOrTtyOnVolteEnabled() {
689         if (isTtyOnVoLteCapable()) {
690             return true;
691         }
692 
693         TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
694         if (tm == null) {
695             logw("isNonTtyOrTtyOnVolteEnabled: telecom not available");
696             return true;
697         }
698         return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
699     }
700 
isTtyOnVoLteCapable()701     public boolean isTtyOnVoLteCapable() {
702         return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
703     }
704 
705     /**
706      * @return true if we are either not on TTY or TTY over VoWiFi is enabled. If we
707      * are on TTY and TTY over VoWiFi is not allowed, this method will return false.
708      */
isNonTtyOrTtyOnVoWifiEnabled()709     public boolean isNonTtyOrTtyOnVoWifiEnabled() {
710 
711         if (isTtyOnVoWifiCapable()) {
712             return true;
713         }
714 
715         TelecomManager tm = mContext.getSystemService(TelecomManager.class);
716         if (tm == null) {
717             logw("isNonTtyOrTtyOnVoWifiEnabled: telecom not available");
718             return true;
719         }
720         return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
721     }
722 
isTtyOnVoWifiCapable()723     public boolean isTtyOnVoWifiCapable() {
724         return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOWIFI_TTY_SUPPORTED_BOOL);
725     }
726 
727     /**
728      * Returns a platform configuration for VoLTE which may override the user setting.
729      * @deprecated Does not support MSIM devices. Please use
730      * {@link #isVolteEnabledByPlatform()} instead.
731      */
732     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isVolteEnabledByPlatform(Context context)733     public static boolean isVolteEnabledByPlatform(Context context) {
734         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
735         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
736         if (mgr != null) {
737             return mgr.isVolteEnabledByPlatform();
738         }
739         Rlog.e(TAG, "isVolteEnabledByPlatform: ImsManager null, returning default value.");
740         return false;
741     }
742 
743     /**
744      * Asynchronous call to ImsService to determine whether or not a specific MmTel capability is
745      * supported.
746      */
isSupported(int capability, int transportType, Consumer<Boolean> result)747     public void isSupported(int capability, int transportType, Consumer<Boolean> result) {
748         getImsThreadExecutor().execute(() -> {
749             switch(transportType) {
750                 // Does not take into account NR, as NR is a superset of LTE support currently.
751                 case (AccessNetworkConstants.TRANSPORT_TYPE_WWAN): {
752                     switch (capability) {
753                         case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE): {
754                             result.accept(isVolteEnabledByPlatform());
755                             return;
756                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO): {
757                             result.accept(isVtEnabledByPlatform());
758                             return;
759                         }case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT): {
760                             result.accept(isSuppServicesOverUtEnabledByPlatform());
761                             return;
762                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS): {
763                             // There is currently no carrier config defined for this.
764                             result.accept(true);
765                             return;
766                         }
767                     }
768                     break;
769                 } case (AccessNetworkConstants.TRANSPORT_TYPE_WLAN): {
770                     switch (capability) {
771                         case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE) : {
772                             result.accept(isWfcEnabledByPlatform());
773                             return;
774                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO) : {
775                             // This is not transport dependent at this time.
776                             result.accept(isVtEnabledByPlatform());
777                             return;
778                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT) : {
779                             // This is not transport dependent at this time.
780                             result.accept(isSuppServicesOverUtEnabledByPlatform());
781                             return;
782                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS) : {
783                             // There is currently no carrier config defined for this.
784                             result.accept(true);
785                             return;
786                         }
787                     }
788                     break;
789                 }
790             }
791             // false for unknown capability/transport types.
792             result.accept(false);
793         });
794 
795     }
796 
797     /**
798      * Returns a platform configuration for VoLTE which may override the user setting on a per Slot
799      * basis.
800      */
isVolteEnabledByPlatform()801     public boolean isVolteEnabledByPlatform() {
802         // We first read the per slot value. If doesn't exist, we read the general value. If still
803         // doesn't exist, we use the hardcoded default value.
804         if (SystemProperties.getInt(
805                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE + Integer.toString(mPhoneId),
806                 SYSTEM_PROPERTY_NOT_SET) == 1 ||
807                 SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
808                         SYSTEM_PROPERTY_NOT_SET) == 1) {
809             return true;
810         }
811 
812         if (getLocalImsConfigKeyInt(KEY_VOIMS_OPT_IN_STATUS)
813                 == ProvisioningManager.PROVISIONING_VALUE_ENABLED) {
814             return true;
815         }
816 
817         return mContext.getResources().getBoolean(
818                 com.android.internal.R.bool.config_device_volte_available)
819                 && getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
820                 && isGbaValid();
821     }
822 
823     /**
824      * @return {@code true} if IMS over NR is enabled by the platform, {@code false} otherwise.
825      */
isImsOverNrEnabledByPlatform()826     public boolean isImsOverNrEnabledByPlatform() {
827         int[] nrCarrierCaps = getIntArrayCarrierConfig(
828                 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
829         if (nrCarrierCaps == null)  return false;
830         boolean voNrCarrierSupported = Arrays.stream(nrCarrierCaps)
831                 .anyMatch(cap -> cap == CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA);
832         if (!voNrCarrierSupported) return false;
833         return isGbaValid();
834     }
835 
836     /**
837      * Indicates whether VoLTE is provisioned on device.
838      *
839      * @deprecated Does not support MSIM devices. Please use
840      * {@link #isVolteProvisionedOnDevice()} instead.
841      */
isVolteProvisionedOnDevice(Context context)842     public static boolean isVolteProvisionedOnDevice(Context context) {
843         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
844         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
845         if (mgr != null) {
846             return mgr.isVolteProvisionedOnDevice();
847         }
848         Rlog.e(TAG, "isVolteProvisionedOnDevice: ImsManager null, returning default value.");
849         return true;
850     }
851 
852     /**
853      * Indicates whether VoLTE is provisioned on this slot.
854      */
isVolteProvisionedOnDevice()855     public boolean isVolteProvisionedOnDevice() {
856         if (isMmTelProvisioningRequired(CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE)) {
857             return isVolteProvisioned();
858         }
859 
860         return true;
861     }
862 
863     /**
864      * Indicates whether EAB is provisioned on this slot.
865      */
isEabProvisionedOnDevice()866     public boolean isEabProvisionedOnDevice() {
867         if (isRcsProvisioningRequired(CAPABILITY_TYPE_PRESENCE_UCE, REGISTRATION_TECH_LTE)) {
868             return isEabProvisioned();
869         }
870 
871         return true;
872     }
873 
874     /**
875      * Indicates whether VoWifi is provisioned on device.
876      *
877      * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
878      * provisioned on device, this method returns false.
879      *
880      * @deprecated Does not support MSIM devices. Please use
881      * {@link #isWfcProvisionedOnDevice()} instead.
882      */
isWfcProvisionedOnDevice(Context context)883     public static boolean isWfcProvisionedOnDevice(Context context) {
884         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
885         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
886         if (mgr != null) {
887             return mgr.isWfcProvisionedOnDevice();
888         }
889         Rlog.e(TAG, "isWfcProvisionedOnDevice: ImsManager null, returning default value.");
890         return true;
891     }
892 
893     /**
894      * Indicates whether VoWifi is provisioned on slot.
895      *
896      * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
897      * provisioned on device, this method returns false.
898      */
isWfcProvisionedOnDevice()899     public boolean isWfcProvisionedOnDevice() {
900         if (getBooleanCarrierConfig(
901                 CarrierConfigManager.KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL)) {
902             if (!isVolteProvisionedOnDevice()) {
903                 return false;
904             }
905         }
906 
907         if (isMmTelProvisioningRequired(CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN)) {
908             return isWfcProvisioned();
909         }
910 
911         return true;
912     }
913 
914     /**
915      * Indicates whether VT is provisioned on device
916      *
917      * @deprecated Does not support MSIM devices. Please use
918      * {@link #isVtProvisionedOnDevice()} instead.
919      */
isVtProvisionedOnDevice(Context context)920     public static boolean isVtProvisionedOnDevice(Context context) {
921         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
922         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
923         if (mgr != null) {
924             return mgr.isVtProvisionedOnDevice();
925         }
926         Rlog.e(TAG, "isVtProvisionedOnDevice: ImsManager null, returning default value.");
927         return true;
928     }
929 
930     /**
931      * Indicates whether VT is provisioned on slot.
932      */
isVtProvisionedOnDevice()933     public boolean isVtProvisionedOnDevice() {
934         if (isMmTelProvisioningRequired(CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE)) {
935             return isVtProvisioned();
936         }
937 
938         return true;
939     }
940 
941     /**
942      * Returns a platform configuration for VT which may override the user setting.
943      *
944      * Note: VT presumes that VoLTE is enabled (these are configuration settings
945      * which must be done correctly).
946      *
947      * @deprecated Does not support MSIM devices. Please use
948      * {@link #isVtEnabledByPlatform()} instead.
949      */
isVtEnabledByPlatform(Context context)950     public static boolean isVtEnabledByPlatform(Context context) {
951         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
952         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
953         if (mgr != null) {
954             return mgr.isVtEnabledByPlatform();
955         }
956         Rlog.e(TAG, "isVtEnabledByPlatform: ImsManager null, returning default value.");
957         return false;
958     }
959 
960     /**
961      * Returns a platform configuration for VT which may override the user setting.
962      *
963      * Note: VT presumes that VoLTE is enabled (these are configuration settings
964      * which must be done correctly).
965      */
isVtEnabledByPlatform()966     public boolean isVtEnabledByPlatform() {
967         // We first read the per slot value. If doesn't exist, we read the general value. If still
968         // doesn't exist, we use the hardcoded default value.
969         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE +
970                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
971                 SystemProperties.getInt(
972                         PROPERTY_DBG_VT_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
973             return true;
974         }
975 
976         return mContext.getResources().getBoolean(
977                 com.android.internal.R.bool.config_device_vt_available) &&
978                 getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
979                 isGbaValid();
980     }
981 
982     /**
983      * Returns the user configuration of VT setting
984      * @deprecated Does not support MSIM devices. Please use
985      * {@link #isVtEnabledByUser()} instead.
986      */
isVtEnabledByUser(Context context)987     public static boolean isVtEnabledByUser(Context context) {
988         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
989         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
990         if (mgr != null) {
991             return mgr.isVtEnabledByUser();
992         }
993         Rlog.e(TAG, "isVtEnabledByUser: ImsManager null, returning default value.");
994         return false;
995     }
996 
997     /**
998      * Returns the user configuration of VT setting per slot. If not set, it
999      * returns true as default value.
1000      */
isVtEnabledByUser()1001     public boolean isVtEnabledByUser() {
1002         int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
1003                 getSubId(), SubscriptionManager.VT_IMS_ENABLED,
1004                 SUB_PROPERTY_NOT_INITIALIZED);
1005 
1006         // If it's never set, by default we return true.
1007         return (setting == SUB_PROPERTY_NOT_INITIALIZED
1008                 || setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
1009     }
1010 
1011     /**
1012      * Returns whether the user sets call composer setting per sub.
1013      */
isCallComposerEnabledByUser()1014     public boolean isCallComposerEnabledByUser() {
1015         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
1016         if (tm == null) {
1017             loge("isCallComposerEnabledByUser: TelephonyManager is null, returning false");
1018             return false;
1019         }
1020         return tm.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON;
1021     }
1022 
1023     /**
1024      * Change persistent VT enabled setting
1025      *
1026      * @deprecated Does not support MSIM devices. Please use {@link #setVtSetting(boolean)} instead.
1027      */
setVtSetting(Context context, boolean enabled)1028     public static void setVtSetting(Context context, boolean enabled) {
1029         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1030         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1031         if (mgr != null) {
1032             mgr.setVtSetting(enabled);
1033         }
1034         Rlog.e(TAG, "setVtSetting: ImsManager null, can not set value.");
1035     }
1036 
1037     /**
1038      * Change persistent VT enabled setting for slot.
1039      */
setVtSetting(boolean enabled)1040     public void setVtSetting(boolean enabled) {
1041         if (enabled && !isVtProvisionedOnDevice()) {
1042             log("setVtSetting: Not possible to enable Vt due to provisioning.");
1043             return;
1044         }
1045 
1046         int subId = getSubId();
1047         if (!isSubIdValid(subId)) {
1048             loge("setVtSetting: sub id invalid, skip modifying vt state in subinfo db; subId="
1049                     + subId);
1050             return;
1051         }
1052         mSubscriptionManagerProxy.setSubscriptionProperty(subId, SubscriptionManager.VT_IMS_ENABLED,
1053                 booleanToPropertyString(enabled));
1054         try {
1055             if (enabled) {
1056                 CapabilityChangeRequest request = new CapabilityChangeRequest();
1057                 updateVideoCallFeatureValue(request, isNonTtyOrTtyOnVolteEnabled());
1058                 changeMmTelCapability(request);
1059                 // ensure IMS is enabled.
1060                 turnOnIms();
1061             } else {
1062                 // This may cause IMS to be disabled, re-evaluate all.
1063                 reevaluateCapabilities();
1064             }
1065         } catch (ImsException e) {
1066             // The ImsService is down. Since the SubscriptionManager already recorded the user's
1067             // preference, it will be resent in updateImsServiceConfig when the ImsPhoneCallTracker
1068             // reconnects.
1069             loge("setVtSetting(b): ", e);
1070         }
1071     }
1072 
1073     /**
1074      * Returns whether turning off ims is allowed by platform.
1075      * The platform property may override the carrier config.
1076      */
isTurnOffImsAllowedByPlatform()1077     private boolean isTurnOffImsAllowedByPlatform() {
1078         // We first read the per slot value. If doesn't exist, we read the general value. If still
1079         // doesn't exist, we use the hardcoded default value.
1080         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE +
1081                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
1082                 SystemProperties.getInt(
1083                         PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
1084             return true;
1085         }
1086 
1087         return getBooleanCarrierConfig(
1088                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
1089     }
1090 
1091     /**
1092      * Returns the user configuration of WFC setting
1093      *
1094      * @deprecated Does not support MSIM devices. Please use
1095      * {@link #isWfcEnabledByUser()} instead.
1096      */
isWfcEnabledByUser(Context context)1097     public static boolean isWfcEnabledByUser(Context context) {
1098         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1099         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1100         if (mgr != null) {
1101             return mgr.isWfcEnabledByUser();
1102         }
1103         Rlog.e(TAG, "isWfcEnabledByUser: ImsManager null, returning default value.");
1104         return true;
1105     }
1106 
1107     /**
1108      * Returns the user configuration of WFC setting for slot. If not set, it
1109      * queries CarrierConfig value as default.
1110      */
isWfcEnabledByUser()1111     public boolean isWfcEnabledByUser() {
1112         int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
1113                 getSubId(), SubscriptionManager.WFC_IMS_ENABLED,
1114                 SUB_PROPERTY_NOT_INITIALIZED);
1115 
1116         // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db.
1117         if (setting == SUB_PROPERTY_NOT_INITIALIZED) {
1118             return getBooleanCarrierConfig(
1119                     CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL);
1120         } else {
1121             return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
1122         }
1123     }
1124 
1125     /**
1126      * Change persistent WFC enabled setting.
1127      * @deprecated Does not support MSIM devices. Please use
1128      * {@link #setWfcSetting} instead.
1129      */
setWfcSetting(Context context, boolean enabled)1130     public static void setWfcSetting(Context context, boolean enabled) {
1131         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1132         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1133         if (mgr != null) {
1134             mgr.setWfcSetting(enabled);
1135         }
1136         Rlog.e(TAG, "setWfcSetting: ImsManager null, can not set value.");
1137     }
1138 
1139     /**
1140      * Change persistent WFC enabled setting for slot.
1141      */
setWfcSetting(boolean enabled)1142     public void setWfcSetting(boolean enabled) {
1143         if (enabled && !isWfcProvisionedOnDevice()) {
1144             log("setWfcSetting: Not possible to enable WFC due to provisioning.");
1145             return;
1146         }
1147         int subId = getSubId();
1148         if (!isSubIdValid(subId)) {
1149             loge("setWfcSetting: invalid sub id, can not set WFC setting in siminfo db; subId="
1150                     + subId);
1151             return;
1152         }
1153         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
1154                 SubscriptionManager.WFC_IMS_ENABLED, booleanToPropertyString(enabled));
1155 
1156         try {
1157             if (enabled) {
1158                 boolean isNonTtyWifi = isNonTtyOrTtyOnVoWifiEnabled();
1159                 CapabilityChangeRequest request = new CapabilityChangeRequest();
1160                 updateVoiceWifiFeatureAndProvisionedValues(request, isNonTtyWifi);
1161                 changeMmTelCapability(request);
1162                 // Ensure IMS is on if this setting is updated.
1163                 turnOnIms();
1164             } else {
1165                 // This may cause IMS to be disabled, re-evaluate all caps
1166                 reevaluateCapabilities();
1167             }
1168         } catch (ImsException e) {
1169             loge("setWfcSetting: " + e);
1170         }
1171     }
1172 
1173     /**
1174      * @return true if the user's setting for Voice over Cross SIM is enabled and
1175      * false if it is not
1176      */
isCrossSimCallingEnabledByUser()1177     public boolean isCrossSimCallingEnabledByUser() {
1178         int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
1179                 getSubId(), SubscriptionManager.CROSS_SIM_CALLING_ENABLED,
1180                 SUB_PROPERTY_NOT_INITIALIZED);
1181 
1182         // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db.
1183         if (setting == SUB_PROPERTY_NOT_INITIALIZED) {
1184             return false;
1185         } else {
1186             return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
1187         }
1188     }
1189 
1190     /**
1191      * @return true if Voice over Cross SIM is provisioned and enabled by user and platform.
1192      * false if any of them is not true
1193      */
isCrossSimCallingEnabled()1194     public boolean isCrossSimCallingEnabled() {
1195         boolean userEnabled = isCrossSimCallingEnabledByUser();
1196         boolean platformEnabled = isCrossSimEnabledByPlatform();
1197         boolean isProvisioned = isWfcProvisionedOnDevice();
1198 
1199         log("isCrossSimCallingEnabled: platformEnabled = " + platformEnabled
1200                 + ", provisioned = " + isProvisioned
1201                 + ", userEnabled = " + userEnabled);
1202         return userEnabled && platformEnabled && isProvisioned;
1203     }
1204 
1205     /**
1206      * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
1207      */
setCrossSimCallingEnabled(boolean enabled)1208     public void setCrossSimCallingEnabled(boolean enabled) {
1209         if (enabled && !isWfcProvisionedOnDevice()) {
1210             log("setCrossSimCallingEnabled: Not possible to enable WFC due to provisioning.");
1211             return;
1212         }
1213         int subId = getSubId();
1214         if (!isSubIdValid(subId)) {
1215             loge("setCrossSimCallingEnabled: "
1216                     + "invalid sub id, can not set Cross SIM setting in siminfo db; subId="
1217                     + subId);
1218             return;
1219         }
1220         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
1221                 SubscriptionManager.CROSS_SIM_CALLING_ENABLED, booleanToPropertyString(enabled));
1222         try {
1223             if (enabled) {
1224                 CapabilityChangeRequest request = new CapabilityChangeRequest();
1225                 updateCrossSimFeatureAndProvisionedValues(request);
1226                 changeMmTelCapability(request);
1227                 turnOnIms();
1228             } else {
1229                 // Recalculate all caps to determine if IMS needs to be disabled.
1230                 reevaluateCapabilities();
1231             }
1232         } catch (ImsException e) {
1233             loge("setCrossSimCallingEnabled(): ", e);
1234         }
1235     }
1236 
1237     /**
1238      * Non-persistently change WFC enabled setting and WFC mode for slot
1239      *
1240      * @param enabled If true, WFC and WFC while roaming will be enabled for the associated
1241      *                subscription, if supported by the carrier. If false, WFC will be disabled for
1242      *                the associated subscription.
1243      * @param wfcMode The WFC preference if WFC is enabled
1244      */
setWfcNonPersistent(boolean enabled, int wfcMode)1245     public void setWfcNonPersistent(boolean enabled, int wfcMode) {
1246         // Force IMS to register over LTE when turning off WFC
1247         int imsWfcModeFeatureValue =
1248                 enabled ? wfcMode : ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED;
1249         try {
1250             changeMmTelCapability(enabled, MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1251                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
1252             // Set the mode and roaming enabled settings before turning on IMS
1253             setWfcModeInternal(imsWfcModeFeatureValue);
1254             // If enabled is false, shortcut to false because of the ImsService
1255             // implementation for WFC roaming, otherwise use the correct user's setting.
1256             setWfcRoamingSettingInternal(enabled && isWfcRoamingEnabledByUser());
1257             // Do not re-evaluate all capabilities because this is a temporary override of WFC
1258             // settings.
1259             if (enabled) {
1260                 log("setWfcNonPersistent() : turnOnIms");
1261                 // Ensure IMS is turned on if this is enabled.
1262                 turnOnIms();
1263             }
1264         } catch (ImsException e) {
1265             loge("setWfcNonPersistent(): ", e);
1266         }
1267     }
1268 
1269     /**
1270      * Returns the user configuration of WFC preference setting.
1271      *
1272      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean roaming)} instead.
1273      */
getWfcMode(Context context)1274     public static int getWfcMode(Context context) {
1275         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1276         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1277         if (mgr != null) {
1278             return mgr.getWfcMode();
1279         }
1280         Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
1281         return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
1282     }
1283 
1284     /**
1285      * Returns the user configuration of WFC preference setting
1286      * @deprecated. Use {@link #getWfcMode(boolean roaming)} instead.
1287      */
getWfcMode()1288     public int getWfcMode() {
1289         return getWfcMode(false);
1290     }
1291 
1292     /**
1293      * Change persistent WFC preference setting.
1294      *
1295      * @deprecated Doesn't support MSIM devices. Use {@link #setWfcMode(int)} instead.
1296      */
setWfcMode(Context context, int wfcMode)1297     public static void setWfcMode(Context context, int wfcMode) {
1298         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1299         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1300         if (mgr != null) {
1301             mgr.setWfcMode(wfcMode);
1302         }
1303         Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
1304     }
1305 
1306     /**
1307      * Change persistent WFC preference setting for slot when not roaming.
1308      * @deprecated Use {@link #setWfcMode(int, boolean)} instead.
1309      */
setWfcMode(int wfcMode)1310     public void setWfcMode(int wfcMode) {
1311         setWfcMode(wfcMode, false /*isRoaming*/);
1312     }
1313 
1314     /**
1315      * Returns the user configuration of WFC preference setting
1316      *
1317      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
1318      *
1319      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean)} instead.
1320      */
getWfcMode(Context context, boolean roaming)1321     public static int getWfcMode(Context context, boolean roaming) {
1322         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1323         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1324         if (mgr != null) {
1325             return mgr.getWfcMode(roaming);
1326         }
1327         Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
1328         return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
1329     }
1330 
1331     /**
1332      * Returns the user configuration of WFC preference setting for slot. If not set, it
1333      * queries CarrierConfig value as default.
1334      *
1335      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
1336      */
getWfcMode(boolean roaming)1337     public int getWfcMode(boolean roaming) {
1338         int setting;
1339         if (!roaming) {
1340             // The WFC mode is not editable, return the default setting in the CarrierConfig, not
1341             // the user set value.
1342             if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL)) {
1343                 setting = getIntCarrierConfig(
1344                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT);
1345 
1346             } else {
1347                 setting = getSettingFromSubscriptionManager(SubscriptionManager.WFC_IMS_MODE,
1348                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT);
1349             }
1350             if (DBG) log("getWfcMode - setting=" + setting);
1351         } else {
1352             if (getBooleanCarrierConfig(
1353                     CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)) {
1354                 setting = getWfcMode(false);
1355             } else if (!getBooleanCarrierConfig(
1356                     CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL)) {
1357                 setting = getIntCarrierConfig(
1358                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT);
1359             } else {
1360                 setting = getSettingFromSubscriptionManager(
1361                         SubscriptionManager.WFC_IMS_ROAMING_MODE,
1362                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT);
1363             }
1364             if (DBG) log("getWfcMode (roaming) - setting=" + setting);
1365         }
1366         return setting;
1367     }
1368 
1369     /**
1370      * Returns the SubscriptionManager setting for the subSetting string. If it is not set, default
1371      * to the default CarrierConfig value for defaultConfigKey.
1372      */
getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey)1373     private int getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey) {
1374         int result;
1375         result = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(getSubId(), subSetting,
1376                 SUB_PROPERTY_NOT_INITIALIZED);
1377 
1378         // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db.
1379         if (result == SUB_PROPERTY_NOT_INITIALIZED) {
1380             result = getIntCarrierConfig(defaultConfigKey);
1381         }
1382         return result;
1383     }
1384 
1385     /**
1386      * Change persistent WFC preference setting
1387      *
1388      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
1389      *
1390      * @deprecated Doesn't support MSIM devices. Please use {@link #setWfcMode(int, boolean)}
1391      * instead.
1392      */
setWfcMode(Context context, int wfcMode, boolean roaming)1393     public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
1394         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1395         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1396         if (mgr != null) {
1397             mgr.setWfcMode(wfcMode, roaming);
1398         }
1399         Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
1400     }
1401 
1402     /**
1403      * Change persistent WFC preference setting
1404      *
1405      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
1406      */
setWfcMode(int wfcMode, boolean roaming)1407     public void setWfcMode(int wfcMode, boolean roaming) {
1408         int subId = getSubId();
1409         if (isSubIdValid(subId)) {
1410             if (!roaming) {
1411                 if (DBG) log("setWfcMode(i,b) - setting=" + wfcMode);
1412                 mSubscriptionManagerProxy.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_MODE,
1413                         Integer.toString(wfcMode));
1414             } else {
1415                 if (DBG) log("setWfcMode(i,b) (roaming) - setting=" + wfcMode);
1416                 mSubscriptionManagerProxy.setSubscriptionProperty(subId,
1417                         SubscriptionManager.WFC_IMS_ROAMING_MODE, Integer.toString(wfcMode));
1418             }
1419         } else {
1420             loge("setWfcMode(i,b): invalid sub id, skip setting setting in siminfo db; subId="
1421                     + subId);
1422         }
1423 
1424         TelephonyManager tm = (TelephonyManager)
1425                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1426         if (tm == null) {
1427             loge("setWfcMode: TelephonyManager is null, can not set WFC.");
1428             return;
1429         }
1430         tm = tm.createForSubscriptionId(getSubId());
1431         // Unfortunately, the WFC mode is the same for home/roaming (we do not have separate
1432         // config keys), so we have to change the WFC mode when moving home<->roaming. So, only
1433         // call setWfcModeInternal when roaming == telephony roaming status. Otherwise, ignore.
1434         if (roaming == tm.isNetworkRoaming()) {
1435             setWfcModeInternal(wfcMode);
1436         }
1437     }
1438 
getSubId()1439     private int getSubId() {
1440         return mSubscriptionManagerProxy.getSubscriptionId(mPhoneId);
1441     }
1442 
setWfcModeInternal(int wfcMode)1443     private void setWfcModeInternal(int wfcMode) {
1444         final int value = wfcMode;
1445         getImsThreadExecutor().execute(() -> {
1446             try {
1447                 getConfigInterface().setConfig(
1448                         ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE, value);
1449             } catch (ImsException e) {
1450                 // do nothing
1451             }
1452         });
1453     }
1454 
1455     /**
1456      * Returns the user configuration of WFC roaming setting
1457      *
1458      * @deprecated Does not support MSIM devices. Please use
1459      * {@link #isWfcRoamingEnabledByUser()} instead.
1460      */
isWfcRoamingEnabledByUser(Context context)1461     public static boolean isWfcRoamingEnabledByUser(Context context) {
1462         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1463         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1464         if (mgr != null) {
1465             return mgr.isWfcRoamingEnabledByUser();
1466         }
1467         Rlog.e(TAG, "isWfcRoamingEnabledByUser: ImsManager null, returning default value.");
1468         return false;
1469     }
1470 
1471     /**
1472      * Returns the user configuration of WFC roaming setting for slot. If not set, it
1473      * queries CarrierConfig value as default.
1474      */
isWfcRoamingEnabledByUser()1475     public boolean isWfcRoamingEnabledByUser() {
1476         int setting =  mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
1477                 getSubId(), SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
1478                 SUB_PROPERTY_NOT_INITIALIZED);
1479         if (setting == SUB_PROPERTY_NOT_INITIALIZED) {
1480             return getBooleanCarrierConfig(
1481                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL);
1482         } else {
1483             return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
1484         }
1485     }
1486 
1487     /**
1488      * Change persistent WFC roaming enabled setting
1489      */
setWfcRoamingSetting(Context context, boolean enabled)1490     public static void setWfcRoamingSetting(Context context, boolean enabled) {
1491         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1492         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1493         if (mgr != null) {
1494             mgr.setWfcRoamingSetting(enabled);
1495         }
1496         Rlog.e(TAG, "setWfcRoamingSetting: ImsManager null, value not set.");
1497     }
1498 
1499     /**
1500      * Change persistent WFC roaming enabled setting
1501      */
setWfcRoamingSetting(boolean enabled)1502     public void setWfcRoamingSetting(boolean enabled) {
1503         mSubscriptionManagerProxy.setSubscriptionProperty(getSubId(),
1504                 SubscriptionManager.WFC_IMS_ROAMING_ENABLED, booleanToPropertyString(enabled)
1505         );
1506 
1507         setWfcRoamingSettingInternal(enabled);
1508     }
1509 
setWfcRoamingSettingInternal(boolean enabled)1510     private void setWfcRoamingSettingInternal(boolean enabled) {
1511         final int value = enabled
1512                 ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1513                 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1514         getImsThreadExecutor().execute(() -> {
1515             try {
1516                 getConfigInterface().setConfig(
1517                         ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE, value);
1518             } catch (ImsException e) {
1519                 // do nothing
1520             }
1521         });
1522     }
1523 
1524     /**
1525      * Returns a platform configuration for WFC which may override the user
1526      * setting. Note: WFC presumes that VoLTE is enabled (these are
1527      * configuration settings which must be done correctly).
1528      *
1529      * @deprecated Doesn't work for MSIM devices. Use {@link #isWfcEnabledByPlatform()}
1530      * instead.
1531      */
isWfcEnabledByPlatform(Context context)1532     public static boolean isWfcEnabledByPlatform(Context context) {
1533         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
1534         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
1535         if (mgr != null) {
1536             return mgr.isWfcEnabledByPlatform();
1537         }
1538         Rlog.e(TAG, "isWfcEnabledByPlatform: ImsManager null, returning default value.");
1539         return false;
1540     }
1541 
1542     /**
1543      * Returns a platform configuration for WFC which may override the user
1544      * setting per slot. Note: WFC presumes that VoLTE is enabled (these are
1545      * configuration settings which must be done correctly).
1546      */
isWfcEnabledByPlatform()1547     public boolean isWfcEnabledByPlatform() {
1548         // We first read the per slot value. If doesn't exist, we read the general value. If still
1549         // doesn't exist, we use the hardcoded default value.
1550         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE +
1551                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
1552                 SystemProperties.getInt(
1553                         PROPERTY_DBG_WFC_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
1554             return true;
1555         }
1556 
1557         return mContext.getResources().getBoolean(
1558                 com.android.internal.R.bool.config_device_wfc_ims_available) &&
1559                 getBooleanCarrierConfig(
1560                         CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1561                 isGbaValid();
1562     }
1563 
1564     /**
1565      * Returns a platform configuration for Cross SIM which may override the user
1566      * setting per slot. Note: Cross SIM presumes that VoLTE is enabled (these are
1567      * configuration settings which must be done correctly).
1568      */
isCrossSimEnabledByPlatform()1569     public boolean isCrossSimEnabledByPlatform() {
1570         if (isWfcEnabledByPlatform()) {
1571             return getBooleanCarrierConfig(
1572                     CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL);
1573         }
1574         return false;
1575     }
1576 
isSuppServicesOverUtEnabledByPlatform()1577     public boolean isSuppServicesOverUtEnabledByPlatform() {
1578         TelephonyManager manager = (TelephonyManager) mContext.getSystemService(
1579                 Context.TELEPHONY_SERVICE);
1580         int cardState = manager.getSimState(mPhoneId);
1581         if (cardState != TelephonyManager.SIM_STATE_READY) {
1582             // Do not report enabled until we actually have an active subscription.
1583             return false;
1584         }
1585         return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL) &&
1586                 isGbaValid();
1587     }
1588 
1589     /**
1590      * If carrier requires that IMS is only available if GBA capable SIM is used,
1591      * then this function checks GBA bit in EF IST.
1592      *
1593      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1594      */
isGbaValid()1595     private boolean isGbaValid() {
1596         if (getBooleanCarrierConfig(
1597                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1598             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
1599             if (tm == null) {
1600                 loge("isGbaValid: TelephonyManager is null, returning false.");
1601                 return false;
1602             }
1603             tm = tm.createForSubscriptionId(getSubId());
1604             String efIst = tm.getIsimIst();
1605             if (efIst == null) {
1606                 loge("isGbaValid - ISF is NULL");
1607                 return true;
1608             }
1609             boolean result = efIst != null && efIst.length() > 1 &&
1610                     (0x02 & (byte)efIst.charAt(1)) != 0;
1611             if (DBG) log("isGbaValid - GBA capable=" + result + ", ISF=" + efIst);
1612             return result;
1613         }
1614         return true;
1615     }
1616 
1617     /**
1618      * Will return with MmTel config value or return false if we receive an error from the AOSP
1619      * storage(ImsProvisioningController) implementation for that value.
1620      */
getImsProvisionedBoolNoException(int capability, int tech)1621     private boolean getImsProvisionedBoolNoException(int capability, int tech) {
1622         int subId = getSubId();
1623         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1624             logw("getImsProvisionedBoolNoException subId is invalid");
1625             return false;
1626         }
1627 
1628         ITelephony iTelephony = getITelephony();
1629         if (iTelephony == null) {
1630             logw("getImsProvisionedBoolNoException ITelephony interface is invalid");
1631             return false;
1632         }
1633 
1634         try {
1635             return iTelephony.getImsProvisioningStatusForCapability(subId, capability, tech);
1636         } catch (RemoteException | IllegalArgumentException e) {
1637             logw("getImsProvisionedBoolNoException: operation failed for capability=" + capability
1638                     + ". Exception:" + e.getMessage() + ". Returning false.");
1639             return false;
1640         }
1641     }
1642 
1643     /**
1644      * Will return with Rcs config value or return false if we receive an error from the AOSP
1645      * storage(ImsProvisioningController) implementation for that value.
1646      */
getRcsProvisionedBoolNoException(int capability, int tech)1647     private boolean getRcsProvisionedBoolNoException(int capability, int tech) {
1648         int subId = getSubId();
1649         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1650             logw("getRcsProvisionedBoolNoException subId is invalid");
1651             return false;
1652         }
1653 
1654         ITelephony iTelephony = getITelephony();
1655         if (iTelephony == null) {
1656             logw("getRcsProvisionedBoolNoException ITelephony interface is invalid");
1657             return false;
1658         }
1659 
1660         try {
1661             return iTelephony.getRcsProvisioningStatusForCapability(subId, capability, tech);
1662         } catch (RemoteException | IllegalArgumentException e) {
1663             logw("getRcsProvisionedBoolNoException: operation failed for capability=" + capability
1664                     + ". Exception:" + e.getMessage() + ". Returning false.");
1665             return false;
1666         }
1667     }
1668 
1669     /**
1670      * Push configuration updates to the ImsService implementation.
1671      */
updateImsServiceConfig()1672     public void updateImsServiceConfig() {
1673         try {
1674             int subId = getSubId();
1675             if (!isSubIdValid(subId)) {
1676                 loge("updateImsServiceConfig: invalid sub id, skipping!");
1677                 return;
1678             }
1679             PersistableBundle imsCarrierConfigs =
1680                     mConfigManager.getConfigByComponentForSubId(
1681                             CarrierConfigManager.Ims.KEY_PREFIX, subId);
1682             updateImsCarrierConfigs(imsCarrierConfigs);
1683             reevaluateCapabilities();
1684             mConfigUpdated = true;
1685         } catch (ImsException e) {
1686             loge("updateImsServiceConfig: ", e);
1687             mConfigUpdated = false;
1688         }
1689     }
1690 
1691     /**
1692      * Evaluate the state of the IMS capabilities and push the updated state to the ImsService.
1693      */
reevaluateCapabilities()1694     private void reevaluateCapabilities() throws ImsException {
1695         logi("reevaluateCapabilities");
1696         CapabilityChangeRequest request = new CapabilityChangeRequest();
1697         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
1698         boolean isNonTtyWifi = isNonTtyOrTtyOnVoWifiEnabled();
1699         updateVoiceCellFeatureValue(request, isNonTty);
1700         updateVoiceWifiFeatureAndProvisionedValues(request, isNonTtyWifi);
1701         updateCrossSimFeatureAndProvisionedValues(request);
1702         updateVideoCallFeatureValue(request, isNonTty);
1703         updateCallComposerFeatureValue(request);
1704         // Only turn on IMS for RTT if there's an active subscription present. If not, the
1705         // modem will be in emergency-call-only mode and will use separate signaling to
1706         // establish an RTT emergency call.
1707         boolean isImsNeededForRtt = updateRttConfigValue() && isActiveSubscriptionPresent();
1708         // Supplementary services over UT do not require IMS registration. Do not alter IMS
1709         // registration based on UT.
1710         updateUtFeatureValue(request);
1711 
1712         // Send the batched request to the modem.
1713         changeMmTelCapability(request);
1714 
1715         if (isImsNeededForRtt || !isTurnOffImsAllowedByPlatform() || isImsNeeded(request)) {
1716             // Turn on IMS if it is used.
1717             // Also, if turning off is not allowed for current carrier,
1718             // we need to turn IMS on because it might be turned off before
1719             // phone switched to current carrier.
1720             log("reevaluateCapabilities: turnOnIms");
1721             turnOnIms();
1722         } else {
1723             // Turn off IMS if it is not used AND turning off is allowed for carrier.
1724             log("reevaluateCapabilities: turnOffIms");
1725             turnOffIms();
1726         }
1727     }
1728 
1729     /**
1730      * @return {@code true} if IMS needs to be turned on for the request, {@code false} if it can
1731      * be disabled.
1732      */
isImsNeeded(CapabilityChangeRequest r)1733     private boolean isImsNeeded(CapabilityChangeRequest r) {
1734         return r.getCapabilitiesToEnable().stream()
1735                 .anyMatch(c -> isImsNeededForCapability(c.getCapability()));
1736     }
1737 
1738     /**
1739      * @return {@code true} if IMS needs to be turned on for the capability.
1740      */
isImsNeededForCapability(int capability)1741     private boolean isImsNeededForCapability(int capability) {
1742         // UT does not require IMS to be enabled.
1743         return capability != MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT &&
1744                 // call composer is used as part of calling, so it should not trigger the enablement
1745                 // of IMS.
1746                 capability != MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER;
1747     }
1748 
1749     /**
1750      * Update VoLTE config
1751      */
updateVoiceCellFeatureValue(CapabilityChangeRequest request, boolean isNonTty)1752     private void updateVoiceCellFeatureValue(CapabilityChangeRequest request, boolean isNonTty) {
1753         boolean available = isVolteEnabledByPlatform();
1754         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser();
1755         boolean isProvisioned = isVolteProvisionedOnDevice();
1756         boolean voLteFeatureOn = available && enabled && isNonTty && isProvisioned;
1757         boolean voNrAvailable = isImsOverNrEnabledByPlatform();
1758 
1759         log("updateVoiceCellFeatureValue: available = " + available
1760                 + ", enabled = " + enabled
1761                 + ", nonTTY = " + isNonTty
1762                 + ", provisioned = " + isProvisioned
1763                 + ", voLteFeatureOn = " + voLteFeatureOn
1764                 + ", voNrAvailable = " + voNrAvailable);
1765 
1766         if (voLteFeatureOn) {
1767             request.addCapabilitiesToEnableForTech(
1768                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1769                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1770         } else {
1771             request.addCapabilitiesToDisableForTech(
1772                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1773                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1774         }
1775         if (voLteFeatureOn && voNrAvailable) {
1776             request.addCapabilitiesToEnableForTech(
1777                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1778                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1779         } else {
1780             request.addCapabilitiesToDisableForTech(
1781                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1782                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1783         }
1784     }
1785 
1786     /**
1787      * Update video call configuration
1788      */
updateVideoCallFeatureValue(CapabilityChangeRequest request, boolean isNonTty)1789     private void updateVideoCallFeatureValue(CapabilityChangeRequest request, boolean isNonTty) {
1790         boolean available = isVtEnabledByPlatform();
1791         boolean vtEnabled = isVtEnabledByUser();
1792         boolean advancedEnabled = isEnhanced4gLteModeSettingEnabledByUser();
1793         boolean isDataEnabled = isDataEnabled();
1794         boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(
1795                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1796         boolean isProvisioned = isVtProvisionedOnDevice();
1797         // TODO: Support carrier config setting about if VT settings should be associated with
1798         //  advanced calling settings.
1799         boolean isLteFeatureOn = available && vtEnabled && isNonTty && isProvisioned
1800                 && advancedEnabled && (ignoreDataEnabledChanged || isDataEnabled);
1801         boolean nrAvailable = isImsOverNrEnabledByPlatform();
1802 
1803         log("updateVideoCallFeatureValue: available = " + available
1804                 + ", vtenabled = " + vtEnabled
1805                 + ", advancedCallEnabled = " + advancedEnabled
1806                 + ", nonTTY = " + isNonTty
1807                 + ", data enabled = " + isDataEnabled
1808                 + ", provisioned = " + isProvisioned
1809                 + ", isLteFeatureOn = " + isLteFeatureOn
1810                 + ", nrAvailable = " + nrAvailable);
1811 
1812         if (isLteFeatureOn) {
1813             request.addCapabilitiesToEnableForTech(
1814                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1815                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1816             // VT does not differentiate transport today, do not set IWLAN.
1817         } else {
1818             request.addCapabilitiesToDisableForTech(
1819                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1820                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1821             // VT does not differentiate transport today, do not set IWLAN.
1822         }
1823 
1824         if (isLteFeatureOn && nrAvailable) {
1825             request.addCapabilitiesToEnableForTech(
1826                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1827                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1828         } else {
1829             request.addCapabilitiesToDisableForTech(
1830                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1831                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1832         }
1833     }
1834 
1835     /**
1836      * Update WFC config
1837      */
updateVoiceWifiFeatureAndProvisionedValues(CapabilityChangeRequest request, boolean isNonTty)1838     private void updateVoiceWifiFeatureAndProvisionedValues(CapabilityChangeRequest request,
1839      boolean isNonTty) {
1840         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
1841         boolean isNetworkRoaming =  false;
1842         if (tm == null) {
1843             loge("updateVoiceWifiFeatureAndProvisionedValues: TelephonyManager is null, assuming"
1844                     + " not roaming.");
1845         } else {
1846             tm = tm.createForSubscriptionId(getSubId());
1847             isNetworkRoaming = tm.isNetworkRoaming();
1848         }
1849 
1850         boolean available = isWfcEnabledByPlatform();
1851         boolean enabled = isWfcEnabledByUser();
1852         boolean isProvisioned = isWfcProvisionedOnDevice();
1853         int mode = getWfcMode(isNetworkRoaming);
1854         boolean roaming = isWfcRoamingEnabledByUser();
1855         boolean isFeatureOn = available && enabled && isProvisioned;
1856 
1857         log("updateWfcFeatureAndProvisionedValues: available = " + available
1858                 + ", enabled = " + enabled
1859                 + ", mode = " + mode
1860                 + ", provisioned = " + isProvisioned
1861                 + ", roaming = " + roaming
1862                 + ", isFeatureOn = " + isFeatureOn
1863                 + ", isNonTtyWifi = " + isNonTty);
1864 
1865         if (isFeatureOn && isNonTty) {
1866             request.addCapabilitiesToEnableForTech(
1867                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1868                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
1869         } else {
1870             request.addCapabilitiesToDisableForTech(
1871                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1872                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
1873         }
1874 
1875         if (!isFeatureOn) {
1876             mode = ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED;
1877             roaming = false;
1878         }
1879         setWfcModeInternal(mode);
1880         setWfcRoamingSettingInternal(roaming);
1881     }
1882 
1883     /**
1884      * Update Cross SIM config
1885      */
updateCrossSimFeatureAndProvisionedValues(CapabilityChangeRequest request)1886     private void updateCrossSimFeatureAndProvisionedValues(CapabilityChangeRequest request) {
1887         if (isCrossSimCallingEnabled()) {
1888             request.addCapabilitiesToEnableForTech(
1889                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1890                     ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
1891         } else {
1892             request.addCapabilitiesToDisableForTech(
1893                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1894                     ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
1895         }
1896     }
1897 
1898 
updateUtFeatureValue(CapabilityChangeRequest request)1899     private void updateUtFeatureValue(CapabilityChangeRequest request) {
1900         boolean isCarrierSupported = isSuppServicesOverUtEnabledByPlatform();
1901 
1902         // check new carrier config first KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
1903         // if that returns false, check deprecated carrier config
1904         // KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
1905         boolean requiresProvisioning = isMmTelProvisioningRequired(CAPABILITY_TYPE_UT,
1906                 REGISTRATION_TECH_LTE) || getBooleanCarrierConfig(
1907                         CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL);
1908         // Count as "provisioned" if we do not require provisioning.
1909         boolean isProvisioned = true;
1910         if (requiresProvisioning) {
1911             ITelephony telephony = getITelephony();
1912             // Only track UT over LTE, since we do not differentiate between UT over LTE and IWLAN
1913             // currently.
1914             try {
1915                 if (telephony != null) {
1916                     isProvisioned = telephony.getImsProvisioningStatusForCapability(getSubId(),
1917                             MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1918                             ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1919                 }
1920             } catch (RemoteException e) {
1921                 loge("updateUtFeatureValue: couldn't reach telephony! returning provisioned");
1922             }
1923         }
1924         boolean isFeatureOn = isCarrierSupported && isProvisioned;
1925 
1926         log("updateUtFeatureValue: available = " + isCarrierSupported
1927                 + ", isProvisioned = " + isProvisioned
1928                 + ", enabled = " + isFeatureOn);
1929 
1930         if (isFeatureOn) {
1931             request.addCapabilitiesToEnableForTech(
1932                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1933                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1934         } else {
1935             request.addCapabilitiesToDisableForTech(
1936                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1937                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1938         }
1939     }
1940 
1941     /**
1942      * Update call composer capability
1943      */
updateCallComposerFeatureValue(CapabilityChangeRequest request)1944     private void updateCallComposerFeatureValue(CapabilityChangeRequest request) {
1945         boolean isUserSetEnabled = isCallComposerEnabledByUser();
1946         boolean isCarrierConfigEnabled = getBooleanCarrierConfig(
1947                 CarrierConfigManager.KEY_SUPPORTS_CALL_COMPOSER_BOOL);
1948 
1949         boolean isFeatureOn = isUserSetEnabled && isCarrierConfigEnabled;
1950         boolean nrAvailable = isImsOverNrEnabledByPlatform();
1951 
1952         log("updateCallComposerFeatureValue: isUserSetEnabled = " + isUserSetEnabled
1953                 + ", isCarrierConfigEnabled = " + isCarrierConfigEnabled
1954                 + ", isFeatureOn = " + isFeatureOn
1955                 + ", nrAvailable = " + nrAvailable);
1956 
1957         if (isFeatureOn) {
1958             request.addCapabilitiesToEnableForTech(
1959                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
1960                             ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1961         } else {
1962             request.addCapabilitiesToDisableForTech(
1963                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
1964                             ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1965         }
1966         if (isFeatureOn && nrAvailable) {
1967             request.addCapabilitiesToEnableForTech(
1968                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
1969                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1970         } else {
1971             request.addCapabilitiesToDisableForTech(
1972                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER,
1973                     ImsRegistrationImplBase.REGISTRATION_TECH_NR);
1974         }
1975     }
1976 
1977     /**
1978      * Do NOT use this directly, instead use {@link #getInstance(Context, int)}.
1979      */
ImsManager(Context context, int phoneId)1980     private ImsManager(Context context, int phoneId) {
1981         mContext = context;
1982         mPhoneId = phoneId;
1983         mSubscriptionManagerProxy = new DefaultSubscriptionManagerProxy(context);
1984         mSettingsProxy = new DefaultSettingsProxy();
1985         mConfigManager = (CarrierConfigManager) context.getSystemService(
1986                 Context.CARRIER_CONFIG_SERVICE);
1987         mExecutor = new LazyExecutor();
1988         mBinderCache = new BinderCacheManager<>(ImsManager::getITelephonyInterface);
1989         // Start off with an empty MmTelFeatureConnection, which will be replaced one an
1990         // ImsService is available (ImsManager expects a non-null FeatureConnection)
1991         associate(null /*container*/, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
1992     }
1993 
1994     /**
1995      * Used for testing only to inject dependencies.
1996      */
1997     @VisibleForTesting
ImsManager(Context context, int phoneId, MmTelFeatureConnectionFactory factory, SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy, BinderCacheManager binderCacheManager)1998     public ImsManager(Context context, int phoneId, MmTelFeatureConnectionFactory factory,
1999             SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy,
2000             BinderCacheManager binderCacheManager) {
2001         mContext = context;
2002         mPhoneId = phoneId;
2003         mMmTelFeatureConnectionFactory = factory;
2004         mSubscriptionManagerProxy = subManagerProxy;
2005         mSettingsProxy = settingsProxy;
2006         mConfigManager = (CarrierConfigManager) context.getSystemService(
2007                 Context.CARRIER_CONFIG_SERVICE);
2008         // Do not multithread tests
2009         mExecutor = Runnable::run;
2010         mBinderCache = binderCacheManager;
2011         // MmTelFeatureConnection should be replaced for tests with mMmTelFeatureConnectionFactory.
2012         associate(null /*container*/, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2013     }
2014 
2015     /*
2016      * Returns a flag indicating whether the IMS service is available.
2017      */
isServiceAvailable()2018     public boolean isServiceAvailable() {
2019         return mMmTelConnectionRef.get().isBinderAlive();
2020     }
2021 
2022     /*
2023      * Returns a flag indicating whether the IMS service is ready to send requests to lower layers.
2024      */
isServiceReady()2025     public boolean isServiceReady() {
2026         return mMmTelConnectionRef.get().isBinderReady();
2027     }
2028 
2029     /**
2030      * Opens the IMS service for making calls and/or receiving generic IMS calls as well as
2031      * register listeners for ECBM, Multiendpoint, and UT if the ImsService supports it.
2032      * <p>
2033      * The caller may make subsequent calls through {@link #makeCall}.
2034      * The IMS service will register the device to the operator's network with the credentials
2035      * (from ISIM) periodically in order to receive calls from the operator's network.
2036      * When the IMS service receives a new call, it will call
2037      * {@link MmTelFeature.Listener#onIncomingCall}
2038      * @param listener A {@link MmTelFeature.Listener}, which is the interface the
2039      * {@link MmTelFeature} uses to notify the framework of updates.
2040      * @param ecbmListener Listener used for ECBM indications.
2041      * @param multiEndpointListener Listener used for multiEndpoint indications.
2042      * @throws NullPointerException if {@code listener} is null
2043      * @throws ImsException if calling the IMS service results in an error
2044      */
open(MmTelFeature.Listener listener, ImsEcbmStateListener ecbmListener, ImsExternalCallStateListener multiEndpointListener)2045     public void open(MmTelFeature.Listener listener, ImsEcbmStateListener ecbmListener,
2046             ImsExternalCallStateListener multiEndpointListener) throws ImsException {
2047         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2048 
2049         if (listener == null) {
2050             throw new NullPointerException("listener can't be null");
2051         }
2052 
2053         try {
2054             c.openConnection(listener, ecbmListener, multiEndpointListener);
2055         } catch (RemoteException e) {
2056             throw new ImsException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2057         }
2058     }
2059 
2060     /**
2061      * Adds registration listener to the IMS service.
2062      *
2063      * @param serviceClass a service class specified in {@link ImsServiceClass}
2064      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
2065      * @param listener To listen to IMS registration events; It cannot be null
2066      * @throws NullPointerException if {@code listener} is null
2067      * @throws ImsException if calling the IMS service results in an error
2068      *
2069      * @deprecated Use {@link #addRegistrationListener(ImsConnectionStateListener)} instead.
2070      */
addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)2071     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
2072             throws ImsException {
2073         addRegistrationListener(listener);
2074     }
2075 
2076     /**
2077      * Adds registration listener to the IMS service.
2078      *
2079      * @param listener To listen to IMS registration events; It cannot be null
2080      * @throws NullPointerException if {@code listener} is null
2081      * @throws ImsException if calling the IMS service results in an error
2082      * @deprecated use {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback,
2083      * Executor)} instead.
2084      */
addRegistrationListener(ImsConnectionStateListener listener)2085     public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException {
2086         if (listener == null) {
2087             throw new NullPointerException("listener can't be null");
2088         }
2089         addRegistrationCallback(listener, getImsThreadExecutor());
2090         // connect the ImsConnectionStateListener to the new CapabilityCallback.
2091         addCapabilitiesCallback(new ImsMmTelManager.CapabilityCallback() {
2092             @Override
2093             public void onCapabilitiesStatusChanged(
2094                     MmTelFeature.MmTelCapabilities capabilities) {
2095                 listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), capabilities);
2096             }
2097         }, getImsThreadExecutor());
2098         log("Registration Callback registered.");
2099     }
2100 
2101     /**
2102      * Adds a callback that gets called when IMS registration has changed for the slot ID
2103      * associated with this ImsManager.
2104      * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
2105      *                 caller when IMS registration status has changed.
2106      * @param executor The Executor that the callback should be called on.
2107      * @throws ImsException when the ImsService connection is not available.
2108      */
addRegistrationCallback(RegistrationManager.RegistrationCallback callback, Executor executor)2109     public void addRegistrationCallback(RegistrationManager.RegistrationCallback callback,
2110             Executor executor)
2111             throws ImsException {
2112         if (callback == null) {
2113             throw new NullPointerException("registration callback can't be null");
2114         }
2115 
2116         try {
2117             callback.setExecutor(executor);
2118             mMmTelConnectionRef.get().addRegistrationCallback(callback.getBinder());
2119             log("Registration Callback registered.");
2120             // Only record if there isn't a RemoteException.
2121         } catch (IllegalStateException e) {
2122             throw new ImsException("addRegistrationCallback(IRIB)", e,
2123                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2124         }
2125     }
2126 
2127     /**
2128      * Removes a previously added registration callback that was added via
2129      * {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback, Executor)} .
2130      * @param callback A {@link RegistrationManager.RegistrationCallback} that was previously added.
2131      */
removeRegistrationListener(RegistrationManager.RegistrationCallback callback)2132     public void removeRegistrationListener(RegistrationManager.RegistrationCallback callback) {
2133         if (callback == null) {
2134             throw new NullPointerException("registration callback can't be null");
2135         }
2136         mMmTelConnectionRef.get().removeRegistrationCallback(callback.getBinder());
2137         log("Registration callback removed.");
2138     }
2139 
2140     /**
2141      * Adds a callback that gets called when IMS registration has changed for a specific
2142      * subscription.
2143      *
2144      * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
2145      *                 caller when IMS registration status has changed.
2146      * @param subId The subscription ID to register this registration callback for.
2147      * @throws RemoteException when the ImsService connection is not available.
2148      */
addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)2149     public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)
2150             throws RemoteException {
2151         if (callback == null) {
2152             throw new IllegalArgumentException("registration callback can't be null");
2153         }
2154         mMmTelConnectionRef.get().addRegistrationCallbackForSubscription(callback, subId);
2155         log("Registration Callback registered.");
2156         // Only record if there isn't a RemoteException.
2157     }
2158 
2159     /**
2160      * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback
2161      * that is associated with a specific subscription.
2162      */
removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)2163     public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
2164             int subId) {
2165         if (callback == null) {
2166             throw new IllegalArgumentException("registration callback can't be null");
2167         }
2168         mMmTelConnectionRef.get().removeRegistrationCallbackForSubscription(callback, subId);
2169     }
2170 
2171     /**
2172      * Adds a callback that gets called when MMTel capability status has changed, for example when
2173      * Voice over IMS or VT over IMS is not available currently.
2174      * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller when
2175      *                 MMTel capability status has changed.
2176      * @param executor The Executor that the callback should be called on.
2177      * @throws ImsException when the ImsService connection is not available.
2178      */
addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback, Executor executor)2179     public void addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback,
2180             Executor executor) throws ImsException {
2181         if (callback == null) {
2182             throw new NullPointerException("capabilities callback can't be null");
2183         }
2184 
2185         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2186         try {
2187             callback.setExecutor(executor);
2188             c.addCapabilityCallback(callback.getBinder());
2189             log("Capability Callback registered.");
2190             // Only record if there isn't a RemoteException.
2191         } catch (IllegalStateException e) {
2192             throw new ImsException("addCapabilitiesCallback(IF)", e,
2193                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2194         }
2195     }
2196 
2197     /**
2198      * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback.
2199      */
removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)2200     public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback) {
2201         if (callback == null) {
2202             throw new NullPointerException("capabilities callback can't be null");
2203         }
2204 
2205         try {
2206             MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2207             c.removeCapabilityCallback(callback.getBinder());
2208         } catch (ImsException e) {
2209             log("Exception removing Capability , exception=" + e);
2210         }
2211     }
2212 
2213     /**
2214      * Adds a callback that gets called when IMS capabilities have changed for a specified
2215      * subscription.
2216      * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller
2217      *                 when the IMS Capabilities have changed.
2218      * @param subId The subscription that is associated with the callback.
2219      * @throws RemoteException when the ImsService connection is not available.
2220      */
addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)2221     public void addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)
2222             throws RemoteException {
2223         if (callback == null) {
2224             throw new IllegalArgumentException("registration callback can't be null");
2225         }
2226         mMmTelConnectionRef.get().addCapabilityCallbackForSubscription(callback, subId);
2227         log("Capability Callback registered for subscription.");
2228     }
2229 
2230     /**
2231      * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} that was
2232      * associated with a specific subscription.
2233      */
removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)2234     public void removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback,
2235             int subId) {
2236         if (callback == null) {
2237             throw new IllegalArgumentException("capabilities callback can't be null");
2238         }
2239         mMmTelConnectionRef.get().removeCapabilityCallbackForSubscription(callback, subId);
2240     }
2241 
2242     /**
2243      * Removes the registration listener from the IMS service.
2244      *
2245      * @param listener Previously registered listener that will be removed. Can not be null.
2246      * @throws NullPointerException if {@code listener} is null
2247      * @throws ImsException if calling the IMS service results in an error
2248      * instead.
2249      */
removeRegistrationListener(ImsConnectionStateListener listener)2250     public void removeRegistrationListener(ImsConnectionStateListener listener)
2251             throws ImsException {
2252         if (listener == null) {
2253             throw new NullPointerException("listener can't be null");
2254         }
2255 
2256         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2257         c.removeRegistrationCallback(listener.getBinder());
2258         log("Registration Callback/Listener registered.");
2259         // Only record if there isn't a RemoteException.
2260     }
2261 
2262     /**
2263      * Adds a callback that gets called when Provisioning has changed for a specified subscription.
2264      * @param callback A {@link ProvisioningManager.Callback} that will notify the caller when
2265      *                 provisioning has changed.
2266      * @param subId The subscription that is associated with the callback.
2267      * @throws IllegalStateException when the {@link ImsService} connection is not available.
2268      * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null.
2269      */
addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)2270     public void addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) {
2271         if (callback == null) {
2272             throw new IllegalArgumentException("provisioning callback can't be null");
2273         }
2274 
2275         mMmTelConnectionRef.get().addProvisioningCallbackForSubscription(callback, subId);
2276         log("Capability Callback registered for subscription.");
2277     }
2278 
2279     /**
2280      * Removes a previously registered {@link ProvisioningManager.Callback} that was associated with
2281      * a specific subscription.
2282      * @throws IllegalStateException when the {@link ImsService} connection is not available.
2283      * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null.
2284      */
removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)2285     public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) {
2286         if (callback == null) {
2287             throw new IllegalArgumentException("provisioning callback can't be null");
2288         }
2289 
2290         mMmTelConnectionRef.get().removeProvisioningCallbackForSubscription(callback, subId);
2291     }
2292 
getRegistrationTech()2293     public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() {
2294         try {
2295             return mMmTelConnectionRef.get().getRegistrationTech();
2296         } catch (RemoteException e) {
2297             logw("getRegistrationTech: no connection to ImsService.");
2298             return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
2299         }
2300     }
2301 
getRegistrationTech(Consumer<Integer> callback)2302     public void getRegistrationTech(Consumer<Integer> callback) {
2303         getImsThreadExecutor().execute(() -> {
2304             try {
2305                 int tech = mMmTelConnectionRef.get().getRegistrationTech();
2306                 callback.accept(tech);
2307             } catch (RemoteException e) {
2308                 logw("getRegistrationTech(C): no connection to ImsService.");
2309                 callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
2310             }
2311         });
2312     }
2313 
2314     /**
2315      * Closes the connection opened in {@link #open} and removes the associated listeners.
2316      */
close()2317     public void close() {
2318         mMmTelConnectionRef.get().closeConnection();
2319     }
2320 
2321     /**
2322      * Create or get the existing configuration interface to provision / withdraw the supplementary
2323      * service settings.
2324      * <p>
2325      * There can only be one connection to the UT interface, so this may only be called by one
2326      * ImsManager instance. Otherwise, an IllegalStateException will be thrown.
2327      *
2328      * @return the Ut interface instance
2329      * @throws ImsException if getting the Ut interface results in an error
2330      */
createOrGetSupplementaryServiceConfiguration()2331     public ImsUtInterface createOrGetSupplementaryServiceConfiguration() throws ImsException {
2332         ImsUt iUt;
2333         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2334         try {
2335             iUt = c.createOrGetUtInterface();
2336             if (iUt == null) {
2337                 throw new ImsException("getSupplementaryServiceConfiguration()",
2338                         ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
2339             }
2340         } catch (RemoteException e) {
2341             throw new ImsException("getSupplementaryServiceConfiguration()", e,
2342                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2343         }
2344         return iUt;
2345     }
2346 
2347     /**
2348      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
2349      *
2350      * @param serviceType a service type that is specified in {@link ImsCallProfile}
2351      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
2352      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
2353      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
2354      * @param callType a call type that is specified in {@link ImsCallProfile}
2355      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
2356      *        {@link ImsCallProfile#CALL_TYPE_VT}
2357      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
2358      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
2359      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
2360      *        {@link ImsCallProfile#CALL_TYPE_VS}
2361      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
2362      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
2363      * @return a {@link ImsCallProfile} object
2364      * @throws ImsException if calling the IMS service results in an error
2365      */
createCallProfile(int serviceType, int callType)2366     public ImsCallProfile createCallProfile(int serviceType, int callType) throws ImsException {
2367         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2368 
2369         try {
2370             return c.createCallProfile(serviceType, callType);
2371         } catch (RemoteException e) {
2372             throw new ImsException("createCallProfile()", e,
2373                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2374         }
2375     }
2376 
2377     /**
2378      * Informs the {@link ImsService} of the {@link RtpHeaderExtensionType}s which the framework
2379      * intends to use for incoming and outgoing calls.
2380      * <p>
2381      * See {@link RtpHeaderExtensionType} for more information.
2382      * @param types The RTP header extension types to use for incoming and outgoing calls, or
2383      *              empty list if none defined.
2384      * @throws ImsException
2385      */
setOfferedRtpHeaderExtensionTypes(@onNull Set<RtpHeaderExtensionType> types)2386     public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> types)
2387             throws ImsException {
2388         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2389 
2390         try {
2391             c.changeOfferedRtpHeaderExtensionTypes(types);
2392         } catch (RemoteException e) {
2393             throw new ImsException("setOfferedRtpHeaderExtensionTypes()", e,
2394                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2395         }
2396     }
2397 
2398     /**
2399      * Creates a {@link ImsCall} to make a call.
2400      *
2401      * @param profile a call profile to make the call
2402      *      (it contains service type, call type, media information, etc.)
2403      * @param callees participants to invite the conference call
2404      * @param listener listen to the call events from {@link ImsCall}
2405      * @return a {@link ImsCall} object
2406      * @throws ImsException if calling the IMS service results in an error
2407      */
makeCall(ImsCallProfile profile, String[] callees, ImsCall.Listener listener)2408     public ImsCall makeCall(ImsCallProfile profile, String[] callees,
2409             ImsCall.Listener listener) throws ImsException {
2410         if (DBG) {
2411             log("makeCall :: profile=" + profile);
2412         }
2413 
2414         // Check we are still alive
2415         getOrThrowExceptionIfServiceUnavailable();
2416 
2417         ImsCall call = new ImsCall(mContext, profile);
2418 
2419         call.setListener(listener);
2420         ImsCallSession session = createCallSession(profile);
2421 
2422         if ((callees != null) && (callees.length == 1) && !(session.isMultiparty())) {
2423             call.start(session, callees[0]);
2424         } else {
2425             call.start(session, callees);
2426         }
2427 
2428         return call;
2429     }
2430 
2431     /**
2432      * Creates a {@link ImsCall} to take an incoming call.
2433      *
2434      * @param listener to listen to the call events from {@link ImsCall}
2435      * @return a {@link ImsCall} object
2436      * @throws ImsException if calling the IMS service results in an error
2437      */
takeCall(IImsCallSession session, ImsCall.Listener listener)2438     public ImsCall takeCall(IImsCallSession session, ImsCall.Listener listener)
2439             throws ImsException {
2440         // Check we are still alive
2441         getOrThrowExceptionIfServiceUnavailable();
2442         try {
2443             if (session == null) {
2444                 throw new ImsException("No pending session for the call",
2445                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
2446             }
2447 
2448             ImsCall call = new ImsCall(mContext, session.getCallProfile());
2449 
2450             call.attachSession(new ImsCallSession(session));
2451             call.setListener(listener);
2452 
2453             return call;
2454         } catch (Throwable t) {
2455             loge("takeCall caught: ", t);
2456             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
2457         }
2458     }
2459 
2460     /**
2461      * Gets the config interface to get/set service/capability parameters.
2462      *
2463      * @return the ImsConfig instance.
2464      * @throws ImsException if getting the setting interface results in an error.
2465      */
2466     @UnsupportedAppUsage
getConfigInterface()2467     public ImsConfig getConfigInterface() throws ImsException {
2468         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2469 
2470         IImsConfig config = c.getConfig();
2471         if (config == null) {
2472             throw new ImsException("getConfigInterface()",
2473                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
2474         }
2475         return new ImsConfig(config);
2476     }
2477 
2478     /**
2479      * Enable or disable a capability for multiple radio technologies.
2480      */
changeMmTelCapability(boolean isEnabled, int capability, int... radioTechs)2481     public void changeMmTelCapability(boolean isEnabled, int capability,
2482             int... radioTechs) throws ImsException {
2483         CapabilityChangeRequest request = new CapabilityChangeRequest();
2484         if (isEnabled) {
2485             for (int tech : radioTechs) {
2486                 request.addCapabilitiesToEnableForTech(capability, tech);
2487             }
2488         } else {
2489             for (int tech : radioTechs) {
2490                 request.addCapabilitiesToDisableForTech(capability, tech);
2491             }
2492         }
2493         changeMmTelCapability(request);
2494     }
2495 
changeMmTelCapability(CapabilityChangeRequest r)2496     private void changeMmTelCapability(CapabilityChangeRequest r) throws ImsException {
2497         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2498         try {
2499             logi("changeMmTelCapability: changing capabilities for sub: " + getSubId()
2500                     + ", request: " + r);
2501             c.changeEnabledCapabilities(r, null);
2502             ImsStatsCallback cb = getStatsCallback(mPhoneId);
2503             if (cb == null) {
2504                 return;
2505             }
2506             for (CapabilityChangeRequest.CapabilityPair enabledCaps : r.getCapabilitiesToEnable()) {
2507                 cb.onEnabledMmTelCapabilitiesChanged(enabledCaps.getCapability(),
2508                         enabledCaps.getRadioTech(), true);
2509             }
2510             for (CapabilityChangeRequest.CapabilityPair disabledCaps :
2511                     r.getCapabilitiesToDisable()) {
2512                 cb.onEnabledMmTelCapabilitiesChanged(disabledCaps.getCapability(),
2513                         disabledCaps.getRadioTech(), false);
2514             }
2515         } catch (RemoteException e) {
2516             throw new ImsException("changeMmTelCapability(CCR)", e,
2517                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2518         }
2519     }
2520 
updateRttConfigValue()2521     private boolean updateRttConfigValue() {
2522         // If there's no active sub anywhere on the device, enable RTT on the modem so that
2523         // the device can make an emergency call.
2524 
2525         boolean isActiveSubscriptionPresent = isActiveSubscriptionPresent();
2526         boolean isCarrierSupported =
2527                 getBooleanCarrierConfig(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL)
2528                 || !isActiveSubscriptionPresent;
2529 
2530         int defaultRttMode =
2531                 getIntCarrierConfig(CarrierConfigManager.KEY_DEFAULT_RTT_MODE_INT);
2532         int rttMode = mSettingsProxy.getSecureIntSetting(mContext.getContentResolver(),
2533                 Settings.Secure.RTT_CALLING_MODE, defaultRttMode);
2534         logi("defaultRttMode = " + defaultRttMode + " rttMode = " + rttMode);
2535         boolean isRttAlwaysOnCarrierConfig = getBooleanCarrierConfig(
2536                 CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
2537         if (isRttAlwaysOnCarrierConfig && rttMode == defaultRttMode) {
2538             mSettingsProxy.putSecureIntSetting(mContext.getContentResolver(),
2539                     Settings.Secure.RTT_CALLING_MODE, defaultRttMode);
2540         }
2541 
2542         boolean isRttUiSettingEnabled = mSettingsProxy.getSecureIntSetting(
2543                 mContext.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
2544 
2545         boolean shouldImsRttBeOn = isRttUiSettingEnabled || isRttAlwaysOnCarrierConfig;
2546         logi("update RTT: settings value: " + isRttUiSettingEnabled + " always-on carrierconfig: "
2547                 + isRttAlwaysOnCarrierConfig
2548                 + "isActiveSubscriptionPresent: " + isActiveSubscriptionPresent);
2549 
2550         if (isCarrierSupported) {
2551             setRttConfig(shouldImsRttBeOn);
2552         } else {
2553             setRttConfig(false);
2554         }
2555         return isCarrierSupported && shouldImsRttBeOn;
2556     }
2557 
setRttConfig(boolean enabled)2558     private void setRttConfig(boolean enabled) {
2559         final int value = enabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2560                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2561         getImsThreadExecutor().execute(() -> {
2562             try {
2563                 logi("Setting RTT enabled to " + enabled);
2564                 getConfigInterface().setProvisionedValue(
2565                         ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value);
2566             } catch (ImsException e) {
2567                 loge("Unable to set RTT value enabled to " + enabled + ": " + e);
2568             }
2569         });
2570     }
2571 
queryMmTelCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2572     public boolean queryMmTelCapability(
2573             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
2574             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
2575         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2576 
2577         BlockingQueue<Boolean> result = new LinkedBlockingDeque<>(1);
2578 
2579         try {
2580             c.queryEnabledCapabilities(capability, radioTech, new IImsCapabilityCallback.Stub() {
2581                         @Override
2582                         public void onQueryCapabilityConfiguration(int resCap, int resTech,
2583                                 boolean enabled) {
2584                             if (resCap == capability && resTech == radioTech) {
2585                                 result.offer(enabled);
2586                             }
2587                         }
2588 
2589                         @Override
2590                         public void onChangeCapabilityConfigurationError(int capability,
2591                                 int radioTech, int reason) {
2592 
2593                         }
2594 
2595                         @Override
2596                         public void onCapabilitiesStatusChanged(int config) {
2597 
2598                         }
2599                     });
2600         } catch (RemoteException e) {
2601             throw new ImsException("queryMmTelCapability()", e,
2602                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2603         }
2604 
2605         try {
2606             return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
2607         } catch (InterruptedException e) {
2608             logw("queryMmTelCapability: interrupted while waiting for response");
2609         }
2610         return false;
2611     }
2612 
queryMmTelCapabilityStatus( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2613     public boolean queryMmTelCapabilityStatus(
2614             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
2615             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
2616         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2617 
2618         if (getRegistrationTech() != radioTech)
2619             return false;
2620 
2621         try {
2622 
2623             MmTelFeature.MmTelCapabilities capabilities =
2624                     c.queryCapabilityStatus();
2625 
2626             return capabilities.isCapable(capability);
2627         } catch (RemoteException e) {
2628             throw new ImsException("queryMmTelCapabilityStatus()", e,
2629                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2630         }
2631     }
2632 
2633     /**
2634      * Enable the RTT configuration on this device.
2635      */
setRttEnabled(boolean enabled)2636     public void setRttEnabled(boolean enabled) {
2637         if (enabled) {
2638             // Override this setting if RTT is enabled.
2639             setEnhanced4gLteModeSetting(true /*enabled*/);
2640         }
2641         setRttConfig(enabled);
2642     }
2643 
2644     /**
2645      * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
2646      */
setTtyMode(int ttyMode)2647     public void setTtyMode(int ttyMode) throws ImsException {
2648         boolean isNonTtyOrTtyOnVolteEnabled = isTtyOnVoLteCapable() ||
2649                 (ttyMode == TelecomManager.TTY_MODE_OFF);
2650 
2651         boolean isNonTtyOrTtyOnWifiEnabled = isTtyOnVoWifiCapable() ||
2652                 (ttyMode == TelecomManager.TTY_MODE_OFF);
2653 
2654         CapabilityChangeRequest request = new CapabilityChangeRequest();
2655         updateVoiceCellFeatureValue(request, isNonTtyOrTtyOnVolteEnabled);
2656         updateVideoCallFeatureValue(request, isNonTtyOrTtyOnVolteEnabled);
2657         updateVoiceWifiFeatureAndProvisionedValues(request, isNonTtyOrTtyOnWifiEnabled);
2658         // update MMTEL caps for the new configuration.
2659         changeMmTelCapability(request);
2660         if (isImsNeeded(request)) {
2661             // Only turn on IMS if voice/video is enabled now in the new configuration.
2662             turnOnIms();
2663         }
2664     }
2665 
2666     /**
2667      * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
2668      * settings screen.
2669      * @param uiTtyMode TTY Mode, valid options are:
2670      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
2671      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
2672      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
2673      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
2674      * @param onComplete A Message that will be called by the ImsService when it has completed this
2675      *           operation or null if not waiting for an async response. The Message must contain a
2676      *           valid {@link Message#replyTo} {@link android.os.Messenger}, since it will be passed
2677      *           through Binder to another process.
2678      */
setUiTTYMode(Context context, int uiTtyMode, Message onComplete)2679     public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete)
2680             throws ImsException {
2681 
2682         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2683         try {
2684             c.setUiTTYMode(uiTtyMode, onComplete);
2685         } catch (RemoteException e) {
2686             throw new ImsException("setTTYMode()", e,
2687                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2688         }
2689     }
2690 
2691     /**
2692      * Notifies the change of user setting.
2693      *
2694      * @param enabled indicates whether the user setting for call waiting is enabled or not.
2695      */
setTerminalBasedCallWaitingStatus(boolean enabled)2696     public void setTerminalBasedCallWaitingStatus(boolean enabled) throws ImsException {
2697         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2698         try {
2699             c.setTerminalBasedCallWaitingStatus(enabled);
2700         } catch (ServiceSpecificException se) {
2701             if (se.errorCode
2702                     == android.telephony.ims.ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
2703                 throw new ImsException("setTerminalBasedCallWaitingStatus()", se,
2704                         ImsReasonInfo.CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE);
2705             } else {
2706                 throw new ImsException("setTerminalBasedCallWaitingStatus()", se,
2707                         ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
2708             }
2709         } catch (RemoteException e) {
2710             throw new ImsException("setTerminalBasedCallWaitingStatus()", e,
2711                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2712         }
2713     }
2714 
2715     /**
2716      * Returns whether all of the capabilities specified are capable or not.
2717      */
isCapable(@msService.ImsServiceCapability long capabilities)2718     public boolean isCapable(@ImsService.ImsServiceCapability long capabilities)
2719             throws ImsException {
2720         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2721         try {
2722             return c.isCapable(capabilities);
2723         } catch (RemoteException e) {
2724             throw new ImsException("isCapable()", e,
2725                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2726         }
2727     }
2728 
2729     /**
2730      * Notifies SRVCC started.
2731      * @param cb The callback to receive the list of {@link SrvccCall}.
2732      */
notifySrvccStarted(ISrvccStartedCallback cb)2733     public void notifySrvccStarted(ISrvccStartedCallback cb)
2734             throws ImsException {
2735         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2736         try {
2737             c.notifySrvccStarted(cb);
2738         } catch (RemoteException e) {
2739             throw new ImsException("notifySrvccStarted", e,
2740                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2741         }
2742     }
2743 
2744     /**
2745      * Notifies SRVCC is completed, IMS service will hang up all calls.
2746      */
notifySrvccCompleted()2747     public void notifySrvccCompleted() throws ImsException {
2748         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2749         try {
2750             c.notifySrvccCompleted();
2751         } catch (RemoteException e) {
2752             throw new ImsException("notifySrvccCompleted", e,
2753                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2754         }
2755     }
2756 
2757     /**
2758      * Notifies SRVCC failed. IMS service will recover and continue calls over IMS.
2759      */
notifySrvccFailed()2760     public void notifySrvccFailed() throws ImsException {
2761         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2762         try {
2763             c.notifySrvccFailed();
2764         } catch (RemoteException e) {
2765             throw new ImsException("notifySrvccFailed", e,
2766                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2767         }
2768     }
2769 
2770     /**
2771      * Notifies SRVCC is canceled. IMS service will recover and continue calls over IMS.
2772      */
notifySrvccCanceled()2773     public void notifySrvccCanceled() throws ImsException {
2774         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2775         try {
2776             c.notifySrvccCanceled();
2777         } catch (RemoteException e) {
2778             throw new ImsException("notifySrvccCanceled", e,
2779                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2780         }
2781     }
2782 
2783     /**
2784      * Notifies that radio triggered IMS deregistration.
2785      * @param reason the reason why the deregistration is triggered.
2786      */
triggerDeregistration(@msRegistrationImplBase.ImsDeregistrationReason int reason)2787     public void triggerDeregistration(@ImsRegistrationImplBase.ImsDeregistrationReason int reason)
2788             throws ImsException {
2789         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2790         try {
2791             c.triggerDeregistration(reason);
2792         } catch (RemoteException e) {
2793             throw new ImsException("triggerDeregistration", e,
2794                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2795         }
2796     }
2797 
getImsServiceState()2798     public int getImsServiceState() throws ImsException {
2799         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2800         return c.getFeatureState();
2801     }
2802 
setMediaThreshold(@ediaQualityStatus.MediaSessionType int sessionType, MediaThreshold threshold)2803     public void setMediaThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
2804             MediaThreshold threshold) throws ImsException {
2805         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2806         try {
2807             c.setMediaThreshold(sessionType, threshold);
2808         } catch (RemoteException e) {
2809             loge("setMediaThreshold Failed.");
2810         }
2811     }
2812 
queryMediaQualityStatus( @ediaQualityStatus.MediaSessionType int sessionType)2813     public MediaQualityStatus queryMediaQualityStatus (
2814             @MediaQualityStatus.MediaSessionType int sessionType) throws ImsException {
2815         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
2816         try {
2817             return c.queryMediaQualityStatus(sessionType);
2818         } catch (RemoteException e) {
2819             loge("queryMediaQualityStatus Failed.");
2820             return null;
2821         }
2822     }
2823 
2824     @Override
updateFeatureState(int state)2825     public void updateFeatureState(int state) {
2826         mMmTelConnectionRef.get().updateFeatureState(state);
2827     }
2828 
2829     @Override
updateFeatureCapabilities(long capabilities)2830     public void updateFeatureCapabilities(long capabilities) {
2831         mMmTelConnectionRef.get().updateFeatureCapabilities(capabilities);
2832     }
2833 
getImsServiceState(Consumer<Integer> result)2834     public void getImsServiceState(Consumer<Integer> result) {
2835         getImsThreadExecutor().execute(() -> {
2836             try {
2837                 result.accept(getImsServiceState());
2838             } catch (ImsException e) {
2839                 // In the case that the ImsService is not available, report unavailable.
2840                 result.accept(ImsFeature.STATE_UNAVAILABLE);
2841             }
2842         });
2843     }
2844 
2845     /**
2846      * @return An Executor that should be used to execute potentially long-running operations.
2847      */
getImsThreadExecutor()2848     private Executor getImsThreadExecutor() {
2849         return mExecutor;
2850     }
2851 
2852     /**
2853      * Get the boolean config from carrier config manager.
2854      *
2855      * @param key config key defined in CarrierConfigManager
2856      * @return boolean value of corresponding key.
2857      */
getBooleanCarrierConfig(String key)2858     private boolean getBooleanCarrierConfig(String key) {
2859         PersistableBundle b = null;
2860         if (mConfigManager != null) {
2861             // If an invalid subId is used, this bundle will contain default values.
2862             b = mConfigManager.getConfigForSubId(getSubId());
2863         }
2864         if (b != null) {
2865             return b.getBoolean(key);
2866         } else {
2867             // Return static default defined in CarrierConfigManager.
2868             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
2869         }
2870     }
2871 
2872     /**
2873      * Get the int config from carrier config manager.
2874      *
2875      * @param key config key defined in CarrierConfigManager
2876      * @return integer value of corresponding key.
2877      */
getIntCarrierConfig(String key)2878     private int getIntCarrierConfig(String key) {
2879         PersistableBundle b = null;
2880         if (mConfigManager != null) {
2881             // If an invalid subId is used, this bundle will contain default values.
2882             b = mConfigManager.getConfigForSubId(getSubId());
2883         }
2884         if (b != null) {
2885             return b.getInt(key);
2886         } else {
2887             // Return static default defined in CarrierConfigManager.
2888             return CarrierConfigManager.getDefaultConfig().getInt(key);
2889         }
2890     }
2891 
2892     /**
2893      * Get the int[] config from carrier config manager.
2894      *
2895      * @param key config key defined in CarrierConfigManager
2896      * @return int[] values of the corresponding key.
2897      */
getIntArrayCarrierConfig(String key)2898     private int[] getIntArrayCarrierConfig(String key) {
2899         PersistableBundle b = null;
2900         if (mConfigManager != null) {
2901             // If an invalid subId is used, this bundle will contain default values.
2902             b = mConfigManager.getConfigForSubId(getSubId());
2903         }
2904         if (b != null) {
2905             return b.getIntArray(key);
2906         } else {
2907             // Return static default defined in CarrierConfigManager.
2908             return CarrierConfigManager.getDefaultConfig().getIntArray(key);
2909         }
2910     }
2911 
2912     /**
2913      * Checks to see if the ImsService Binder is connected. If it is not, we try to create the
2914      * connection again.
2915      */
getOrThrowExceptionIfServiceUnavailable()2916     private MmTelFeatureConnection getOrThrowExceptionIfServiceUnavailable()
2917             throws ImsException {
2918         if (!isImsSupportedOnDevice(mContext)) {
2919             throw new ImsException("IMS not supported on device.",
2920                     ImsReasonInfo.CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE);
2921         }
2922         MmTelFeatureConnection c = mMmTelConnectionRef.get();
2923         if (c == null || !c.isBinderAlive()) {
2924             throw new ImsException("Service is unavailable",
2925                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2926         }
2927         if (getSubId() != c.getSubId()) {
2928             logi("Trying to get MmTelFeature when it is still setting up, curr subId=" + getSubId()
2929                     + ", target subId=" + c.getSubId());
2930             throw new ImsException("Service is still initializing",
2931                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2932         }
2933         return c;
2934     }
2935 
2936     @Override
registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb)2937     public void registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb) {
2938         try {
2939             ITelephony telephony = mBinderCache.listenOnBinder(cb, () -> {
2940                 try {
2941                     cb.imsFeatureRemoved(
2942                             FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE);
2943                 } catch (RemoteException ignore) {} // This is local.
2944             });
2945 
2946             if (telephony != null) {
2947                 telephony.registerMmTelFeatureCallback(slotId, cb);
2948             } else {
2949                 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE);
2950             }
2951         } catch (ServiceSpecificException e) {
2952             try {
2953                 switch (e.errorCode) {
2954                     case android.telephony.ims.ImsException.CODE_ERROR_UNSUPPORTED_OPERATION:
2955                         cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED);
2956                         break;
2957                     default: {
2958                         cb.imsFeatureRemoved(
2959                                 FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE);
2960                     }
2961                 }
2962             } catch (RemoteException ignore) {} // Already dead anyway if this happens.
2963         } catch (RemoteException e) {
2964             try {
2965                 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE);
2966             } catch (RemoteException ignore) {} // Already dead if this happens.
2967         }
2968     }
2969 
2970     @Override
unregisterFeatureCallback(IImsServiceFeatureCallback cb)2971     public void unregisterFeatureCallback(IImsServiceFeatureCallback cb) {
2972         try {
2973             ITelephony telephony = mBinderCache.removeRunnable(cb);
2974             if (telephony != null) {
2975                 telephony.unregisterImsFeatureCallback(cb);
2976             }
2977         } catch (RemoteException e) {
2978             // This means that telephony died, so do not worry about it.
2979             loge("unregisterImsFeatureCallback (MMTEL), RemoteException: " + e.getMessage());
2980         }
2981     }
2982 
2983     @Override
associate(ImsFeatureContainer c, int subId)2984     public void associate(ImsFeatureContainer c, int subId) {
2985         if (c == null) {
2986             mMmTelConnectionRef.set(mMmTelFeatureConnectionFactory.create(
2987                     mContext, mPhoneId, subId, null, null, null, null));
2988         } else {
2989             mMmTelConnectionRef.set(mMmTelFeatureConnectionFactory.create(
2990                     mContext, mPhoneId, subId, IImsMmTelFeature.Stub.asInterface(c.imsFeature),
2991                     c.imsConfig, c.imsRegistration, c.sipTransport));
2992         }
2993     }
2994 
2995     @Override
invalidate()2996     public void invalidate() {
2997         mMmTelConnectionRef.get().onRemovedOrDied();
2998     }
2999 
getITelephony()3000     private ITelephony getITelephony() {
3001         return mBinderCache.getBinder();
3002     }
3003 
getITelephonyInterface()3004     private static ITelephony getITelephonyInterface() {
3005         return ITelephony.Stub.asInterface(
3006                 TelephonyFrameworkInitializer
3007                         .getTelephonyServiceManager()
3008                         .getTelephonyServiceRegisterer()
3009                         .get());
3010     }
3011 
3012     /**
3013      * Creates a {@link ImsCallSession} with the specified call profile.
3014      * Use other methods, if applicable, instead of interacting with
3015      * {@link ImsCallSession} directly.
3016      *
3017      * @param profile a call profile to make the call
3018      */
createCallSession(ImsCallProfile profile)3019     private ImsCallSession createCallSession(ImsCallProfile profile) throws ImsException {
3020         try {
3021             MmTelFeatureConnection c = mMmTelConnectionRef.get();
3022             // Throws an exception if the ImsService Feature is not ready to accept commands.
3023             return new ImsCallSession(c.createCallSession(profile));
3024         } catch (RemoteException e) {
3025             logw("CreateCallSession: Error, remote exception: " + e.getMessage());
3026             throw new ImsException("createCallSession()", e,
3027                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3028 
3029         }
3030     }
3031 
log(String s)3032     private void log(String s) {
3033         Rlog.d(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s);
3034     }
3035 
logi(String s)3036     private void logi(String s) {
3037         Rlog.i(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s);
3038     }
3039 
logw(String s)3040     private void logw(String s) {
3041         Rlog.w(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s);
3042     }
3043 
loge(String s)3044     private void loge(String s) {
3045         Rlog.e(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s);
3046     }
3047 
loge(String s, Throwable t)3048     private void loge(String s, Throwable t) {
3049         Rlog.e(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s, t);
3050     }
3051 
3052     /**
3053      * Used for turning on IMS.if its off already
3054      */
turnOnIms()3055     private void turnOnIms() throws ImsException {
3056         TelephonyManager tm = (TelephonyManager)
3057                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
3058         tm.enableIms(mPhoneId);
3059     }
3060 
isImsTurnOffAllowed()3061     private boolean isImsTurnOffAllowed() {
3062         return isTurnOffImsAllowedByPlatform()
3063                 && (!isWfcEnabledByPlatform()
3064                 || !isWfcEnabledByUser());
3065     }
3066 
3067     /**
3068      * Used for turning off IMS completely in order to make the device CSFB'ed.
3069      * Once turned off, all calls will be over CS.
3070      */
turnOffIms()3071     private void turnOffIms() throws ImsException {
3072         TelephonyManager tm = (TelephonyManager)
3073                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
3074         tm.disableIms(mPhoneId);
3075     }
3076 
3077     /**
3078      * Gets the ECBM interface to request ECBM exit.
3079      * <p>
3080      * This should only be called after {@link #open} has been called.
3081      *
3082      * @return the ECBM interface instance
3083      * @throws ImsException if getting the ECBM interface results in an error
3084      */
getEcbmInterface()3085     public ImsEcbm getEcbmInterface() throws ImsException {
3086         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
3087         ImsEcbm iEcbm = c.getEcbmInterface();
3088 
3089         if (iEcbm == null) {
3090             throw new ImsException("getEcbmInterface()",
3091                     ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
3092         }
3093         return iEcbm;
3094     }
3095 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)3096     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
3097             byte[] pdu) throws ImsException {
3098         try {
3099             mMmTelConnectionRef.get().sendSms(token, messageRef, format, smsc, isRetry, pdu);
3100         } catch (RemoteException e) {
3101             throw new ImsException("sendSms()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3102         }
3103     }
3104 
onMemoryAvailable(int token)3105     public void onMemoryAvailable(int token) throws ImsException {
3106         try {
3107             mMmTelConnectionRef.get().onMemoryAvailable(token);
3108         } catch (RemoteException e) {
3109             throw new ImsException("onMemoryAvailable()", e,
3110                 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3111         }
3112     }
3113 
acknowledgeSms(int token, int messageRef, int result)3114     public void acknowledgeSms(int token, int messageRef, int result) throws ImsException {
3115         try {
3116             mMmTelConnectionRef.get().acknowledgeSms(token, messageRef, result);
3117         } catch (RemoteException e) {
3118             throw new ImsException("acknowledgeSms()", e,
3119                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3120         }
3121     }
3122 
acknowledgeSms(int token, int messageRef, int result, byte[] pdu)3123     public void acknowledgeSms(int token, int messageRef, int result, byte[] pdu) throws ImsException {
3124         try {
3125             mMmTelConnectionRef.get().acknowledgeSms(token, messageRef, result, pdu);
3126         } catch (RemoteException e) {
3127             throw new ImsException("acknowledgeSms()", e,
3128                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3129         }
3130     }
3131 
acknowledgeSmsReport(int token, int messageRef, int result)3132     public void acknowledgeSmsReport(int token, int messageRef, int result) throws  ImsException{
3133         try {
3134             mMmTelConnectionRef.get().acknowledgeSmsReport(token, messageRef, result);
3135         } catch (RemoteException e) {
3136             throw new ImsException("acknowledgeSmsReport()", e,
3137                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3138         }
3139     }
3140 
getSmsFormat()3141     public String getSmsFormat() throws ImsException{
3142         try {
3143             return mMmTelConnectionRef.get().getSmsFormat();
3144         } catch (RemoteException e) {
3145             throw new ImsException("getSmsFormat()", e,
3146                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3147         }
3148     }
3149 
setSmsListener(IImsSmsListener listener)3150     public void setSmsListener(IImsSmsListener listener) throws ImsException {
3151         try {
3152             mMmTelConnectionRef.get().setSmsListener(listener);
3153         } catch (RemoteException e) {
3154             throw new ImsException("setSmsListener()", e,
3155                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3156         }
3157     }
3158 
onSmsReady()3159     public void onSmsReady() throws ImsException {
3160         try {
3161             mMmTelConnectionRef.get().onSmsReady();
3162         } catch (RemoteException e) {
3163             throw new ImsException("onSmsReady()", e,
3164                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3165         }
3166     }
3167 
3168     /**
3169      * Determines whether or not a call with the specified numbers should be placed over IMS or over
3170      * CSFB.
3171      * @param isEmergency is at least one call an emergency number.
3172      * @param numbers A {@link String} array containing the numbers in the call being placed. Can
3173      *         be multiple numbers in the case of dialing out a conference.
3174      * @return The result of the query, one of the following values:
3175      *         - {@link MmTelFeature#PROCESS_CALL_IMS}
3176      *         - {@link MmTelFeature#PROCESS_CALL_CSFB}
3177      * @throws ImsException if the ImsService is not available. In this case, we should fall back
3178      * to CSFB anyway.
3179      */
shouldProcessCall(boolean isEmergency, String[] numbers)3180     public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
3181             String[] numbers) throws ImsException {
3182         try {
3183             MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
3184             return c.shouldProcessCall(isEmergency, numbers);
3185         } catch (RemoteException e) {
3186             throw new ImsException("shouldProcessCall()", e,
3187                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3188         }
3189     }
3190 
3191     /**
3192      * Resets ImsManager settings back to factory defaults.
3193      *
3194      * @deprecated Doesn't support MSIM devices. Use {@link #factoryReset()} instead.
3195      *
3196      * @hide
3197      */
factoryReset(Context context)3198     public static void factoryReset(Context context) {
3199         DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context);
3200         ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId());
3201         if (mgr != null) {
3202             mgr.factoryReset();
3203         }
3204         Rlog.e(TAG, "factoryReset: ImsManager null.");
3205     }
3206 
3207     /**
3208      * Resets ImsManager settings back to factory defaults.
3209      *
3210      * @hide
3211      */
factoryReset()3212     public void factoryReset() {
3213         int subId = getSubId();
3214         if (!isSubIdValid(subId)) {
3215             loge("factoryReset: invalid sub id, can not reset siminfo db settings; subId="
3216                     + subId);
3217             return;
3218         }
3219         // Set VoLTE to default
3220         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3221                 SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
3222                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3223 
3224         // Set VoWiFi to default
3225         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3226                 SubscriptionManager.WFC_IMS_ENABLED,
3227                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3228 
3229         // Set VoWiFi mode to default
3230         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3231                 SubscriptionManager.WFC_IMS_MODE,
3232                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3233 
3234         // Set VoWiFi roaming to default
3235         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3236                 SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
3237                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3238 
3239         // Set VoWiFi roaming mode to default
3240         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3241                 SubscriptionManager.WFC_IMS_ROAMING_MODE,
3242                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3243 
3244 
3245         // Set VT to default
3246         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3247                 SubscriptionManager.VT_IMS_ENABLED,
3248                 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
3249 
3250         // Set RCS UCE to default
3251         mSubscriptionManagerProxy.setSubscriptionProperty(subId,
3252                 SubscriptionManager.IMS_RCS_UCE_ENABLED, Integer.toString(
3253                         SUBINFO_PROPERTY_FALSE));
3254         // Push settings
3255         try {
3256             reevaluateCapabilities();
3257         } catch (ImsException e) {
3258             loge("factoryReset, exception: " + e);
3259         }
3260     }
3261 
isDataEnabled()3262     private boolean isDataEnabled() {
3263         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
3264         if (tm == null) {
3265             loge("isDataEnabled: TelephonyManager not available, returning false...");
3266             return false;
3267         }
3268         tm = tm.createForSubscriptionId(getSubId());
3269         return tm.isDataConnectionAllowed();
3270     }
3271 
isVolteProvisioned()3272     private boolean isVolteProvisioned() {
3273         return getImsProvisionedBoolNoException(CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_LTE);
3274     }
3275 
isEabProvisioned()3276     private boolean isEabProvisioned() {
3277         return getRcsProvisionedBoolNoException(CAPABILITY_TYPE_PRESENCE_UCE,
3278                 REGISTRATION_TECH_LTE);
3279     }
3280 
isWfcProvisioned()3281     private boolean isWfcProvisioned() {
3282         return getImsProvisionedBoolNoException(CAPABILITY_TYPE_VOICE, REGISTRATION_TECH_IWLAN);
3283     }
3284 
isVtProvisioned()3285     private boolean isVtProvisioned() {
3286         return getImsProvisionedBoolNoException(CAPABILITY_TYPE_VIDEO, REGISTRATION_TECH_LTE);
3287     }
3288 
isMmTelProvisioningRequired(int capability, int tech)3289     private boolean isMmTelProvisioningRequired(int capability, int tech) {
3290         int subId = getSubId();
3291         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
3292             logw("isMmTelProvisioningRequired subId is invalid");
3293             return false;
3294         }
3295 
3296         ITelephony iTelephony = getITelephony();
3297         if (iTelephony == null) {
3298             logw("isMmTelProvisioningRequired ITelephony interface is invalid");
3299             return false;
3300         }
3301 
3302         boolean required = false;
3303         try {
3304             required = iTelephony.isProvisioningRequiredForCapability(subId, capability,
3305                     tech);
3306         } catch (RemoteException | IllegalArgumentException e) {
3307             logw("isMmTelProvisioningRequired : operation failed" + " capability=" + capability
3308                     + " tech=" + tech + ". Exception:" + e.getMessage());
3309         }
3310 
3311         log("MmTel Provisioning required " + required + " for capability " + capability);
3312         return required;
3313     }
3314 
isRcsProvisioningRequired(int capability, int tech)3315     private boolean isRcsProvisioningRequired(int capability, int tech) {
3316         int subId = getSubId();
3317         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
3318             logw("isRcsProvisioningRequired subId is invalid");
3319             return false;
3320         }
3321 
3322         ITelephony iTelephony = getITelephony();
3323         if (iTelephony == null) {
3324             logw("isRcsProvisioningRequired ITelephony interface is invalid");
3325             return false;
3326         }
3327 
3328         boolean required = false;
3329         try {
3330             required = iTelephony.isRcsProvisioningRequiredForCapability(subId, capability,
3331                     tech);
3332         } catch (RemoteException | IllegalArgumentException e) {
3333             logw("isRcsProvisioningRequired : operation failed" + " capability=" + capability
3334                     + " tech=" + tech + ". Exception:" + e.getMessage());
3335         }
3336 
3337         log("Rcs Provisioning required " + required + " for capability " + capability);
3338         return required;
3339     }
3340 
booleanToPropertyString(boolean bool)3341     private static String booleanToPropertyString(boolean bool) {
3342         return bool ? "1" : "0";
3343     }
3344 
getConfigInt(int key)3345     public int getConfigInt(int key) throws ImsException {
3346         if (isLocalImsConfigKey(key)) {
3347             return getLocalImsConfigKeyInt(key);
3348         } else {
3349             return getConfigInterface().getConfigInt(key);
3350         }
3351     }
3352 
getConfigString(int key)3353     public String getConfigString(int key) throws ImsException {
3354         if (isLocalImsConfigKey(key)) {
3355             return getLocalImsConfigKeyString(key);
3356         } else {
3357             return getConfigInterface().getConfigString(key);
3358         }
3359     }
3360 
setConfig(int key, int value)3361     public int setConfig(int key, int value) throws ImsException, RemoteException {
3362         if (isLocalImsConfigKey(key)) {
3363             return setLocalImsConfigKeyInt(key, value);
3364         } else {
3365             return getConfigInterface().setConfig(key, value);
3366         }
3367     }
3368 
setConfig(int key, String value)3369     public int setConfig(int key, String value) throws ImsException, RemoteException {
3370         if (isLocalImsConfigKey(key)) {
3371             return setLocalImsConfigKeyString(key, value);
3372         } else {
3373             return getConfigInterface().setConfig(key, value);
3374         }
3375     }
3376 
3377     /**
3378      * Gets the configuration value that supported in frameworks.
3379      *
3380      * @param key, as defined in com.android.ims.ProvisioningManager.
3381      * @return the value in Integer format
3382      */
getLocalImsConfigKeyInt(int key)3383     private int getLocalImsConfigKeyInt(int key) {
3384         int result = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN;
3385 
3386         switch (key) {
3387             case KEY_VOIMS_OPT_IN_STATUS:
3388                 result = isVoImsOptInEnabled() ? 1 : 0;
3389                 break;
3390         }
3391         log("getLocalImsConfigKeInt() for key:" + key + ", result: " + result);
3392         return result;
3393     }
3394 
3395     /**
3396      * Gets the configuration value that supported in frameworks.
3397      *
3398      * @param key, as defined in com.android.ims.ProvisioningManager.
3399      * @return the value in String format
3400      */
getLocalImsConfigKeyString(int key)3401     private String getLocalImsConfigKeyString(int key) {
3402         String result = "";
3403 
3404         switch (key) {
3405             case KEY_VOIMS_OPT_IN_STATUS:
3406                 result = booleanToPropertyString(isVoImsOptInEnabled());
3407 
3408                 break;
3409         }
3410         log("getLocalImsConfigKeyString() for key:" + key + ", result: " + result);
3411         return result;
3412     }
3413 
3414     /**
3415      * Sets the configuration value that supported in frameworks.
3416      *
3417      * @param key, as defined in com.android.ims.ProvisioningManager.
3418      * @param value in Integer format.
3419      * @return as defined in com.android.ims.ProvisioningManager#OperationStatusConstants
3420      */
setLocalImsConfigKeyInt(int key, int value)3421     private int setLocalImsConfigKeyInt(int key, int value) throws ImsException, RemoteException {
3422         int result = ImsConfig.OperationStatusConstants.UNKNOWN;
3423 
3424         switch (key) {
3425             case KEY_VOIMS_OPT_IN_STATUS:
3426                 result = setVoImsOptInSetting(value);
3427                 reevaluateCapabilities();
3428                 break;
3429         }
3430         log("setLocalImsConfigKeyInt() for" +
3431                 " key: " + key +
3432                 ", value: " + value +
3433                 ", result: " + result);
3434 
3435         // Notify ims config changed
3436         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
3437         IImsConfig config = c.getConfig();
3438         config.notifyIntImsConfigChanged(key, value);
3439 
3440         return result;
3441     }
3442 
3443     /**
3444      * Sets the configuration value that supported in frameworks.
3445      *
3446      * @param key, as defined in com.android.ims.ProvisioningManager.
3447      * @param value in String format.
3448      * @return as defined in com.android.ims.ProvisioningManager#OperationStatusConstants
3449      */
setLocalImsConfigKeyString(int key, String value)3450     private int setLocalImsConfigKeyString(int key, String value)
3451             throws ImsException, RemoteException {
3452         int result = ImsConfig.OperationStatusConstants.UNKNOWN;
3453 
3454         switch (key) {
3455             case KEY_VOIMS_OPT_IN_STATUS:
3456                 result = setVoImsOptInSetting(Integer.parseInt(value));
3457                 reevaluateCapabilities();
3458                 break;
3459         }
3460         log("setLocalImsConfigKeyString() for" +
3461                 " key: " + key +
3462                 ", value: " + value +
3463                 ", result: " + result);
3464 
3465         // Notify ims config changed
3466         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
3467         IImsConfig config = c.getConfig();
3468         config.notifyStringImsConfigChanged(key, value);
3469 
3470         return result;
3471     }
3472 
3473     /**
3474      * Check the config whether supported by framework.
3475      *
3476      * @param key, as defined in com.android.ims.ProvisioningManager.
3477      * @return true if the config is supported by framework.
3478      */
isLocalImsConfigKey(int key)3479     private boolean isLocalImsConfigKey(int key) {
3480         return Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(value -> value == key);
3481     }
3482 
isVoImsOptInEnabled()3483     private boolean isVoImsOptInEnabled() {
3484         int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(
3485                 getSubId(), SubscriptionManager.VOIMS_OPT_IN_STATUS,
3486                 SUB_PROPERTY_NOT_INITIALIZED);
3487         return (setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
3488     }
3489 
setVoImsOptInSetting(int value)3490     private int setVoImsOptInSetting(int value) {
3491         mSubscriptionManagerProxy.setSubscriptionProperty(
3492                 getSubId(),
3493                 SubscriptionManager.VOIMS_OPT_IN_STATUS,
3494                 String.valueOf(value));
3495         return ImsConfig.OperationStatusConstants.SUCCESS;
3496     }
3497 
dump(FileDescriptor fd, PrintWriter pw, String[] args)3498     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3499         pw.println("ImsManager:");
3500         pw.println("  device supports IMS = " + isImsSupportedOnDevice(mContext));
3501         pw.println("  mPhoneId = " + mPhoneId);
3502         pw.println("  mConfigUpdated = " + mConfigUpdated);
3503         pw.println("  mImsServiceProxy = " + mMmTelConnectionRef.get());
3504         pw.println("  mDataEnabled = " + isDataEnabled());
3505         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(
3506                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
3507 
3508         pw.println("  isGbaValid = " + isGbaValid());
3509         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
3510         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled());
3511 
3512         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatform());
3513         pw.println("  isVoImsOptInEnabled = " + isVoImsOptInEnabled());
3514         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice());
3515         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
3516                 isEnhanced4gLteModeSettingEnabledByUser());
3517         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatform());
3518         pw.println("  isVtEnabledByUser = " + isVtEnabledByUser());
3519 
3520         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatform());
3521         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUser());
3522         pw.println("  getWfcMode(non-roaming) = " + getWfcMode(false));
3523         pw.println("  getWfcMode(roaming) = " + getWfcMode(true));
3524         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser());
3525 
3526         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDevice());
3527         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice());
3528 
3529         pw.println("  isCrossSimEnabledByPlatform = " + isCrossSimEnabledByPlatform());
3530         pw.println("  isCrossSimCallingEnabledByUser = " + isCrossSimCallingEnabledByUser());
3531         pw.println("  isImsOverNrEnabledByPlatform = " + isImsOverNrEnabledByPlatform());
3532         pw.flush();
3533     }
3534 
3535     /**
3536      * Determines if a sub id is valid.
3537      * Mimics the logic in SubscriptionController.validateSubId.
3538      * @param subId The sub id to check.
3539      * @return {@code true} if valid, {@code false} otherwise.
3540      */
isSubIdValid(int subId)3541     private boolean isSubIdValid(int subId) {
3542         return mSubscriptionManagerProxy.isValidSubscriptionId(subId) &&
3543                 subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
3544     }
3545 
isActiveSubscriptionPresent()3546     private boolean isActiveSubscriptionPresent() {
3547         return mSubscriptionManagerProxy.getActiveSubscriptionIdList().length > 0;
3548     }
3549 
updateImsCarrierConfigs(PersistableBundle configs)3550     private void updateImsCarrierConfigs(PersistableBundle configs) throws ImsException {
3551         MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable();
3552 
3553         IImsConfig config = c.getConfig();
3554         if (config == null) {
3555             throw new ImsException("getConfigInterface()",
3556                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
3557         }
3558         try {
3559             config.updateImsCarrierConfigs(configs);
3560         } catch (RemoteException e) {
3561             throw new ImsException("updateImsCarrierConfigs()", e,
3562                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
3563         }
3564     }
3565 }
3566