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