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