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