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