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 android.annotation.Nullable;
20 import android.app.PendingIntent;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.Parcel;
29 import android.os.PersistableBundle;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.os.SystemProperties;
33 import android.provider.Settings;
34 import android.telecom.TelecomManager;
35 import android.telephony.AccessNetworkConstants;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.SubscriptionManager;
38 import android.telephony.TelephonyManager;
39 import android.telephony.ims.ImsCallProfile;
40 import android.telephony.ims.ImsCallSession;
41 import android.telephony.ims.ImsMmTelManager;
42 import android.telephony.ims.ImsReasonInfo;
43 import android.telephony.ims.ImsService;
44 import android.telephony.ims.ProvisioningManager;
45 import android.telephony.ims.RegistrationManager;
46 import android.telephony.ims.aidl.IImsCapabilityCallback;
47 import android.telephony.ims.aidl.IImsConfig;
48 import android.telephony.ims.aidl.IImsConfigCallback;
49 import android.telephony.ims.aidl.IImsRegistrationCallback;
50 import android.telephony.ims.aidl.IImsSmsListener;
51 import android.telephony.ims.feature.CapabilityChangeRequest;
52 import android.telephony.ims.feature.ImsFeature;
53 import android.telephony.ims.feature.MmTelFeature;
54 import android.telephony.ims.stub.ImsCallSessionImplBase;
55 import android.telephony.ims.stub.ImsConfigImplBase;
56 import android.telephony.ims.stub.ImsRegistrationImplBase;
57 
58 import com.android.ims.internal.IImsCallSession;
59 import com.android.ims.internal.IImsEcbm;
60 import com.android.ims.internal.IImsMultiEndpoint;
61 import com.android.ims.internal.IImsUt;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.telephony.ITelephony;
64 import com.android.internal.telephony.util.HandlerExecutor;
65 import com.android.telephony.Rlog;
66 
67 import java.io.FileDescriptor;
68 import java.io.PrintWriter;
69 import java.util.ArrayList;
70 import java.util.HashMap;
71 import java.util.Set;
72 import java.util.concurrent.BlockingQueue;
73 import java.util.concurrent.ConcurrentLinkedDeque;
74 import java.util.concurrent.CopyOnWriteArraySet;
75 import java.util.concurrent.Executor;
76 import java.util.concurrent.LinkedBlockingDeque;
77 import java.util.concurrent.TimeUnit;
78 import java.util.function.Consumer;
79 
80 /**
81  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
82  * the operator's IMS network. This class is the starting point for any IMS actions.
83  * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
84  * For internal use ONLY! Use {@link ImsMmTelManager} instead.
85  * @hide
86  */
87 public class ImsManager implements IFeatureConnector {
88 
89     /*
90      * Debug flag to override configuration flag
91      */
92     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
93     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
94     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
95     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
96     public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
97     public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
98     public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
99     public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
100 
101     /**
102      * The result code to be sent back with the incoming call {@link PendingIntent}.
103      * @see #open(MmTelFeature.Listener)
104      */
105     public static final int INCOMING_CALL_RESULT_CODE = 101;
106 
107     /**
108      * Key to retrieve the call ID from an incoming call intent. No longer used, see
109      * {@link ImsCallSessionImplBase#getCallId()}.
110      * @deprecated Not used in the framework, keeping around symbol to not break old vendor
111      * components.
112      */
113     @Deprecated
114     public static final String EXTRA_CALL_ID = "android:imsCallID";
115 
116     /**
117      * Action to broadcast when ImsService is up.
118      * Internal use only.
119      * @deprecated
120      * @hide
121      */
122     public static final String ACTION_IMS_SERVICE_UP =
123             "com.android.ims.IMS_SERVICE_UP";
124 
125     /**
126      * Action to broadcast when ImsService is down.
127      * Internal use only.
128      * @deprecated
129      * @hide
130      */
131     public static final String ACTION_IMS_SERVICE_DOWN =
132             "com.android.ims.IMS_SERVICE_DOWN";
133 
134     /**
135      * Action to broadcast when ImsService registration fails.
136      * Internal use only.
137      * @hide
138      * @deprecated use {@link android.telephony.ims.ImsManager#ACTION_WFC_IMS_REGISTRATION_ERROR}
139      * instead.
140      */
141     @Deprecated
142     public static final String ACTION_IMS_REGISTRATION_ERROR =
143             android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR;
144 
145     /**
146      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
147      * A long value; the phone ID corresponding to the IMS service coming up or down.
148      * Internal use only.
149      * @hide
150      */
151     public static final String EXTRA_PHONE_ID = "android:phone_id";
152 
153     /**
154      * Action for the incoming call intent for the Phone app.
155      * Internal use only.
156      * @hide
157      */
158     public static final String ACTION_IMS_INCOMING_CALL =
159             "com.android.ims.IMS_INCOMING_CALL";
160 
161     /**
162      * Part of the ACTION_IMS_INCOMING_CALL intents.
163      * An integer value; service identifier obtained from {@link ImsManager#open}.
164      * Internal use only.
165      * @hide
166      * @deprecated Not used in the system, keeping around to not break old vendor components.
167      */
168     @Deprecated
169     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
170 
171     /**
172      * Part of the ACTION_IMS_INCOMING_CALL intents.
173      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
174      * The value "true" indicates that the incoming call is for USSD.
175      * Internal use only.
176      * @deprecated Keeping around to not break old vendor components. Use
177      * {@link MmTelFeature#EXTRA_USSD} instead.
178      * @hide
179      */
180     public static final String EXTRA_USSD = "android:ussd";
181 
182     /**
183      * Part of the ACTION_IMS_INCOMING_CALL intents.
184      * A boolean value; Flag to indicate whether the call is an unknown
185      * dialing call. Such calls are originated by sending commands (like
186      * AT commands) directly to modem without Android involvement.
187      * Even though they are not incoming calls, they are propagated
188      * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
189      * Internal use only.
190      * @deprecated Keeping around to not break old vendor components. Use
191      * {@link MmTelFeature#EXTRA_IS_UNKNOWN_CALL} instead.
192      * @hide
193      */
194     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
195 
196     private static final int SUBINFO_PROPERTY_FALSE = 0;
197 
198     private static final int SYSTEM_PROPERTY_NOT_SET = -1;
199 
200     // -1 indicates a subscriptionProperty value that is never set.
201     private static final int SUB_PROPERTY_NOT_INITIALIZED = -1;
202 
203     private static final String TAG = "ImsManager";
204     private static final boolean DBG = true;
205 
206     private static final int RESPONSE_WAIT_TIME_MS = 3000;
207 
208     @VisibleForTesting
209     public interface ExecutorFactory {
executeRunnable(Runnable runnable)210         void executeRunnable(Runnable runnable);
211     }
212 
213     @VisibleForTesting
214     public static class ImsExecutorFactory implements ExecutorFactory {
215 
216         private final HandlerThread mThreadHandler;
217         private final Handler mHandler;
218 
ImsExecutorFactory()219         public ImsExecutorFactory() {
220             mThreadHandler = new HandlerThread("ImsHandlerThread");
221             mThreadHandler.start();
222             mHandler = new Handler(mThreadHandler.getLooper());
223         }
224 
225         @Override
executeRunnable(Runnable runnable)226         public void executeRunnable(Runnable runnable) {
227             mHandler.post(runnable);
228         }
229 
destroy()230         public void destroy() {
231             mThreadHandler.quit();
232         }
233     }
234 
235     // Replaced with single-threaded executor for testing.
236     @VisibleForTesting
237     public ExecutorFactory mExecutorFactory = new ImsExecutorFactory();
238 
239     private static HashMap<Integer, ImsManager> sImsManagerInstances =
240             new HashMap<Integer, ImsManager>();
241 
242     private Context mContext;
243     private CarrierConfigManager mConfigManager;
244     private int mPhoneId;
245     private @Nullable MmTelFeatureConnection mMmTelFeatureConnection = null;
246     private boolean mConfigUpdated = false;
247 
248     private ImsConfigListener mImsConfigListener;
249 
250     //TODO: Move these caches into the MmTelFeature Connection and restrict their lifetimes to the
251     // lifetime of the MmTelFeature.
252     // Ut interface for the supplementary service configuration
253     private ImsUt mUt = null;
254     // ECBM interface
255     private ImsEcbm mEcbm = null;
256     private ImsMultiEndpoint mMultiEndpoint = null;
257 
258     private Set<FeatureConnection.IFeatureUpdate> mStatusCallbacks = new CopyOnWriteArraySet<>();
259 
260     public static final String TRUE = "true";
261     public static final String FALSE = "false";
262 
263     // mRecentDisconnectReasons stores the last 16 disconnect reasons
264     private static final int MAX_RECENT_DISCONNECT_REASONS = 16;
265     private ConcurrentLinkedDeque<ImsReasonInfo> mRecentDisconnectReasons =
266             new ConcurrentLinkedDeque<>();
267 
268     /**
269      * Gets a manager instance.
270      *
271      * @param context application context for creating the manager object
272      * @param phoneId the phone ID for the IMS Service
273      * @return the manager instance corresponding to the phoneId
274      */
275     @UnsupportedAppUsage
getInstance(Context context, int phoneId)276     public static ImsManager getInstance(Context context, int phoneId) {
277         synchronized (sImsManagerInstances) {
278             if (sImsManagerInstances.containsKey(phoneId)) {
279                 ImsManager m = sImsManagerInstances.get(phoneId);
280                 // May be null for some tests
281                 if (m != null) {
282                     m.connectIfServiceIsAvailable();
283                 }
284                 return m;
285             }
286 
287             ImsManager mgr = new ImsManager(context, phoneId);
288             sImsManagerInstances.put(phoneId, mgr);
289 
290             return mgr;
291         }
292     }
293 
isImsSupportedOnDevice(Context context)294     public static boolean isImsSupportedOnDevice(Context context) {
295         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
296     }
297 
298     /**
299      * Returns the user configuration of Enhanced 4G LTE Mode setting.
300      *
301      * @deprecated Doesn't support MSIM devices. Use
302      * {@link #isEnhanced4gLteModeSettingEnabledByUser()} instead.
303      */
304     @UnsupportedAppUsage
isEnhanced4gLteModeSettingEnabledByUser(Context context)305     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
306         ImsManager mgr = ImsManager.getInstance(context,
307                 SubscriptionManager.getDefaultVoicePhoneId());
308         if (mgr != null) {
309             return mgr.isEnhanced4gLteModeSettingEnabledByUser();
310         }
311         Rlog.e(TAG, "isEnhanced4gLteModeSettingEnabledByUser: ImsManager null, returning default"
312                 + " value.");
313         return false;
314     }
315 
316     /**
317      * Returns the user configuration of Enhanced 4G LTE Mode setting for slot. If the option is
318      * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false),
319      * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), or
320      * the setting is not initialized, this method will return default value specified by
321      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
322      *
323      * Note that even if the setting was set, it may no longer be editable. If this is the case we
324      * return the default value.
325      */
isEnhanced4gLteModeSettingEnabledByUser()326     public boolean isEnhanced4gLteModeSettingEnabledByUser() {
327         int setting = SubscriptionManager.getIntegerSubscriptionProperty(
328                 getSubId(), SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
329                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
330         boolean onByDefault = getBooleanCarrierConfig(
331                 CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL);
332 
333         // If Enhanced 4G LTE Mode is uneditable, hidden or not initialized, we use the default
334         // value
335         if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)
336                 || getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)
337                 || setting == SUB_PROPERTY_NOT_INITIALIZED) {
338             return onByDefault;
339         } else {
340             return (setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
341         }
342     }
343 
344     /**
345      * Change persistent Enhanced 4G LTE Mode setting.
346      *
347      * @deprecated Doesn't support MSIM devices. Use {@link #setEnhanced4gLteModeSetting(boolean)}
348      * instead.
349      */
setEnhanced4gLteModeSetting(Context context, boolean enabled)350     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
351         ImsManager mgr = ImsManager.getInstance(context,
352                 SubscriptionManager.getDefaultVoicePhoneId());
353         if (mgr != null) {
354             mgr.setEnhanced4gLteModeSetting(enabled);
355         }
356         Rlog.e(TAG, "setEnhanced4gLteModeSetting: ImsManager null, value not set.");
357     }
358 
359     /**
360      * Change persistent Enhanced 4G LTE Mode setting. If the option is not editable
361      * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false)
362      * or hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true),
363      * this method will set the setting to the default value specified by
364      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
365      */
setEnhanced4gLteModeSetting(boolean enabled)366     public void setEnhanced4gLteModeSetting(boolean enabled) {
367         if (enabled && !isVolteProvisionedOnDevice()) {
368             log("setEnhanced4gLteModeSetting: Not possible to enable VoLTE due to provisioning.");
369             return;
370         }
371         int subId = getSubId();
372         // If editable=false or hidden=true, we must keep default advanced 4G mode.
373         if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) ||
374                 getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) {
375             enabled = getBooleanCarrierConfig(
376                     CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL);
377         }
378 
379         int prevSetting = SubscriptionManager.getIntegerSubscriptionProperty(subId,
380                 SubscriptionManager.ENHANCED_4G_MODE_ENABLED, SUB_PROPERTY_NOT_INITIALIZED,
381                 mContext);
382 
383         if (prevSetting != (enabled ?
384                 ProvisioningManager.PROVISIONING_VALUE_ENABLED :
385                 ProvisioningManager.PROVISIONING_VALUE_DISABLED)) {
386             if (isSubIdValid(subId)) {
387                 SubscriptionManager.setSubscriptionProperty(subId,
388                         SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
389                         booleanToPropertyString(enabled));
390             } else {
391                 loge("setEnhanced4gLteModeSetting: invalid sub id, can not set property in " +
392                         " siminfo db; subId=" + subId);
393             }
394             if (isNonTtyOrTtyOnVolteEnabled()) {
395                 try {
396                     setAdvanced4GMode(enabled);
397                 } catch (ImsException ie) {
398                     // do nothing
399                 }
400             }
401         }
402     }
403 
404     /**
405      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
406      * supported.
407      * @deprecated Does not support MSIM devices. Please use
408      * {@link #isNonTtyOrTtyOnVolteEnabled()} instead.
409      */
410     @UnsupportedAppUsage
isNonTtyOrTtyOnVolteEnabled(Context context)411     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
412         ImsManager mgr = ImsManager.getInstance(context,
413                 SubscriptionManager.getDefaultVoicePhoneId());
414         if (mgr != null) {
415             return mgr.isNonTtyOrTtyOnVolteEnabled();
416         }
417         Rlog.e(TAG, "isNonTtyOrTtyOnVolteEnabled: ImsManager null, returning default value.");
418         return false;
419     }
420 
421     /**
422      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
423      * supported on a per slot basis.
424      */
isNonTtyOrTtyOnVolteEnabled()425     public boolean isNonTtyOrTtyOnVolteEnabled() {
426         if (isTtyOnVoLteCapable()) {
427             return true;
428         }
429 
430         TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
431         if (tm == null) {
432             logw("isNonTtyOrTtyOnVolteEnabled: telecom not available");
433             return true;
434         }
435         return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
436     }
437 
isTtyOnVoLteCapable()438     public boolean isTtyOnVoLteCapable() {
439         return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
440     }
441 
442     /**
443      * Returns a platform configuration for VoLTE which may override the user setting.
444      * @deprecated Does not support MSIM devices. Please use
445      * {@link #isVolteEnabledByPlatform()} instead.
446      */
447     @UnsupportedAppUsage
isVolteEnabledByPlatform(Context context)448     public static boolean isVolteEnabledByPlatform(Context context) {
449         ImsManager mgr = ImsManager.getInstance(context,
450                 SubscriptionManager.getDefaultVoicePhoneId());
451         if (mgr != null) {
452             return mgr.isVolteEnabledByPlatform();
453         }
454         Rlog.e(TAG, "isVolteEnabledByPlatform: ImsManager null, returning default value.");
455         return false;
456     }
457 
458     /**
459      * Asynchronous call to ImsService to determine whether or not a specific MmTel capability is
460      * supported.
461      */
isSupported(int capability, int transportType, Consumer<Boolean> result)462     public void isSupported(int capability, int transportType, Consumer<Boolean> result) {
463         mExecutorFactory.executeRunnable(() -> {
464             switch(transportType) {
465                 case (AccessNetworkConstants.TRANSPORT_TYPE_WWAN): {
466                     switch (capability) {
467                         case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE): {
468                             result.accept(isVolteEnabledByPlatform());
469                             return;
470                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO): {
471                             result.accept(isVtEnabledByPlatform());
472                             return;
473                         }case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT): {
474                             result.accept(isSuppServicesOverUtEnabledByPlatform());
475                             return;
476                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS): {
477                             // There is currently no carrier config defined for this.
478                             result.accept(true);
479                             return;
480                         }
481                     }
482                     break;
483                 } case (AccessNetworkConstants.TRANSPORT_TYPE_WLAN): {
484                     switch (capability) {
485                         case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE) : {
486                             result.accept(isWfcEnabledByPlatform());
487                             return;
488                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO) : {
489                             // This is not transport dependent at this time.
490                             result.accept(isVtEnabledByPlatform());
491                             return;
492                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT) : {
493                             // This is not transport dependent at this time.
494                             result.accept(isSuppServicesOverUtEnabledByPlatform());
495                             return;
496                         } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS) : {
497                             // There is currently no carrier config defined for this.
498                             result.accept(true);
499                             return;
500                         }
501                     }
502                     break;
503                 }
504             }
505             // false for unknown capability/transport types.
506             result.accept(false);
507         });
508 
509     }
510 
511     /**
512      * Returns a platform configuration for VoLTE which may override the user setting on a per Slot
513      * basis.
514      */
isVolteEnabledByPlatform()515     public boolean isVolteEnabledByPlatform() {
516         // We first read the per slot value. If doesn't exist, we read the general value. If still
517         // doesn't exist, we use the hardcoded default value.
518         if (SystemProperties.getInt(
519                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE + Integer.toString(mPhoneId),
520                 SYSTEM_PROPERTY_NOT_SET) == 1 ||
521                 SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
522                         SYSTEM_PROPERTY_NOT_SET) == 1) {
523             return true;
524         }
525 
526         return mContext.getResources().getBoolean(
527                 com.android.internal.R.bool.config_device_volte_available)
528                 && getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
529                 && isGbaValid();
530     }
531 
532     /**
533      * Indicates whether VoLTE is provisioned on device.
534      *
535      * @deprecated Does not support MSIM devices. Please use
536      * {@link #isVolteProvisionedOnDevice()} instead.
537      */
isVolteProvisionedOnDevice(Context context)538     public static boolean isVolteProvisionedOnDevice(Context context) {
539         ImsManager mgr = ImsManager.getInstance(context,
540                 SubscriptionManager.getDefaultVoicePhoneId());
541         if (mgr != null) {
542             return mgr.isVolteProvisionedOnDevice();
543         }
544         Rlog.e(TAG, "isVolteProvisionedOnDevice: ImsManager null, returning default value.");
545         return true;
546     }
547 
548     /**
549      * Indicates whether VoLTE is provisioned on this slot.
550      */
isVolteProvisionedOnDevice()551     public boolean isVolteProvisionedOnDevice() {
552         if (getBooleanCarrierConfig(
553                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
554             return isVolteProvisioned();
555         }
556 
557         return true;
558     }
559 
560     /**
561      * Indicates whether EAB is provisioned on this slot.
562      */
isEabProvisionedOnDevice()563     public boolean isEabProvisionedOnDevice() {
564         if (getBooleanCarrierConfig(
565                 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL)) {
566             return isEabProvisioned();
567         }
568 
569         return true;
570     }
571 
572     /**
573      * Indicates whether VoWifi is provisioned on device.
574      *
575      * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
576      * provisioned on device, this method returns false.
577      *
578      * @deprecated Does not support MSIM devices. Please use
579      * {@link #isWfcProvisionedOnDevice()} instead.
580      */
isWfcProvisionedOnDevice(Context context)581     public static boolean isWfcProvisionedOnDevice(Context context) {
582         ImsManager mgr = ImsManager.getInstance(context,
583                 SubscriptionManager.getDefaultVoicePhoneId());
584         if (mgr != null) {
585             return mgr.isWfcProvisionedOnDevice();
586         }
587         Rlog.e(TAG, "isWfcProvisionedOnDevice: ImsManager null, returning default value.");
588         return true;
589     }
590 
591     /**
592      * Indicates whether VoWifi is provisioned on slot.
593      *
594      * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not
595      * provisioned on device, this method returns false.
596      */
isWfcProvisionedOnDevice()597     public boolean isWfcProvisionedOnDevice() {
598         if (getBooleanCarrierConfig(
599                 CarrierConfigManager.KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL)) {
600             if (!isVolteProvisionedOnDevice()) {
601                 return false;
602             }
603         }
604 
605         if (getBooleanCarrierConfig(
606                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
607             return isWfcProvisioned();
608         }
609 
610         return true;
611     }
612 
613     /**
614      * Indicates whether VT is provisioned on device
615      *
616      * @deprecated Does not support MSIM devices. Please use
617      * {@link #isVtProvisionedOnDevice()} instead.
618      */
isVtProvisionedOnDevice(Context context)619     public static boolean isVtProvisionedOnDevice(Context context) {
620         ImsManager mgr = ImsManager.getInstance(context,
621                 SubscriptionManager.getDefaultVoicePhoneId());
622         if (mgr != null) {
623             return mgr.isVtProvisionedOnDevice();
624         }
625         Rlog.e(TAG, "isVtProvisionedOnDevice: ImsManager null, returning default value.");
626         return true;
627     }
628 
629     /**
630      * Indicates whether VT is provisioned on slot.
631      */
isVtProvisionedOnDevice()632     public boolean isVtProvisionedOnDevice() {
633         if (getBooleanCarrierConfig(
634                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
635             return isVtProvisioned();
636         }
637 
638         return true;
639     }
640 
641     /**
642      * Returns a platform configuration for VT which may override the user setting.
643      *
644      * Note: VT presumes that VoLTE is enabled (these are configuration settings
645      * which must be done correctly).
646      *
647      * @deprecated Does not support MSIM devices. Please use
648      * {@link #isVtEnabledByPlatform()} instead.
649      */
isVtEnabledByPlatform(Context context)650     public static boolean isVtEnabledByPlatform(Context context) {
651         ImsManager mgr = ImsManager.getInstance(context,
652                 SubscriptionManager.getDefaultVoicePhoneId());
653         if (mgr != null) {
654             return mgr.isVtEnabledByPlatform();
655         }
656         Rlog.e(TAG, "isVtEnabledByPlatform: ImsManager null, returning default value.");
657         return false;
658     }
659 
660     /**
661      * Returns a platform configuration for VT which may override the user setting.
662      *
663      * Note: VT presumes that VoLTE is enabled (these are configuration settings
664      * which must be done correctly).
665      */
isVtEnabledByPlatform()666     public boolean isVtEnabledByPlatform() {
667         // We first read the per slot value. If doesn't exist, we read the general value. If still
668         // doesn't exist, we use the hardcoded default value.
669         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE +
670                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
671                 SystemProperties.getInt(
672                         PROPERTY_DBG_VT_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
673             return true;
674         }
675 
676         return mContext.getResources().getBoolean(
677                 com.android.internal.R.bool.config_device_vt_available) &&
678                 getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
679                 isGbaValid();
680     }
681 
682     /**
683      * Returns the user configuration of VT setting
684      * @deprecated Does not support MSIM devices. Please use
685      * {@link #isVtEnabledByUser()} instead.
686      */
isVtEnabledByUser(Context context)687     public static boolean isVtEnabledByUser(Context context) {
688         ImsManager mgr = ImsManager.getInstance(context,
689                 SubscriptionManager.getDefaultVoicePhoneId());
690         if (mgr != null) {
691             return mgr.isVtEnabledByUser();
692         }
693         Rlog.e(TAG, "isVtEnabledByUser: ImsManager null, returning default value.");
694         return false;
695     }
696 
697     /**
698      * Returns the user configuration of VT setting per slot. If not set, it
699      * returns true as default value.
700      */
isVtEnabledByUser()701     public boolean isVtEnabledByUser() {
702         int setting = SubscriptionManager.getIntegerSubscriptionProperty(
703                 getSubId(), SubscriptionManager.VT_IMS_ENABLED,
704                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
705 
706         // If it's never set, by default we return true.
707         return (setting == SUB_PROPERTY_NOT_INITIALIZED
708                 || setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
709     }
710 
711     /**
712      * Change persistent VT enabled setting
713      *
714      * @deprecated Does not support MSIM devices. Please use {@link #setVtSetting(boolean)} instead.
715      */
setVtSetting(Context context, boolean enabled)716     public static void setVtSetting(Context context, boolean enabled) {
717         ImsManager mgr = ImsManager.getInstance(context,
718                 SubscriptionManager.getDefaultVoicePhoneId());
719         if (mgr != null) {
720             mgr.setVtSetting(enabled);
721         }
722         Rlog.e(TAG, "setVtSetting: ImsManager null, can not set value.");
723     }
724 
725     /**
726      * Change persistent VT enabled setting for slot.
727      */
setVtSetting(boolean enabled)728     public void setVtSetting(boolean enabled) {
729         if (enabled && !isVtProvisionedOnDevice()) {
730             log("setVtSetting: Not possible to enable Vt due to provisioning.");
731             return;
732         }
733 
734         int subId = getSubId();
735         if (isSubIdValid(subId)) {
736             SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.VT_IMS_ENABLED,
737                     booleanToPropertyString(enabled));
738         } else {
739             loge("setVtSetting: sub id invalid, skip modifying vt state in subinfo db; subId="
740                     + subId);
741         }
742 
743         try {
744             changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
745                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE, enabled);
746 
747             if (enabled) {
748                 log("setVtSetting(b) : turnOnIms");
749                 turnOnIms();
750             } else if (isTurnOffImsAllowedByPlatform()
751                     && (!isVolteEnabledByPlatform()
752                     || !isEnhanced4gLteModeSettingEnabledByUser())) {
753                 log("setVtSetting(b) : imsServiceAllowTurnOff -> turnOffIms");
754                 turnOffIms();
755             }
756         } catch (ImsException e) {
757             // The ImsService is down. Since the SubscriptionManager already recorded the user's
758             // preference, it will be resent in updateImsServiceConfig when the ImsPhoneCallTracker
759             // reconnects.
760             loge("setVtSetting(b): ", e);
761         }
762     }
763 
764     /**
765      * Returns whether turning off ims is allowed by platform.
766      * The platform property may override the carrier config.
767      *
768      * @deprecated Does not support MSIM devices. Please use
769      * {@link #isTurnOffImsAllowedByPlatform()} instead.
770      */
isTurnOffImsAllowedByPlatform(Context context)771     private static boolean isTurnOffImsAllowedByPlatform(Context context) {
772         ImsManager mgr = ImsManager.getInstance(context,
773                 SubscriptionManager.getDefaultVoicePhoneId());
774         if (mgr != null) {
775             return mgr.isTurnOffImsAllowedByPlatform();
776         }
777         Rlog.e(TAG, "isTurnOffImsAllowedByPlatform: ImsManager null, returning default value.");
778         return true;
779     }
780 
781     /**
782      * Returns whether turning off ims is allowed by platform.
783      * The platform property may override the carrier config.
784      */
isTurnOffImsAllowedByPlatform()785     private boolean isTurnOffImsAllowedByPlatform() {
786         // We first read the per slot value. If doesn't exist, we read the general value. If still
787         // doesn't exist, we use the hardcoded default value.
788         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE +
789                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
790                 SystemProperties.getInt(
791                         PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
792             return true;
793         }
794 
795         return getBooleanCarrierConfig(
796                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
797     }
798 
799     /**
800      * Returns the user configuration of WFC setting
801      *
802      * @deprecated Does not support MSIM devices. Please use
803      * {@link #isWfcEnabledByUser()} instead.
804      */
isWfcEnabledByUser(Context context)805     public static boolean isWfcEnabledByUser(Context context) {
806         ImsManager mgr = ImsManager.getInstance(context,
807                 SubscriptionManager.getDefaultVoicePhoneId());
808         if (mgr != null) {
809             return mgr.isWfcEnabledByUser();
810         }
811         Rlog.e(TAG, "isWfcEnabledByUser: ImsManager null, returning default value.");
812         return true;
813     }
814 
815     /**
816      * Returns the user configuration of WFC setting for slot. If not set, it
817      * queries CarrierConfig value as default.
818      */
isWfcEnabledByUser()819     public boolean isWfcEnabledByUser() {
820         int setting = SubscriptionManager.getIntegerSubscriptionProperty(
821                 getSubId(), SubscriptionManager.WFC_IMS_ENABLED,
822                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
823 
824         // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db.
825         if (setting == SUB_PROPERTY_NOT_INITIALIZED) {
826             return getBooleanCarrierConfig(
827                     CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL);
828         } else {
829             return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
830         }
831     }
832 
833     /**
834      * Change persistent WFC enabled setting.
835      * @deprecated Does not support MSIM devices. Please use
836      * {@link #setWfcSetting} instead.
837      */
setWfcSetting(Context context, boolean enabled)838     public static void setWfcSetting(Context context, boolean enabled) {
839         ImsManager mgr = ImsManager.getInstance(context,
840                 SubscriptionManager.getDefaultVoicePhoneId());
841         if (mgr != null) {
842             mgr.setWfcSetting(enabled);
843         }
844         Rlog.e(TAG, "setWfcSetting: ImsManager null, can not set value.");
845     }
846 
847     /**
848      * Change persistent WFC enabled setting for slot.
849      */
setWfcSetting(boolean enabled)850     public void setWfcSetting(boolean enabled) {
851         if (enabled && !isWfcProvisionedOnDevice()) {
852             log("setWfcSetting: Not possible to enable WFC due to provisioning.");
853             return;
854         }
855 
856         int subId = getSubId();
857         if (isSubIdValid(subId)) {
858             SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_ENABLED,
859                     booleanToPropertyString(enabled));
860         } else {
861             loge("setWfcSetting: invalid sub id, can not set WFC setting in siminfo db; subId="
862                     + subId);
863         }
864 
865         TelephonyManager tm = (TelephonyManager)
866                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
867         boolean isRoaming = tm.isNetworkRoaming(subId);
868         setWfcNonPersistent(enabled, getWfcMode(isRoaming));
869     }
870 
871     /**
872      * Non-persistently change WFC enabled setting and WFC mode for slot
873      *
874      * @param enabled If true, WFC and WFC while roaming will be enabled for the associated
875      *                subscription, if supported by the carrier. If false, WFC will be disabled for
876      *                the associated subscription.
877      * @param wfcMode The WFC preference if WFC is enabled
878      */
setWfcNonPersistent(boolean enabled, int wfcMode)879     public void setWfcNonPersistent(boolean enabled, int wfcMode) {
880         // Force IMS to register over LTE when turning off WFC
881         int imsWfcModeFeatureValue =
882                 enabled ? wfcMode : ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED;
883 
884         try {
885             changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
886                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, enabled);
887 
888             // Set the mode and roaming enabled settings before turning on IMS
889             setWfcModeInternal(imsWfcModeFeatureValue);
890             // If enabled is false, shortcut to false because of the ImsService
891             // implementation for WFC roaming, otherwise use the correct user's setting.
892             setWfcRoamingSettingInternal(enabled && isWfcRoamingEnabledByUser());
893 
894             if (enabled) {
895                 log("setWfcSetting() : turnOnIms");
896                 turnOnIms();
897             } else if (isTurnOffImsAllowedByPlatform()
898                     && (!isVolteEnabledByPlatform()
899                     || !isEnhanced4gLteModeSettingEnabledByUser())) {
900                 log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
901                 turnOffIms();
902             }
903         } catch (ImsException e) {
904             loge("setWfcSetting(): ", e);
905         }
906     }
907 
908     /**
909      * Returns the user configuration of WFC preference setting.
910      *
911      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean roaming)} instead.
912      */
getWfcMode(Context context)913     public static int getWfcMode(Context context) {
914         ImsManager mgr = ImsManager.getInstance(context,
915                 SubscriptionManager.getDefaultVoicePhoneId());
916         if (mgr != null) {
917             return mgr.getWfcMode();
918         }
919         Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
920         return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
921     }
922 
923     /**
924      * Returns the user configuration of WFC preference setting
925      * @deprecated. Use {@link #getWfcMode(boolean roaming)} instead.
926      */
getWfcMode()927     public int getWfcMode() {
928         return getWfcMode(false);
929     }
930 
931     /**
932      * Change persistent WFC preference setting.
933      *
934      * @deprecated Doesn't support MSIM devices. Use {@link #setWfcMode(int)} instead.
935      */
setWfcMode(Context context, int wfcMode)936     public static void setWfcMode(Context context, int wfcMode) {
937         ImsManager mgr = ImsManager.getInstance(context,
938                 SubscriptionManager.getDefaultVoicePhoneId());
939         if (mgr != null) {
940             mgr.setWfcMode(wfcMode);
941         }
942         Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
943     }
944 
945     /**
946      * Change persistent WFC preference setting for slot when not roaming.
947      * @deprecated Use {@link #setWfcMode(int, boolean)} instead.
948      */
setWfcMode(int wfcMode)949     public void setWfcMode(int wfcMode) {
950         setWfcMode(wfcMode, false /*isRoaming*/);
951     }
952 
953     /**
954      * Returns the user configuration of WFC preference setting
955      *
956      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
957      *
958      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean)} instead.
959      */
getWfcMode(Context context, boolean roaming)960     public static int getWfcMode(Context context, boolean roaming) {
961         ImsManager mgr = ImsManager.getInstance(context,
962                 SubscriptionManager.getDefaultVoicePhoneId());
963         if (mgr != null) {
964             return mgr.getWfcMode(roaming);
965         }
966         Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
967         return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
968     }
969 
970     /**
971      * Returns the user configuration of WFC preference setting for slot. If not set, it
972      * queries CarrierConfig value as default.
973      *
974      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
975      */
getWfcMode(boolean roaming)976     public int getWfcMode(boolean roaming) {
977         int setting;
978         if (!roaming) {
979             // The WFC mode is not editable, return the default setting in the CarrierConfig, not
980             // the user set value.
981             if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL)) {
982                 setting = getIntCarrierConfig(
983                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT);
984 
985             } else {
986                 setting = getSettingFromSubscriptionManager(SubscriptionManager.WFC_IMS_MODE,
987                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT);
988             }
989             if (DBG) log("getWfcMode - setting=" + setting);
990         } else {
991             if (getBooleanCarrierConfig(
992                     CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)) {
993                 setting = getWfcMode(false);
994             } else if (!getBooleanCarrierConfig(
995                     CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL)) {
996                 setting = getIntCarrierConfig(
997                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT);
998             } else {
999                 setting = getSettingFromSubscriptionManager(
1000                         SubscriptionManager.WFC_IMS_ROAMING_MODE,
1001                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT);
1002             }
1003             if (DBG) log("getWfcMode (roaming) - setting=" + setting);
1004         }
1005         return setting;
1006     }
1007 
1008     /**
1009      * Returns the SubscriptionManager setting for the subSetting string. If it is not set, default
1010      * to the default CarrierConfig value for defaultConfigKey.
1011      */
getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey)1012     private int getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey) {
1013         int result;
1014         result = SubscriptionManager.getIntegerSubscriptionProperty(getSubId(), subSetting,
1015                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
1016 
1017         // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db.
1018         if (result == SUB_PROPERTY_NOT_INITIALIZED) {
1019             result = getIntCarrierConfig(defaultConfigKey);
1020         }
1021         return result;
1022     }
1023 
1024     /**
1025      * Change persistent WFC preference setting
1026      *
1027      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
1028      *
1029      * @deprecated Doesn't support MSIM devices. Please use {@link #setWfcMode(int, boolean)}
1030      * instead.
1031      */
setWfcMode(Context context, int wfcMode, boolean roaming)1032     public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
1033         ImsManager mgr = ImsManager.getInstance(context,
1034                 SubscriptionManager.getDefaultVoicePhoneId());
1035         if (mgr != null) {
1036             mgr.setWfcMode(wfcMode, roaming);
1037         }
1038         Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
1039     }
1040 
1041     /**
1042      * Change persistent WFC preference setting
1043      *
1044      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
1045      */
setWfcMode(int wfcMode, boolean roaming)1046     public void setWfcMode(int wfcMode, boolean roaming) {
1047         int subId = getSubId();
1048         if (isSubIdValid(subId)) {
1049             if (!roaming) {
1050                 if (DBG) log("setWfcMode(i,b) - setting=" + wfcMode);
1051                 SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_MODE,
1052                         Integer.toString(wfcMode));
1053             } else {
1054                 if (DBG) log("setWfcMode(i,b) (roaming) - setting=" + wfcMode);
1055                 SubscriptionManager.setSubscriptionProperty(subId,
1056                         SubscriptionManager.WFC_IMS_ROAMING_MODE, Integer.toString(wfcMode));
1057             }
1058         } else {
1059             loge("setWfcMode(i,b): invalid sub id, skip setting setting in siminfo db; subId="
1060                     + subId);
1061         }
1062 
1063         TelephonyManager tm = (TelephonyManager)
1064                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
1065         // Unfortunately, the WFC mode is the same for home/roaming (we do not have separate
1066         // config keys), so we have to change the WFC mode when moving home<->roaming. So, only
1067         // call setWfcModeInternal when roaming == telephony roaming status. Otherwise, ignore.
1068         if (roaming == tm.isNetworkRoaming(getSubId())) {
1069             setWfcModeInternal(wfcMode);
1070         }
1071     }
1072 
getSubId()1073     private int getSubId() {
1074         int[] subIds = SubscriptionManager.getSubId(mPhoneId);
1075         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1076         if (subIds != null && subIds.length >= 1) {
1077             subId = subIds[0];
1078         }
1079         return subId;
1080     }
1081 
setWfcModeInternal(int wfcMode)1082     private void setWfcModeInternal(int wfcMode) {
1083         final int value = wfcMode;
1084         mExecutorFactory.executeRunnable(() -> {
1085             try {
1086                 getConfigInterface().setConfig(
1087                         ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE, value);
1088             } catch (ImsException e) {
1089                 // do nothing
1090             }
1091         });
1092     }
1093 
1094     /**
1095      * Returns the user configuration of WFC roaming setting
1096      *
1097      * @deprecated Does not support MSIM devices. Please use
1098      * {@link #isWfcRoamingEnabledByUser()} instead.
1099      */
isWfcRoamingEnabledByUser(Context context)1100     public static boolean isWfcRoamingEnabledByUser(Context context) {
1101         ImsManager mgr = ImsManager.getInstance(context,
1102                 SubscriptionManager.getDefaultVoicePhoneId());
1103         if (mgr != null) {
1104             return mgr.isWfcRoamingEnabledByUser();
1105         }
1106         Rlog.e(TAG, "isWfcRoamingEnabledByUser: ImsManager null, returning default value.");
1107         return false;
1108     }
1109 
1110     /**
1111      * Returns the user configuration of WFC roaming setting for slot. If not set, it
1112      * queries CarrierConfig value as default.
1113      */
isWfcRoamingEnabledByUser()1114     public boolean isWfcRoamingEnabledByUser() {
1115         int setting =  SubscriptionManager.getIntegerSubscriptionProperty(
1116                 getSubId(), SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
1117                 SUB_PROPERTY_NOT_INITIALIZED, mContext);
1118         if (setting == SUB_PROPERTY_NOT_INITIALIZED) {
1119             return getBooleanCarrierConfig(
1120                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL);
1121         } else {
1122             return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
1123         }
1124     }
1125 
1126     /**
1127      * Change persistent WFC roaming enabled setting
1128      */
setWfcRoamingSetting(Context context, boolean enabled)1129     public static void setWfcRoamingSetting(Context context, boolean enabled) {
1130         ImsManager mgr = ImsManager.getInstance(context,
1131                 SubscriptionManager.getDefaultVoicePhoneId());
1132         if (mgr != null) {
1133             mgr.setWfcRoamingSetting(enabled);
1134         }
1135         Rlog.e(TAG, "setWfcRoamingSetting: ImsManager null, value not set.");
1136     }
1137 
1138     /**
1139      * Change persistent WFC roaming enabled setting
1140      */
setWfcRoamingSetting(boolean enabled)1141     public void setWfcRoamingSetting(boolean enabled) {
1142         SubscriptionManager.setSubscriptionProperty(getSubId(),
1143                 SubscriptionManager.WFC_IMS_ROAMING_ENABLED, booleanToPropertyString(enabled)
1144         );
1145 
1146         setWfcRoamingSettingInternal(enabled);
1147     }
1148 
setWfcRoamingSettingInternal(boolean enabled)1149     private void setWfcRoamingSettingInternal(boolean enabled) {
1150         final int value = enabled
1151                 ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
1152                 : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
1153         mExecutorFactory.executeRunnable(() -> {
1154             try {
1155                 getConfigInterface().setConfig(
1156                         ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE, value);
1157             } catch (ImsException e) {
1158                 // do nothing
1159             }
1160         });
1161     }
1162 
1163     /**
1164      * Returns a platform configuration for WFC which may override the user
1165      * setting. Note: WFC presumes that VoLTE is enabled (these are
1166      * configuration settings which must be done correctly).
1167      *
1168      * @deprecated Doesn't work for MSIM devices. Use {@link #isWfcEnabledByPlatform()}
1169      * instead.
1170      */
isWfcEnabledByPlatform(Context context)1171     public static boolean isWfcEnabledByPlatform(Context context) {
1172         ImsManager mgr = ImsManager.getInstance(context,
1173                 SubscriptionManager.getDefaultVoicePhoneId());
1174         if (mgr != null) {
1175             return mgr.isWfcEnabledByPlatform();
1176         }
1177         Rlog.e(TAG, "isWfcEnabledByPlatform: ImsManager null, returning default value.");
1178         return false;
1179     }
1180 
1181     /**
1182      * Returns a platform configuration for WFC which may override the user
1183      * setting per slot. Note: WFC presumes that VoLTE is enabled (these are
1184      * configuration settings which must be done correctly).
1185      */
isWfcEnabledByPlatform()1186     public boolean isWfcEnabledByPlatform() {
1187         // We first read the per slot value. If doesn't exist, we read the general value. If still
1188         // doesn't exist, we use the hardcoded default value.
1189         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE +
1190                 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1  ||
1191                 SystemProperties.getInt(
1192                         PROPERTY_DBG_WFC_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) {
1193             return true;
1194         }
1195 
1196         return mContext.getResources().getBoolean(
1197                 com.android.internal.R.bool.config_device_wfc_ims_available) &&
1198                 getBooleanCarrierConfig(
1199                         CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1200                 isGbaValid();
1201     }
1202 
isSuppServicesOverUtEnabledByPlatform()1203     public boolean isSuppServicesOverUtEnabledByPlatform() {
1204         TelephonyManager manager = (TelephonyManager) mContext.getSystemService(
1205                 Context.TELEPHONY_SERVICE);
1206         int cardState = manager.getSimState(mPhoneId);
1207         if (cardState != TelephonyManager.SIM_STATE_READY) {
1208             // Do not report enabled until we actually have an active subscription.
1209             return false;
1210         }
1211         return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL) &&
1212                 isGbaValid();
1213     }
1214 
1215     /**
1216      * If carrier requires that IMS is only available if GBA capable SIM is used,
1217      * then this function checks GBA bit in EF IST.
1218      *
1219      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1220      */
isGbaValid()1221     private boolean isGbaValid() {
1222         if (getBooleanCarrierConfig(
1223                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1224             final TelephonyManager telephonyManager = new TelephonyManager(mContext, getSubId());
1225             String efIst = telephonyManager.getIsimIst();
1226             if (efIst == null) {
1227                 loge("isGbaValid - ISF is NULL");
1228                 return true;
1229             }
1230             boolean result = efIst != null && efIst.length() > 1 &&
1231                     (0x02 & (byte)efIst.charAt(1)) != 0;
1232             if (DBG) log("isGbaValid - GBA capable=" + result + ", ISF=" + efIst);
1233             return result;
1234         }
1235         return true;
1236     }
1237 
1238     /**
1239      * Will return with config value or throw an ImsException if we receive an error from
1240      * ImsConfig for that value.
1241      */
getProvisionedBool(ImsConfig config, int item)1242     private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
1243         int value = config.getProvisionedValue(item);
1244         if (value == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) {
1245             throw new ImsException("getProvisionedBool failed with error for item: " + item,
1246                     ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
1247         }
1248         return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED;
1249     }
1250 
1251     /**
1252      * Will set config value or throw an ImsException if we receive an error from ImsConfig for that
1253      * value.
1254      */
setProvisionedBool(ImsConfig config, int item, int value)1255     private void setProvisionedBool(ImsConfig config, int item, int value) throws ImsException {
1256         int result = config.setConfig(item, value);
1257         if (result != ImsConfigImplBase.CONFIG_RESULT_SUCCESS) {
1258             throw new ImsException("setProvisionedBool failed with error for item: " + item,
1259                     ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR);
1260         }
1261     }
1262 
1263     /**
1264      * Will return with config value or return false if we receive an error from
1265      * ImsConfigImplBase implementation for that value.
1266      */
getProvisionedBoolNoException(int item)1267     private boolean getProvisionedBoolNoException(int item) {
1268         try {
1269             ImsConfig config = getConfigInterface();
1270             return getProvisionedBool(config, item);
1271         } catch (ImsException ex) {
1272             logw("getProvisionedBoolNoException: operation failed for item=" + item
1273                     + ". Exception:" + ex.getMessage() + ". Returning false.");
1274             return false;
1275         }
1276     }
1277 
1278     /**
1279      * Will return with config value or return false if we receive an error from
1280      * ImsConfigImplBase implementation for that value.
1281      */
setProvisionedBoolNoException(int item, int value)1282     private boolean setProvisionedBoolNoException(int item, int value) {
1283         try {
1284             ImsConfig config = getConfigInterface();
1285             setProvisionedBool(config, item, value);
1286         } catch (ImsException ex) {
1287             logw("setProvisionedBoolNoException: operation failed for item=" + item
1288                     + ", value=" + value + ". Exception:" + ex.getMessage());
1289             return false;
1290         }
1291         return true;
1292     }
1293 
1294     /**
1295      * Sync carrier config and user settings with ImsConfigImplBase implementation.
1296      *
1297      * @param context for the manager object
1298      * @param phoneId phone id
1299      * @param force update
1300      *
1301      * @deprecated Doesn't support MSIM devices. Use {@link #updateImsServiceConfig(boolean)}
1302      * instead.
1303      */
updateImsServiceConfig(Context context, int phoneId, boolean force)1304     public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
1305         ImsManager mgr = ImsManager.getInstance(context, phoneId);
1306         if (mgr != null) {
1307             mgr.updateImsServiceConfig(force);
1308         }
1309         Rlog.e(TAG, "updateImsServiceConfig: ImsManager null, returning without update.");
1310     }
1311 
1312     /**
1313      * Sync carrier config and user settings with ImsConfigImplBase implementation.
1314      *
1315      * @param force update
1316      */
updateImsServiceConfig(boolean force)1317     public void updateImsServiceConfig(boolean force) {
1318         if (!force) {
1319             TelephonyManager tm = new TelephonyManager(mContext, getSubId());
1320             if (tm.getSimState() != TelephonyManager.SIM_STATE_READY) {
1321                 log("updateImsServiceConfig: SIM not ready");
1322                 // Don't disable IMS if SIM is not ready
1323                 return;
1324             }
1325         }
1326 
1327         if (!mConfigUpdated || force) {
1328             try {
1329                 PersistableBundle imsCarrierConfigs =
1330                         mConfigManager.getConfigByComponentForSubId(
1331                                 CarrierConfigManager.Ims.KEY_PREFIX, getSubId());
1332 
1333                 updateImsCarrierConfigs(imsCarrierConfigs);
1334 
1335                 // Note: currently the order of updates is set to produce different order of
1336                 // changeEnabledCapabilities() function calls from setAdvanced4GMode(). This is done
1337                 // to differentiate this code path from vendor code perspective.
1338                 CapabilityChangeRequest request = new CapabilityChangeRequest();
1339                 updateVolteFeatureValue(request);
1340                 updateWfcFeatureAndProvisionedValues(request);
1341                 updateVideoCallFeatureValue(request);
1342                 // Only turn on IMS for RTT if there's an active subscription present. If not, the
1343                 // modem will be in emergency-call-only mode and will use separate signaling to
1344                 // establish an RTT emergency call.
1345                 boolean isImsNeededForRtt = updateRttConfigValue() && isActiveSubscriptionPresent();
1346                 // Supplementary services over UT do not require IMS registration. Do not alter IMS
1347                 // registration based on UT.
1348                 updateUtFeatureValue(request);
1349 
1350                 // Send the batched request to the modem.
1351                 changeMmTelCapability(request);
1352 
1353                 if (isImsNeededForRtt || !isTurnOffImsAllowedByPlatform() || isImsNeeded(request)) {
1354                     // Turn on IMS if it is used.
1355                     // Also, if turning off is not allowed for current carrier,
1356                     // we need to turn IMS on because it might be turned off before
1357                     // phone switched to current carrier.
1358                     log("updateImsServiceConfig: turnOnIms");
1359                     turnOnIms();
1360                 } else {
1361                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
1362                     log("updateImsServiceConfig: turnOffIms");
1363                     turnOffIms();
1364                 }
1365 
1366                 mConfigUpdated = true;
1367             } catch (ImsException e) {
1368                 loge("updateImsServiceConfig: ", e);
1369                 mConfigUpdated = false;
1370             }
1371         }
1372     }
1373 
isImsNeeded(CapabilityChangeRequest r)1374     private boolean isImsNeeded(CapabilityChangeRequest r) {
1375         // IMS is not needed for UT, so only enabled IMS if any other capability is enabled.
1376         return r.getCapabilitiesToEnable().stream()
1377                 .anyMatch((c) ->
1378                         (c.getCapability() != MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT));
1379     }
1380 
1381     /**
1382      * Update VoLTE config
1383      */
updateVolteFeatureValue(CapabilityChangeRequest request)1384     private void updateVolteFeatureValue(CapabilityChangeRequest request) {
1385         boolean available = isVolteEnabledByPlatform();
1386         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser();
1387         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
1388         boolean isProvisioned = isVolteProvisionedOnDevice();
1389         boolean isFeatureOn = available && enabled && isNonTty && isProvisioned;
1390 
1391         log("updateVolteFeatureValue: available = " + available
1392                 + ", enabled = " + enabled
1393                 + ", nonTTY = " + isNonTty
1394                 + ", provisioned = " + isProvisioned
1395                 + ", isFeatureOn = " + isFeatureOn);
1396 
1397         if (isFeatureOn) {
1398             request.addCapabilitiesToEnableForTech(
1399                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1400                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1401         } else {
1402             request.addCapabilitiesToDisableForTech(
1403                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1404                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1405         }
1406     }
1407 
1408     /**
1409      * Update video call over LTE config
1410      */
updateVideoCallFeatureValue(CapabilityChangeRequest request)1411     private void updateVideoCallFeatureValue(CapabilityChangeRequest request) {
1412         boolean available = isVtEnabledByPlatform();
1413         boolean vtEnabled = isVtEnabledByUser();
1414         boolean advancedEnabled = isEnhanced4gLteModeSettingEnabledByUser();
1415         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled();
1416         boolean isDataEnabled = isDataEnabled();
1417         boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(
1418                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1419         boolean isProvisioned = isVtProvisionedOnDevice();
1420         // TODO: Support carrier config setting about if VT settings should be associated with
1421         //  advanced calling settings.
1422         boolean isFeatureOn = available && vtEnabled && isNonTty && isProvisioned
1423                 && advancedEnabled && (ignoreDataEnabledChanged || isDataEnabled);
1424 
1425         log("updateVideoCallFeatureValue: available = " + available
1426                 + ", vtenabled = " + vtEnabled
1427                 + ", advancedCallEnabled = " + advancedEnabled
1428                 + ", nonTTY = " + isNonTty
1429                 + ", data enabled = " + isDataEnabled
1430                 + ", provisioned = " + isProvisioned
1431                 + ", isFeatureOn = " + isFeatureOn);
1432 
1433         if (isFeatureOn) {
1434             request.addCapabilitiesToEnableForTech(
1435                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1436                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1437         } else {
1438             request.addCapabilitiesToDisableForTech(
1439                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
1440                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1441         }
1442     }
1443 
1444     /**
1445      * Update WFC config
1446      */
updateWfcFeatureAndProvisionedValues(CapabilityChangeRequest request)1447     private void updateWfcFeatureAndProvisionedValues(CapabilityChangeRequest request) {
1448         TelephonyManager tm = new TelephonyManager(mContext, getSubId());
1449         boolean isNetworkRoaming = tm.isNetworkRoaming();
1450         boolean available = isWfcEnabledByPlatform();
1451         boolean enabled = isWfcEnabledByUser();
1452         boolean isProvisioned = isWfcProvisionedOnDevice();
1453         int mode = getWfcMode(isNetworkRoaming);
1454         boolean roaming = isWfcRoamingEnabledByUser();
1455         boolean isFeatureOn = available && enabled && isProvisioned;
1456 
1457         log("updateWfcFeatureAndProvisionedValues: available = " + available
1458                 + ", enabled = " + enabled
1459                 + ", mode = " + mode
1460                 + ", provisioned = " + isProvisioned
1461                 + ", roaming = " + roaming
1462                 + ", isFeatureOn = " + isFeatureOn);
1463 
1464         if (isFeatureOn) {
1465             request.addCapabilitiesToEnableForTech(
1466                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1467                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
1468         } else {
1469             request.addCapabilitiesToDisableForTech(
1470                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
1471                     ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
1472         }
1473 
1474         if (!isFeatureOn) {
1475             mode = ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED;
1476             roaming = false;
1477         }
1478         setWfcModeInternal(mode);
1479         setWfcRoamingSettingInternal(roaming);
1480     }
1481 
1482 
updateUtFeatureValue(CapabilityChangeRequest request)1483     private void updateUtFeatureValue(CapabilityChangeRequest request) {
1484         boolean isCarrierSupported = isSuppServicesOverUtEnabledByPlatform();
1485         boolean requiresProvisioning = getBooleanCarrierConfig(
1486                 CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL);
1487         // Count as "provisioned" if we do not require provisioning.
1488         boolean isProvisioned = true;
1489         if (requiresProvisioning) {
1490             ITelephony telephony = ITelephony.Stub.asInterface(
1491                     ServiceManager.getService(Context.TELEPHONY_SERVICE));
1492             // Only track UT over LTE, since we do not differentiate between UT over LTE and IWLAN
1493             // currently.
1494             try {
1495                 if (telephony != null) {
1496                     isProvisioned = telephony.isMmTelCapabilityProvisionedInCache(getSubId(),
1497                             MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1498                             ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1499                 }
1500             } catch (RemoteException e) {
1501                 loge("updateUtFeatureValue: couldn't reach telephony! returning provisioned");
1502             }
1503         }
1504         boolean isFeatureOn = isCarrierSupported && isProvisioned;
1505 
1506         log("updateUtFeatureValue: available = " + isCarrierSupported
1507                 + ", isProvisioned = " + isProvisioned
1508                 + ", enabled = " + isFeatureOn);
1509 
1510         if (isFeatureOn) {
1511             request.addCapabilitiesToEnableForTech(
1512                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1513                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1514         } else {
1515             request.addCapabilitiesToDisableForTech(
1516                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT,
1517                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
1518         }
1519     }
1520 
1521     /**
1522      * Do NOT use this directly, instead use {@link #getInstance(Context, int)}.
1523      */
1524     @VisibleForTesting
ImsManager(Context context, int phoneId)1525     public ImsManager(Context context, int phoneId) {
1526         mContext = context;
1527         mPhoneId = phoneId;
1528         mConfigManager = (CarrierConfigManager) context.getSystemService(
1529                 Context.CARRIER_CONFIG_SERVICE);
1530         createImsService();
1531     }
1532 
1533     /*
1534      * Returns a flag indicating whether the IMS service is available. If it is not available or
1535      * busy, it will try to connect before reporting failure.
1536      */
isServiceAvailable()1537     public boolean isServiceAvailable() {
1538         connectIfServiceIsAvailable();
1539         // mImsServiceProxy will always create an ImsServiceProxy.
1540         return mMmTelFeatureConnection.isBinderAlive();
1541     }
1542 
1543     /*
1544      * Returns a flag indicating whether the IMS service is ready to send requests to lower layers.
1545      */
isServiceReady()1546     public boolean isServiceReady() {
1547         connectIfServiceIsAvailable();
1548         return mMmTelFeatureConnection.isBinderReady();
1549     }
1550 
1551     /**
1552      * If the service is available, try to reconnect.
1553      */
connectIfServiceIsAvailable()1554     public void connectIfServiceIsAvailable() {
1555         if (mMmTelFeatureConnection == null || !mMmTelFeatureConnection.isBinderAlive()) {
1556             createImsService();
1557         }
1558     }
1559 
setConfigListener(ImsConfigListener listener)1560     public void setConfigListener(ImsConfigListener listener) {
1561         mImsConfigListener = listener;
1562     }
1563 
1564 
1565     /**
1566      * Adds a callback for status changed events if the binder is already available. If it is not,
1567      * this method will throw an ImsException.
1568      */
1569     @Override
1570     @VisibleForTesting
addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate c)1571     public void addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate c)
1572             throws android.telephony.ims.ImsException {
1573         if (!mMmTelFeatureConnection.isBinderAlive()) {
1574             throw new android.telephony.ims.ImsException("Can not connect to ImsService",
1575                     android.telephony.ims.ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1576         }
1577         if (c != null) {
1578             mStatusCallbacks.add(c);
1579         }
1580     }
1581 
1582     @Override
removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate c)1583     public void removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate c) {
1584         if (c != null) {
1585             mStatusCallbacks.remove(c);
1586         } else {
1587             logw("removeNotifyStatusChangedCallback: callback is null!");
1588         }
1589     }
1590 
1591     /**
1592      * Opens the IMS service for making calls and/or receiving generic IMS calls.
1593      * The caller may make subsequent calls through {@link #makeCall}.
1594      * The IMS service will register the device to the operator's network with the credentials
1595      * (from ISIM) periodically in order to receive calls from the operator's network.
1596      * When the IMS service receives a new call, it will call
1597      * {@link MmTelFeature.Listener#onIncomingCall}
1598      * @param listener A {@link MmTelFeature.Listener}, which is the interface the
1599      * {@link MmTelFeature} uses to notify the framework of updates
1600      * @throws NullPointerException if {@code listener} is null
1601      * @throws ImsException if calling the IMS service results in an error
1602      */
open(MmTelFeature.Listener listener)1603     public void open(MmTelFeature.Listener listener) throws ImsException {
1604         checkAndThrowExceptionIfServiceUnavailable();
1605 
1606         if (listener == null) {
1607             throw new NullPointerException("listener can't be null");
1608         }
1609 
1610         try {
1611             mMmTelFeatureConnection.openConnection(listener);
1612         } catch (RemoteException e) {
1613             throw new ImsException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1614         }
1615     }
1616 
1617     /**
1618      * Adds registration listener to the IMS service.
1619      *
1620      * @param serviceClass a service class specified in {@link ImsServiceClass}
1621      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
1622      * @param listener To listen to IMS registration events; It cannot be null
1623      * @throws NullPointerException if {@code listener} is null
1624      * @throws ImsException if calling the IMS service results in an error
1625      *
1626      * @deprecated Use {@link #addRegistrationListener(ImsConnectionStateListener)} instead.
1627      */
addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)1628     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
1629             throws ImsException {
1630         addRegistrationListener(listener);
1631     }
1632 
1633     /**
1634      * Adds registration listener to the IMS service.
1635      *
1636      * @param listener To listen to IMS registration events; It cannot be null
1637      * @throws NullPointerException if {@code listener} is null
1638      * @throws ImsException if calling the IMS service results in an error
1639      * @deprecated use {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback)}
1640      * instead.
1641      */
addRegistrationListener(ImsConnectionStateListener listener)1642     public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException {
1643         if (listener == null) {
1644             throw new NullPointerException("listener can't be null");
1645         }
1646         addRegistrationCallback(listener);
1647         // connect the ImsConnectionStateListener to the new CapabilityCallback.
1648         addCapabilitiesCallback(new ImsMmTelManager.CapabilityCallback() {
1649             @Override
1650             public void onCapabilitiesStatusChanged(
1651                     MmTelFeature.MmTelCapabilities capabilities) {
1652                 listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), capabilities);
1653             }
1654         });
1655         log("Registration Callback registered.");
1656     }
1657 
1658     /**
1659      * Adds a callback that gets called when IMS registration has changed for the slot ID
1660      * associated with this ImsManager.
1661      * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
1662      *                 caller when IMS registration status has changed.
1663      * @throws ImsException when the ImsService connection is not available.
1664      */
addRegistrationCallback(RegistrationManager.RegistrationCallback callback)1665     public void addRegistrationCallback(RegistrationManager.RegistrationCallback callback)
1666             throws ImsException {
1667         if (callback == null) {
1668             throw new NullPointerException("registration callback can't be null");
1669         }
1670 
1671         try {
1672             callback.setExecutor(getThreadExecutor());
1673             mMmTelFeatureConnection.addRegistrationCallback(callback.getBinder());
1674             log("Registration Callback registered.");
1675             // Only record if there isn't a RemoteException.
1676         } catch (IllegalStateException e) {
1677             throw new ImsException("addRegistrationCallback(IRIB)", e,
1678                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1679         }
1680     }
1681 
1682     /**
1683      * Removes a previously added registration callback that was added via
1684      * {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback)} .
1685      * @param callback A {@link RegistrationManager.RegistrationCallback} that was previously added.
1686      */
removeRegistrationListener(RegistrationManager.RegistrationCallback callback)1687     public void removeRegistrationListener(RegistrationManager.RegistrationCallback callback) {
1688         if (callback == null) {
1689             throw new NullPointerException("registration callback can't be null");
1690         }
1691 
1692         mMmTelFeatureConnection.removeRegistrationCallback(callback.getBinder());
1693         log("Registration callback removed.");
1694     }
1695 
1696     /**
1697      * Adds a callback that gets called when IMS registration has changed for a specific
1698      * subscription.
1699      *
1700      * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
1701      *                 caller when IMS registration status has changed.
1702      * @param subId The subscription ID to register this registration callback for.
1703      * @throws RemoteException when the ImsService connection is not available.
1704      */
addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)1705     public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)
1706             throws RemoteException {
1707         if (callback == null) {
1708             throw new IllegalArgumentException("registration callback can't be null");
1709         }
1710         mMmTelFeatureConnection.addRegistrationCallbackForSubscription(callback, subId);
1711         log("Registration Callback registered.");
1712         // Only record if there isn't a RemoteException.
1713     }
1714 
1715     /**
1716      * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback
1717      * that is associated with a specific subscription.
1718      */
removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)1719     public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
1720             int subId) {
1721         if (callback == null) {
1722             throw new IllegalArgumentException("registration callback can't be null");
1723         }
1724 
1725         mMmTelFeatureConnection.removeRegistrationCallbackForSubscription(callback, subId);
1726     }
1727 
1728     /**
1729      * Adds a callback that gets called when MMTel capability status has changed, for example when
1730      * Voice over IMS or VT over IMS is not available currently.
1731      * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller when
1732      *                 MMTel capability status has changed.
1733      * @throws ImsException when the ImsService connection is not available.
1734      */
addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)1735     public void addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)
1736             throws ImsException {
1737         if (callback == null) {
1738             throw new NullPointerException("capabilities callback can't be null");
1739         }
1740 
1741         checkAndThrowExceptionIfServiceUnavailable();
1742         try {
1743             callback.setExecutor(getThreadExecutor());
1744             mMmTelFeatureConnection.addCapabilityCallback(callback.getBinder());
1745             log("Capability Callback registered.");
1746             // Only record if there isn't a RemoteException.
1747         } catch (IllegalStateException e) {
1748             throw new ImsException("addCapabilitiesCallback(IF)", e,
1749                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1750         }
1751     }
1752 
1753     /**
1754      * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback.
1755      * @throws ImsException when the ImsService connection is not available.
1756      */
removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)1757     public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)
1758             throws ImsException {
1759         if (callback == null) {
1760             throw new NullPointerException("capabilities callback can't be null");
1761         }
1762 
1763         checkAndThrowExceptionIfServiceUnavailable();
1764         mMmTelFeatureConnection.removeCapabilityCallback(callback.getBinder());
1765     }
1766 
1767     /**
1768      * Adds a callback that gets called when IMS capabilities have changed for a specified
1769      * subscription.
1770      * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller
1771      *                 when the IMS Capabilities have changed.
1772      * @param subId The subscription that is associated with the callback.
1773      * @throws RemoteException when the ImsService connection is not available.
1774      */
addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)1775     public void addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)
1776             throws RemoteException {
1777         if (callback == null) {
1778             throw new IllegalArgumentException("registration callback can't be null");
1779         }
1780 
1781         mMmTelFeatureConnection.addCapabilityCallbackForSubscription(callback, subId);
1782         log("Capability Callback registered for subscription.");
1783     }
1784 
1785     /**
1786      * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} that was
1787      * associated with a specific subscription.
1788      */
removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)1789     public void removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback,
1790             int subId) {
1791         if (callback == null) {
1792             throw new IllegalArgumentException("capabilities callback can't be null");
1793         }
1794 
1795         mMmTelFeatureConnection.removeCapabilityCallbackForSubscription(callback, subId);
1796     }
1797 
1798     /**
1799      * Removes the registration listener from the IMS service.
1800      *
1801      * @param listener Previously registered listener that will be removed. Can not be null.
1802      * @throws NullPointerException if {@code listener} is null
1803      * @throws ImsException if calling the IMS service results in an error
1804      * instead.
1805      */
removeRegistrationListener(ImsConnectionStateListener listener)1806     public void removeRegistrationListener(ImsConnectionStateListener listener)
1807             throws ImsException {
1808         if (listener == null) {
1809             throw new NullPointerException("listener can't be null");
1810         }
1811 
1812         checkAndThrowExceptionIfServiceUnavailable();
1813         mMmTelFeatureConnection.removeRegistrationCallback(listener.getBinder());
1814         log("Registration Callback/Listener registered.");
1815         // Only record if there isn't a RemoteException.
1816     }
1817 
1818     /**
1819      * Adds a callback that gets called when Provisioning has changed for a specified subscription.
1820      * @param callback A {@link ProvisioningManager.Callback} that will notify the caller when
1821      *                 provisioning has changed.
1822      * @param subId The subscription that is associated with the callback.
1823      * @throws IllegalStateException when the {@link ImsService} connection is not available.
1824      * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null.
1825      */
addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)1826     public void addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) {
1827         if (callback == null) {
1828             throw new IllegalArgumentException("provisioning callback can't be null");
1829         }
1830 
1831         mMmTelFeatureConnection.addProvisioningCallbackForSubscription(callback, subId);
1832         log("Capability Callback registered for subscription.");
1833     }
1834 
1835     /**
1836      * Removes a previously registered {@link ProvisioningManager.Callback} that was associated with
1837      * a specific subscription.
1838      * @throws IllegalStateException when the {@link ImsService} connection is not available.
1839      * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null.
1840      */
removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)1841     public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) {
1842         if (callback == null) {
1843             throw new IllegalArgumentException("provisioning callback can't be null");
1844         }
1845 
1846         mMmTelFeatureConnection.removeProvisioningCallbackForSubscription(callback, subId);
1847     }
1848 
getRegistrationTech()1849     public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() {
1850         try {
1851             return mMmTelFeatureConnection.getRegistrationTech();
1852         } catch (RemoteException e) {
1853             logw("getRegistrationTech: no connection to ImsService.");
1854             return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
1855         }
1856     }
1857 
getRegistrationTech(Consumer<Integer> callback)1858     public void getRegistrationTech(Consumer<Integer> callback) {
1859         mExecutorFactory.executeRunnable(() -> {
1860             try {
1861                 int tech = mMmTelFeatureConnection.getRegistrationTech();
1862                 callback.accept(tech);
1863             } catch (RemoteException e) {
1864                 logw("getRegistrationTech(C): no connection to ImsService.");
1865                 callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
1866             }
1867         });
1868     }
1869 
1870     /**
1871      * Closes the connection and removes all active callbacks.
1872      * All the resources that were allocated to the service are also released.
1873      */
close()1874     public void close() {
1875         if (mMmTelFeatureConnection != null) {
1876             mMmTelFeatureConnection.closeConnection();
1877         }
1878         mUt = null;
1879         mEcbm = null;
1880         mMultiEndpoint = null;
1881     }
1882 
1883     /**
1884      * Gets the configuration interface to provision / withdraw the supplementary service settings.
1885      *
1886      * @return the Ut interface instance
1887      * @throws ImsException if getting the Ut interface results in an error
1888      */
getSupplementaryServiceConfiguration()1889     public ImsUtInterface getSupplementaryServiceConfiguration() throws ImsException {
1890         // FIXME: manage the multiple Ut interfaces based on the session id
1891         if (mUt != null && mUt.isBinderAlive()) {
1892             return mUt;
1893         }
1894 
1895         checkAndThrowExceptionIfServiceUnavailable();
1896         try {
1897             IImsUt iUt = mMmTelFeatureConnection.getUtInterface();
1898 
1899             if (iUt == null) {
1900                 throw new ImsException("getSupplementaryServiceConfiguration()",
1901                         ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
1902             }
1903 
1904             mUt = new ImsUt(iUt);
1905         } catch (RemoteException e) {
1906             throw new ImsException("getSupplementaryServiceConfiguration()", e,
1907                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1908         }
1909         return mUt;
1910     }
1911 
1912     /**
1913      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1914      *
1915      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1916      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1917      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1918      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1919      * @param callType a call type that is specified in {@link ImsCallProfile}
1920      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1921      *        {@link ImsCallProfile#CALL_TYPE_VT}
1922      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1923      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1924      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1925      *        {@link ImsCallProfile#CALL_TYPE_VS}
1926      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1927      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1928      * @return a {@link ImsCallProfile} object
1929      * @throws ImsException if calling the IMS service results in an error
1930      */
createCallProfile(int serviceType, int callType)1931     public ImsCallProfile createCallProfile(int serviceType, int callType) throws ImsException {
1932         checkAndThrowExceptionIfServiceUnavailable();
1933 
1934         try {
1935             return mMmTelFeatureConnection.createCallProfile(serviceType, callType);
1936         } catch (RemoteException e) {
1937             throw new ImsException("createCallProfile()", e,
1938                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1939         }
1940     }
1941 
1942     /**
1943      * Creates a {@link ImsCall} to make a call.
1944      *
1945      * @param profile a call profile to make the call
1946      *      (it contains service type, call type, media information, etc.)
1947      * @param callees participants to invite the conference call
1948      * @param listener listen to the call events from {@link ImsCall}
1949      * @return a {@link ImsCall} object
1950      * @throws ImsException if calling the IMS service results in an error
1951      */
makeCall(ImsCallProfile profile, String[] callees, ImsCall.Listener listener)1952     public ImsCall makeCall(ImsCallProfile profile, String[] callees,
1953             ImsCall.Listener listener) throws ImsException {
1954         if (DBG) {
1955             log("makeCall :: profile=" + profile);
1956         }
1957 
1958         checkAndThrowExceptionIfServiceUnavailable();
1959 
1960         ImsCall call = new ImsCall(mContext, profile);
1961 
1962         call.setListener(listener);
1963         ImsCallSession session = createCallSession(profile);
1964 
1965         if ((callees != null) && (callees.length == 1) && !(session.isMultiparty())) {
1966             call.start(session, callees[0]);
1967         } else {
1968             call.start(session, callees);
1969         }
1970 
1971         return call;
1972     }
1973 
1974     /**
1975      * Creates a {@link ImsCall} to take an incoming call.
1976      *
1977      * @param listener to listen to the call events from {@link ImsCall}
1978      * @return a {@link ImsCall} object
1979      * @throws ImsException if calling the IMS service results in an error
1980      */
takeCall(IImsCallSession session, ImsCall.Listener listener)1981     public ImsCall takeCall(IImsCallSession session, ImsCall.Listener listener)
1982             throws ImsException {
1983         checkAndThrowExceptionIfServiceUnavailable();
1984         try {
1985             if (session == null) {
1986                 throw new ImsException("No pending session for the call",
1987                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
1988             }
1989 
1990             ImsCall call = new ImsCall(mContext, session.getCallProfile());
1991 
1992             call.attachSession(new ImsCallSession(session));
1993             call.setListener(listener);
1994 
1995             return call;
1996         } catch (Throwable t) {
1997             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
1998         }
1999     }
2000 
2001     /**
2002      * Gets the config interface to get/set service/capability parameters.
2003      *
2004      * @return the ImsConfig instance.
2005      * @throws ImsException if getting the setting interface results in an error.
2006      */
2007     @UnsupportedAppUsage
getConfigInterface()2008     public ImsConfig getConfigInterface() throws ImsException {
2009         checkAndThrowExceptionIfServiceUnavailable();
2010 
2011         IImsConfig config = mMmTelFeatureConnection.getConfigInterface();
2012         if (config == null) {
2013             throw new ImsException("getConfigInterface()",
2014                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
2015         }
2016         return new ImsConfig(config);
2017     }
2018 
changeMmTelCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, boolean isEnabled)2019     public void changeMmTelCapability(
2020             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
2021             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech,
2022             boolean isEnabled) throws ImsException {
2023 
2024         CapabilityChangeRequest request = new CapabilityChangeRequest();
2025         if (isEnabled) {
2026             request.addCapabilitiesToEnableForTech(capability, radioTech);
2027         } else {
2028             request.addCapabilitiesToDisableForTech(capability, radioTech);
2029         }
2030         changeMmTelCapability(request);
2031     }
2032 
changeMmTelCapability(CapabilityChangeRequest r)2033     public void changeMmTelCapability(CapabilityChangeRequest r) throws ImsException {
2034         checkAndThrowExceptionIfServiceUnavailable();
2035         try {
2036             logi("changeMmTelCapability: changing capabilities for sub: " + getSubId()
2037                     + ", request: " + r);
2038             mMmTelFeatureConnection.changeEnabledCapabilities(r, null);
2039             if (mImsConfigListener == null) {
2040                 return;
2041             }
2042             for (CapabilityChangeRequest.CapabilityPair enabledCaps : r.getCapabilitiesToEnable()) {
2043                 mImsConfigListener.onSetFeatureResponse(enabledCaps.getCapability(),
2044                         enabledCaps.getRadioTech(),
2045                         ProvisioningManager.PROVISIONING_VALUE_ENABLED, -1);
2046             }
2047             for (CapabilityChangeRequest.CapabilityPair disabledCaps :
2048                     r.getCapabilitiesToDisable()) {
2049                 mImsConfigListener.onSetFeatureResponse(disabledCaps.getCapability(),
2050                         disabledCaps.getRadioTech(),
2051                         ProvisioningManager.PROVISIONING_VALUE_DISABLED, -1);
2052             }
2053         } catch (RemoteException e) {
2054             throw new ImsException("changeMmTelCapability(CCR)", e,
2055                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2056         }
2057     }
2058 
updateRttConfigValue()2059     public boolean updateRttConfigValue() {
2060         // If there's no active sub anywhere on the device, enable RTT on the modem so that
2061         // the device can make an emergency call.
2062 
2063         boolean isActiveSubscriptionPresent = isActiveSubscriptionPresent();
2064         boolean isCarrierSupported =
2065                 getBooleanCarrierConfig(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL)
2066                 || !isActiveSubscriptionPresent;
2067 
2068         boolean isRttUiSettingEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
2069                 Settings.Secure.RTT_CALLING_MODE, 0) != 0;
2070         boolean isRttAlwaysOnCarrierConfig = getBooleanCarrierConfig(
2071                 CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
2072 
2073         boolean shouldImsRttBeOn = isRttUiSettingEnabled || isRttAlwaysOnCarrierConfig;
2074         logi("update RTT: settings value: " + isRttUiSettingEnabled + " always-on carrierconfig: "
2075                 + isRttAlwaysOnCarrierConfig
2076                 + "isActiveSubscriptionPresent: " + isActiveSubscriptionPresent);
2077 
2078         if (isCarrierSupported) {
2079             setRttConfig(shouldImsRttBeOn);
2080         } else {
2081             setRttConfig(false);
2082         }
2083         return isCarrierSupported && shouldImsRttBeOn;
2084     }
2085 
setRttConfig(boolean enabled)2086     private void setRttConfig(boolean enabled) {
2087         final int value = enabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2088                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2089         mExecutorFactory.executeRunnable(() -> {
2090             try {
2091                 logi("Setting RTT enabled to " + enabled);
2092                 getConfigInterface().setProvisionedValue(
2093                         ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value);
2094             } catch (ImsException e) {
2095                 loge("Unable to set RTT value enabled to " + enabled + ": " + e);
2096             }
2097         });
2098     }
2099 
queryMmTelCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2100     public boolean queryMmTelCapability(
2101             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
2102             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
2103         checkAndThrowExceptionIfServiceUnavailable();
2104 
2105         BlockingQueue<Boolean> result = new LinkedBlockingDeque<>(1);
2106 
2107         try {
2108             mMmTelFeatureConnection.queryEnabledCapabilities(capability, radioTech,
2109                     new IImsCapabilityCallback.Stub() {
2110                         @Override
2111                         public void onQueryCapabilityConfiguration(int resCap, int resTech,
2112                                 boolean enabled) {
2113                             if (resCap == capability && resTech == radioTech) {
2114                                 result.offer(enabled);
2115                             }
2116                         }
2117 
2118                         @Override
2119                         public void onChangeCapabilityConfigurationError(int capability,
2120                                 int radioTech, int reason) {
2121 
2122                         }
2123 
2124                         @Override
2125                         public void onCapabilitiesStatusChanged(int config) {
2126 
2127                         }
2128                     });
2129         } catch (RemoteException e) {
2130             throw new ImsException("queryMmTelCapability()", e,
2131                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2132         }
2133 
2134         try {
2135             return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
2136         } catch (InterruptedException e) {
2137             logw("queryMmTelCapability: interrupted while waiting for response");
2138         }
2139         return false;
2140     }
2141 
queryMmTelCapabilityStatus( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2142     public boolean queryMmTelCapabilityStatus(
2143             @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
2144             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
2145         checkAndThrowExceptionIfServiceUnavailable();
2146 
2147         if (getRegistrationTech() != radioTech)
2148             return false;
2149 
2150         try {
2151 
2152             MmTelFeature.MmTelCapabilities capabilities =
2153                     mMmTelFeatureConnection.queryCapabilityStatus();
2154 
2155             return capabilities.isCapable(capability);
2156         } catch (RemoteException e) {
2157             throw new ImsException("queryMmTelCapabilityStatus()", e,
2158                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2159         }
2160     }
2161 
setRttEnabled(boolean enabled)2162     public void setRttEnabled(boolean enabled) {
2163         try {
2164             if (enabled) {
2165                 setEnhanced4gLteModeSetting(enabled);
2166             } else {
2167                 setAdvanced4GMode(enabled || isEnhanced4gLteModeSettingEnabledByUser());
2168             }
2169             setRttConfig(enabled);
2170         } catch (ImsException e) {
2171             loge("Unable to set RTT enabled to " + enabled + ": " + e);
2172         }
2173     }
2174 
2175     /**
2176      * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
2177      */
setTtyMode(int ttyMode)2178     public void setTtyMode(int ttyMode) throws ImsException {
2179         if (!getBooleanCarrierConfig(
2180                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
2181             setAdvanced4GMode((ttyMode == TelecomManager.TTY_MODE_OFF) &&
2182                     isEnhanced4gLteModeSettingEnabledByUser());
2183         }
2184     }
2185 
2186     /**
2187      * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
2188      * settings screen.
2189      * @param uiTtyMode TTY Mode, valid options are:
2190      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
2191      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
2192      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
2193      *         - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
2194      * @param onComplete A Message that will be called by the ImsService when it has completed this
2195      *           operation or null if not waiting for an async response. The Message must contain a
2196      *           valid {@link Message#replyTo} {@link android.os.Messenger}, since it will be passed
2197      *           through Binder to another process.
2198      */
setUiTTYMode(Context context, int uiTtyMode, Message onComplete)2199     public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete)
2200             throws ImsException {
2201 
2202         checkAndThrowExceptionIfServiceUnavailable();
2203 
2204         try {
2205             mMmTelFeatureConnection.setUiTTYMode(uiTtyMode, onComplete);
2206         } catch (RemoteException e) {
2207             throw new ImsException("setTTYMode()", e,
2208                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2209         }
2210     }
2211 
makeACopy(ImsReasonInfo imsReasonInfo)2212     private ImsReasonInfo makeACopy(ImsReasonInfo imsReasonInfo) {
2213         Parcel p = Parcel.obtain();
2214         imsReasonInfo.writeToParcel(p, 0);
2215         p.setDataPosition(0);
2216         ImsReasonInfo clonedReasonInfo = ImsReasonInfo.CREATOR.createFromParcel(p);
2217         p.recycle();
2218         return clonedReasonInfo;
2219     }
2220 
2221     /**
2222      * Get Recent IMS Disconnect Reasons.
2223      *
2224      * @return ArrayList of ImsReasonInfo objects. MAX size of the arraylist
2225      * is MAX_RECENT_DISCONNECT_REASONS. The objects are in the
2226      * chronological order.
2227      */
getRecentImsDisconnectReasons()2228     public ArrayList<ImsReasonInfo> getRecentImsDisconnectReasons() {
2229         ArrayList<ImsReasonInfo> disconnectReasons = new ArrayList<>();
2230 
2231         for (ImsReasonInfo reason : mRecentDisconnectReasons) {
2232             disconnectReasons.add(makeACopy(reason));
2233         }
2234         return disconnectReasons;
2235     }
2236 
2237     @Override
getImsServiceState()2238     public int getImsServiceState() throws ImsException {
2239         return mMmTelFeatureConnection.getFeatureState();
2240     }
2241 
getImsServiceState(Consumer<Integer> result)2242     public void getImsServiceState(Consumer<Integer> result) {
2243         mExecutorFactory.executeRunnable(() -> {
2244             try {
2245                 result.accept(getImsServiceState());
2246             } catch (ImsException e) {
2247                 // In the case that the ImsService is not available, report unavailable.
2248                 result.accept(ImsFeature.STATE_UNAVAILABLE);
2249             }
2250         });
2251     }
2252 
getThreadExecutor()2253     private Executor getThreadExecutor() {
2254         if (Looper.myLooper() == null) {
2255             Looper.prepare();
2256         }
2257         return new HandlerExecutor(new Handler(Looper.myLooper()));
2258     }
2259 
2260     /**
2261      * Get the boolean config from carrier config manager.
2262      *
2263      * @param key config key defined in CarrierConfigManager
2264      * @return boolean value of corresponding key.
2265      */
getBooleanCarrierConfig(String key)2266     private boolean getBooleanCarrierConfig(String key) {
2267         PersistableBundle b = null;
2268         if (mConfigManager != null) {
2269             // If an invalid subId is used, this bundle will contain default values.
2270             b = mConfigManager.getConfigForSubId(getSubId());
2271         }
2272         if (b != null) {
2273             return b.getBoolean(key);
2274         } else {
2275             // Return static default defined in CarrierConfigManager.
2276             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
2277         }
2278     }
2279 
2280     /**
2281      * Get the int config from carrier config manager.
2282      *
2283      * @param key config key defined in CarrierConfigManager
2284      * @return integer value of corresponding key.
2285      */
getIntCarrierConfig(String key)2286     private int getIntCarrierConfig(String key) {
2287         PersistableBundle b = null;
2288         if (mConfigManager != null) {
2289             // If an invalid subId is used, this bundle will contain default values.
2290             b = mConfigManager.getConfigForSubId(getSubId());
2291         }
2292         if (b != null) {
2293             return b.getInt(key);
2294         } else {
2295             // Return static default defined in CarrierConfigManager.
2296             return CarrierConfigManager.getDefaultConfig().getInt(key);
2297         }
2298     }
2299 
2300     /**
2301      * Checks to see if the ImsService Binder is connected. If it is not, we try to create the
2302      * connection again.
2303      */
checkAndThrowExceptionIfServiceUnavailable()2304     private void checkAndThrowExceptionIfServiceUnavailable()
2305             throws ImsException {
2306         if (!isImsSupportedOnDevice(mContext)) {
2307             throw new ImsException("IMS not supported on device.",
2308                     ImsReasonInfo.CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE);
2309         }
2310         if (mMmTelFeatureConnection == null || !mMmTelFeatureConnection.isBinderAlive()) {
2311             createImsService();
2312 
2313             if (mMmTelFeatureConnection == null) {
2314                 throw new ImsException("Service is unavailable",
2315                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2316             }
2317         }
2318     }
2319 
2320     /**
2321      * Creates a connection to the ImsService associated with this slot.
2322      */
createImsService()2323     private void createImsService() {
2324         mMmTelFeatureConnection = MmTelFeatureConnection.create(mContext, mPhoneId);
2325 
2326         // Forwarding interface to tell mStatusCallbacks that the Proxy is unavailable.
2327         mMmTelFeatureConnection.setStatusCallback(new FeatureConnection.IFeatureUpdate() {
2328             @Override
2329             public void notifyStateChanged() {
2330                 mStatusCallbacks.forEach(FeatureConnection.IFeatureUpdate::notifyStateChanged);
2331             }
2332 
2333             @Override
2334             public void notifyUnavailable() {
2335                 mStatusCallbacks.forEach(FeatureConnection.IFeatureUpdate::notifyUnavailable);
2336             }
2337         });
2338     }
2339 
2340     /**
2341      * Creates a {@link ImsCallSession} with the specified call profile.
2342      * Use other methods, if applicable, instead of interacting with
2343      * {@link ImsCallSession} directly.
2344      *
2345      * @param profile a call profile to make the call
2346      */
createCallSession(ImsCallProfile profile)2347     private ImsCallSession createCallSession(ImsCallProfile profile) throws ImsException {
2348         try {
2349             // Throws an exception if the ImsService Feature is not ready to accept commands.
2350             return new ImsCallSession(mMmTelFeatureConnection.createCallSession(profile));
2351         } catch (RemoteException e) {
2352             logw("CreateCallSession: Error, remote exception: " + e.getMessage());
2353             throw new ImsException("createCallSession()", e,
2354                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2355 
2356         }
2357     }
2358 
log(String s)2359     private void log(String s) {
2360         Rlog.d(TAG + " [" + mPhoneId + "]", s);
2361     }
2362 
logi(String s)2363     private void logi(String s) {
2364         Rlog.i(TAG + " [" + mPhoneId + "]", s);
2365     }
2366 
logw(String s)2367     private void logw(String s) {
2368         Rlog.w(TAG + " [" + mPhoneId + "]", s);
2369     }
2370 
loge(String s)2371     private void loge(String s) {
2372         Rlog.e(TAG + " [" + mPhoneId + "]", s);
2373     }
2374 
loge(String s, Throwable t)2375     private void loge(String s, Throwable t) {
2376         Rlog.e(TAG + " [" + mPhoneId + "]", s, t);
2377     }
2378 
2379     /**
2380      * Used for turning on IMS.if its off already
2381      */
turnOnIms()2382     private void turnOnIms() throws ImsException {
2383         TelephonyManager tm = (TelephonyManager)
2384                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
2385         tm.enableIms(mPhoneId);
2386     }
2387 
isImsTurnOffAllowed()2388     private boolean isImsTurnOffAllowed() {
2389         return isTurnOffImsAllowedByPlatform()
2390                 && (!isWfcEnabledByPlatform()
2391                 || !isWfcEnabledByUser());
2392     }
2393 
setLteFeatureValues(boolean turnOn)2394     private void setLteFeatureValues(boolean turnOn) {
2395         log("setLteFeatureValues: " + turnOn);
2396         CapabilityChangeRequest request = new CapabilityChangeRequest();
2397         if (turnOn) {
2398             request.addCapabilitiesToEnableForTech(
2399                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
2400                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
2401         } else {
2402             request.addCapabilitiesToDisableForTech(
2403                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
2404                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
2405         }
2406 
2407         if (isVtEnabledByPlatform()) {
2408             boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(
2409                     CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
2410             boolean enableViLte = turnOn && isVtEnabledByUser() &&
2411                     (ignoreDataEnabledChanged || isDataEnabled());
2412             if (enableViLte) {
2413                 request.addCapabilitiesToEnableForTech(
2414                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
2415                         ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
2416             } else {
2417                 request.addCapabilitiesToDisableForTech(
2418                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
2419                         ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
2420             }
2421         }
2422         try {
2423             mMmTelFeatureConnection.changeEnabledCapabilities(request, null);
2424         } catch (RemoteException e) {
2425             loge("setLteFeatureValues: Exception: " + e.getMessage());
2426         }
2427     }
2428 
setAdvanced4GMode(boolean turnOn)2429     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
2430         checkAndThrowExceptionIfServiceUnavailable();
2431 
2432         // if turnOn: first set feature values then call turnOnIms()
2433         // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
2434         // allowed, first call turnOffIms() then set feature values
2435         if (turnOn) {
2436             setLteFeatureValues(turnOn);
2437             log("setAdvanced4GMode: turnOnIms");
2438             turnOnIms();
2439         } else {
2440             if (isImsTurnOffAllowed()) {
2441                 log("setAdvanced4GMode: turnOffIms");
2442                 turnOffIms();
2443             }
2444             setLteFeatureValues(turnOn);
2445         }
2446     }
2447 
2448     /**
2449      * Used for turning off IMS completely in order to make the device CSFB'ed.
2450      * Once turned off, all calls will be over CS.
2451      */
turnOffIms()2452     private void turnOffIms() throws ImsException {
2453         TelephonyManager tm = (TelephonyManager)
2454                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
2455         tm.disableIms(mPhoneId);
2456     }
2457 
addToRecentDisconnectReasons(ImsReasonInfo reason)2458     private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
2459         if (reason == null) return;
2460         while (mRecentDisconnectReasons.size() >= MAX_RECENT_DISCONNECT_REASONS) {
2461             mRecentDisconnectReasons.removeFirst();
2462         }
2463         mRecentDisconnectReasons.addLast(reason);
2464     }
2465 
2466     /**
2467      * Gets the ECBM interface to request ECBM exit.
2468      *
2469      * @return the ECBM interface instance
2470      * @throws ImsException if getting the ECBM interface results in an error
2471      */
getEcbmInterface()2472     public ImsEcbm getEcbmInterface() throws ImsException {
2473         if (mEcbm != null && mEcbm.isBinderAlive()) {
2474             return mEcbm;
2475         }
2476 
2477         checkAndThrowExceptionIfServiceUnavailable();
2478         try {
2479             IImsEcbm iEcbm = mMmTelFeatureConnection.getEcbmInterface();
2480 
2481             if (iEcbm == null) {
2482                 throw new ImsException("getEcbmInterface()",
2483                         ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
2484             }
2485             mEcbm = new ImsEcbm(iEcbm);
2486         } catch (RemoteException e) {
2487             throw new ImsException("getEcbmInterface()", e,
2488                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2489         }
2490         return mEcbm;
2491     }
2492 
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)2493     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
2494             byte[] pdu) throws ImsException {
2495         try {
2496             mMmTelFeatureConnection.sendSms(token, messageRef, format, smsc, isRetry, pdu);
2497         } catch (RemoteException e) {
2498             throw new ImsException("sendSms()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2499         }
2500     }
2501 
acknowledgeSms(int token, int messageRef, int result)2502     public void acknowledgeSms(int token, int messageRef, int result) throws ImsException {
2503         try {
2504             mMmTelFeatureConnection.acknowledgeSms(token, messageRef, result);
2505         } catch (RemoteException e) {
2506             throw new ImsException("acknowledgeSms()", e,
2507                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2508         }
2509     }
2510 
acknowledgeSmsReport(int token, int messageRef, int result)2511     public void acknowledgeSmsReport(int token, int messageRef, int result) throws  ImsException{
2512         try {
2513             mMmTelFeatureConnection.acknowledgeSmsReport(token, messageRef, result);
2514         } catch (RemoteException e) {
2515             throw new ImsException("acknowledgeSmsReport()", e,
2516                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2517         }
2518     }
2519 
getSmsFormat()2520     public String getSmsFormat() throws ImsException{
2521         try {
2522             return mMmTelFeatureConnection.getSmsFormat();
2523         } catch (RemoteException e) {
2524             throw new ImsException("getSmsFormat()", e,
2525                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2526         }
2527     }
2528 
setSmsListener(IImsSmsListener listener)2529     public void setSmsListener(IImsSmsListener listener) throws ImsException {
2530         try {
2531             mMmTelFeatureConnection.setSmsListener(listener);
2532         } catch (RemoteException e) {
2533             throw new ImsException("setSmsListener()", e,
2534                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2535         }
2536     }
2537 
onSmsReady()2538     public void onSmsReady() throws ImsException {
2539         try {
2540             mMmTelFeatureConnection.onSmsReady();
2541         } catch (RemoteException e) {
2542             throw new ImsException("onSmsReady()", e,
2543                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2544         }
2545     }
2546 
2547     /**
2548      * Determines whether or not a call with the specified numbers should be placed over IMS or over
2549      * CSFB.
2550      * @param isEmergency is at least one call an emergency number.
2551      * @param numbers A {@link String} array containing the numbers in the call being placed. Can
2552      *         be multiple numbers in the case of dialing out a conference.
2553      * @return The result of the query, one of the following values:
2554      *         - {@link MmTelFeature#PROCESS_CALL_IMS}
2555      *         - {@link MmTelFeature#PROCESS_CALL_CSFB}
2556      * @throws ImsException if the ImsService is not available. In this case, we should fall back
2557      * to CSFB anyway.
2558      */
shouldProcessCall(boolean isEmergency, String[] numbers)2559     public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency,
2560             String[] numbers) throws ImsException {
2561         try {
2562             return mMmTelFeatureConnection.shouldProcessCall(isEmergency, numbers);
2563         } catch (RemoteException e) {
2564             throw new ImsException("shouldProcessCall()", e,
2565                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2566         }
2567     }
2568 
2569     /**
2570      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
2571      *
2572      * @return the multi-endpoint interface instance
2573      * @throws ImsException if getting the multi-endpoint interface results in an error
2574      */
getMultiEndpointInterface()2575     public ImsMultiEndpoint getMultiEndpointInterface() throws ImsException {
2576         if (mMultiEndpoint != null && mMultiEndpoint.isBinderAlive()) {
2577             return mMultiEndpoint;
2578         }
2579 
2580         checkAndThrowExceptionIfServiceUnavailable();
2581         try {
2582             IImsMultiEndpoint iImsMultiEndpoint = mMmTelFeatureConnection.getMultiEndpointInterface();
2583 
2584             if (iImsMultiEndpoint == null) {
2585                 throw new ImsException("getMultiEndpointInterface()",
2586                         ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
2587             }
2588             mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
2589         } catch (RemoteException e) {
2590             throw new ImsException("getMultiEndpointInterface()", e,
2591                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2592         }
2593 
2594         return mMultiEndpoint;
2595     }
2596 
2597     /**
2598      * Resets ImsManager settings back to factory defaults.
2599      *
2600      * @deprecated Doesn't support MSIM devices. Use {@link #factoryReset()} instead.
2601      *
2602      * @hide
2603      */
factoryReset(Context context)2604     public static void factoryReset(Context context) {
2605         ImsManager mgr = ImsManager.getInstance(context,
2606                 SubscriptionManager.getDefaultVoicePhoneId());
2607         if (mgr != null) {
2608             mgr.factoryReset();
2609         }
2610         Rlog.e(TAG, "factoryReset: ImsManager null.");
2611     }
2612 
2613     /**
2614      * Resets ImsManager settings back to factory defaults.
2615      *
2616      * @hide
2617      */
factoryReset()2618     public void factoryReset() {
2619         int subId = getSubId();
2620         if (isSubIdValid(subId)) {
2621             // Set VoLTE to default
2622             SubscriptionManager.setSubscriptionProperty(subId,
2623                     SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
2624                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2625 
2626             // Set VoWiFi to default
2627             SubscriptionManager.setSubscriptionProperty(subId,
2628                     SubscriptionManager.WFC_IMS_ENABLED,
2629                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2630 
2631             // Set VoWiFi mode to default
2632             SubscriptionManager.setSubscriptionProperty(subId,
2633                     SubscriptionManager.WFC_IMS_MODE,
2634                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2635 
2636             // Set VoWiFi roaming to default
2637             SubscriptionManager.setSubscriptionProperty(subId,
2638                     SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
2639                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2640 
2641             // Set VoWiFi roaming mode to default
2642             SubscriptionManager.setSubscriptionProperty(subId,
2643                     SubscriptionManager.WFC_IMS_ROAMING_MODE,
2644                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2645 
2646 
2647             // Set VT to default
2648             SubscriptionManager.setSubscriptionProperty(subId,
2649                     SubscriptionManager.VT_IMS_ENABLED,
2650                     Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
2651 
2652             // Set RCS UCE to default
2653             SubscriptionManager.setSubscriptionProperty(subId,
2654                     SubscriptionManager.IMS_RCS_UCE_ENABLED, Integer.toString(
2655                             SUBINFO_PROPERTY_FALSE));
2656         } else {
2657             loge("factoryReset: invalid sub id, can not reset siminfo db settings; subId=" + subId);
2658         }
2659 
2660         // Push settings to ImsConfig
2661         updateImsServiceConfig(true);
2662     }
2663 
setVolteProvisioned(boolean isProvisioned)2664     public void setVolteProvisioned(boolean isProvisioned) {
2665         int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2666                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2667         setProvisionedBoolNoException(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED,
2668                 provisionStatus);
2669     }
2670 
setWfcProvisioned(boolean isProvisioned)2671     public void setWfcProvisioned(boolean isProvisioned) {
2672         int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2673                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2674         setProvisionedBoolNoException(
2675                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED, provisionStatus);
2676     }
2677 
setVtProvisioned(boolean isProvisioned)2678     public void setVtProvisioned(boolean isProvisioned) {
2679         int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2680                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2681         setProvisionedBoolNoException(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED,
2682                 provisionStatus);
2683     }
2684 
setEabProvisioned(boolean isProvisioned)2685     public void setEabProvisioned(boolean isProvisioned) {
2686         int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED :
2687                 ProvisioningManager.PROVISIONING_VALUE_DISABLED;
2688         setProvisionedBoolNoException(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED,
2689                 provisionStatus);
2690     }
2691 
isDataEnabled()2692     private boolean isDataEnabled() {
2693         return new TelephonyManager(mContext, getSubId()).isDataConnectionAllowed();
2694     }
2695 
isVolteProvisioned()2696     private boolean isVolteProvisioned() {
2697         return getProvisionedBoolNoException(
2698                 ImsConfig.ConfigConstants.VLT_SETTING_ENABLED);
2699     }
2700 
isEabProvisioned()2701     private boolean isEabProvisioned() {
2702         return getProvisionedBoolNoException(
2703                 ImsConfig.ConfigConstants.EAB_SETTING_ENABLED);
2704     }
2705 
isWfcProvisioned()2706     private boolean isWfcProvisioned() {
2707         return getProvisionedBoolNoException(
2708                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED);
2709     }
2710 
isVtProvisioned()2711     private boolean isVtProvisioned() {
2712         return getProvisionedBoolNoException(
2713                 ImsConfig.ConfigConstants.LVC_SETTING_ENABLED);
2714     }
2715 
booleanToPropertyString(boolean bool)2716     private static String booleanToPropertyString(boolean bool) {
2717         return bool ? "1" : "0";
2718     }
2719 
2720 
dump(FileDescriptor fd, PrintWriter pw, String[] args)2721     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2722         pw.println("ImsManager:");
2723         pw.println("  device supports IMS = " + isImsSupportedOnDevice(mContext));
2724         pw.println("  mPhoneId = " + mPhoneId);
2725         pw.println("  mConfigUpdated = " + mConfigUpdated);
2726         pw.println("  mImsServiceProxy = " + mMmTelFeatureConnection);
2727         pw.println("  mDataEnabled = " + isDataEnabled());
2728         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(
2729                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
2730 
2731         pw.println("  isGbaValid = " + isGbaValid());
2732         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
2733         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled());
2734 
2735         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatform());
2736         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice());
2737         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
2738                 isEnhanced4gLteModeSettingEnabledByUser());
2739         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatform());
2740         pw.println("  isVtEnabledByUser = " + isVtEnabledByUser());
2741 
2742         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatform());
2743         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUser());
2744         pw.println("  getWfcMode(non-roaming) = " + getWfcMode(false));
2745         pw.println("  getWfcMode(roaming) = " + getWfcMode(true));
2746         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser());
2747 
2748         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDevice());
2749         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice());
2750         pw.flush();
2751     }
2752 
2753     /**
2754      * Determines if a sub id is valid.
2755      * Mimics the logic in SubscriptionController.validateSubId.
2756      * @param subId The sub id to check.
2757      * @return {@code true} if valid, {@code false} otherwise.
2758      */
isSubIdValid(int subId)2759     private boolean isSubIdValid(int subId) {
2760         return SubscriptionManager.isValidSubscriptionId(subId) &&
2761                 subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
2762     }
2763 
isActiveSubscriptionPresent()2764     private boolean isActiveSubscriptionPresent() {
2765         SubscriptionManager sm = (SubscriptionManager) mContext.getSystemService(
2766                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
2767         return sm.getActiveSubscriptionIdList().length > 0;
2768     }
2769 
updateImsCarrierConfigs(PersistableBundle configs)2770     private void updateImsCarrierConfigs(PersistableBundle configs) throws ImsException {
2771         checkAndThrowExceptionIfServiceUnavailable();
2772 
2773         IImsConfig config = mMmTelFeatureConnection.getConfigInterface();
2774         if (config == null) {
2775             throw new ImsException("getConfigInterface()",
2776                     ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
2777         }
2778         try {
2779             config.updateImsCarrierConfigs(configs);
2780         } catch (RemoteException e) {
2781             throw new ImsException("updateImsCarrierConfigs()", e,
2782                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2783         }
2784     }
2785 }
2786