a4f29c6ae9737e27bcfc498766f540ee43f3a5e1
[android/platform/frameworks/opt/net/ims.git] / src / java / com / android / ims / ImsManager.java
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.app.PendingIntent;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.os.IBinder;
25 import android.os.Message;
26 import android.os.Parcel;
27 import android.os.PersistableBundle;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.SystemProperties;
31 import android.provider.Settings;
32 import android.telecom.TelecomManager;
33 import android.telephony.CarrierConfigManager;
34 import android.telephony.Rlog;
35 import android.telephony.ServiceState;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.telephony.ims.ImsServiceProxy;
39 import android.telephony.ims.ImsServiceProxyCompat;
40 import android.telephony.ims.feature.ImsFeature;
41
42 import com.android.ims.internal.IImsCallSession;
43 import com.android.ims.internal.IImsConfig;
44 import com.android.ims.internal.IImsEcbm;
45 import com.android.ims.internal.IImsMultiEndpoint;
46 import com.android.ims.internal.IImsRegistrationListener;
47 import com.android.ims.internal.IImsServiceController;
48 import com.android.ims.internal.IImsUt;
49 import com.android.ims.internal.ImsCallSession;
50 import com.android.ims.internal.IImsConfig;
51 import com.android.internal.annotations.VisibleForTesting;
52
53 import java.io.FileDescriptor;
54 import java.io.PrintWriter;
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.concurrent.ConcurrentLinkedDeque;
58 import java.util.HashSet;
59 import java.util.Optional;
60 import java.util.Set;
61
62 /**
63  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
64  * the operator's IMS network. This class is the starting point for any IMS actions.
65  * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
66  * <p>The APIs in this class allows you to:</p>
67  *
68  * @hide
69  */
70 public class ImsManager {
71
72     /*
73      * Debug flag to override configuration flag
74      */
75     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
76     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
77     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
78     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
79     public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
80     public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
81     public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
82     public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
83
84     /**
85      * For accessing the IMS related service.
86      * Internal use only.
87      * @hide
88      */
89     private static final String IMS_SERVICE = "ims";
90
91     /**
92      * The result code to be sent back with the incoming call {@link PendingIntent}.
93      * @see #open(PendingIntent, ImsConnectionStateListener)
94      */
95     public static final int INCOMING_CALL_RESULT_CODE = 101;
96
97     /**
98      * Key to retrieve the call ID from an incoming call intent.
99      * @see #open(PendingIntent, ImsConnectionStateListener)
100      */
101     public static final String EXTRA_CALL_ID = "android:imsCallID";
102
103     /**
104      * Action to broadcast when ImsService is up.
105      * Internal use only.
106      * @deprecated
107      * @hide
108      */
109     public static final String ACTION_IMS_SERVICE_UP =
110             "com.android.ims.IMS_SERVICE_UP";
111
112     /**
113      * Action to broadcast when ImsService is down.
114      * Internal use only.
115      * @deprecated
116      * @hide
117      */
118     public static final String ACTION_IMS_SERVICE_DOWN =
119             "com.android.ims.IMS_SERVICE_DOWN";
120
121     /**
122      * Action to broadcast when ImsService registration fails.
123      * Internal use only.
124      * @hide
125      */
126     public static final String ACTION_IMS_REGISTRATION_ERROR =
127             "com.android.ims.REGISTRATION_ERROR";
128
129     /**
130      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
131      * A long value; the phone ID corresponding to the IMS service coming up or down.
132      * Internal use only.
133      * @hide
134      */
135     public static final String EXTRA_PHONE_ID = "android:phone_id";
136
137     /**
138      * Action for the incoming call intent for the Phone app.
139      * Internal use only.
140      * @hide
141      */
142     public static final String ACTION_IMS_INCOMING_CALL =
143             "com.android.ims.IMS_INCOMING_CALL";
144
145     /**
146      * Part of the ACTION_IMS_INCOMING_CALL intents.
147      * An integer value; service identifier obtained from {@link ImsManager#open}.
148      * Internal use only.
149      * @hide
150      */
151     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
152
153     /**
154      * Part of the ACTION_IMS_INCOMING_CALL intents.
155      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
156      * The value "true" indicates that the incoming call is for USSD.
157      * Internal use only.
158      * @hide
159      */
160     public static final String EXTRA_USSD = "android:ussd";
161
162     /**
163      * Part of the ACTION_IMS_INCOMING_CALL intents.
164      * A boolean value; Flag to indicate whether the call is an unknown
165      * dialing call. Such calls are originated by sending commands (like
166      * AT commands) directly to modem without Android involvement.
167      * Even though they are not incoming calls, they are propagated
168      * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
169      * Internal use only.
170      * @hide
171      */
172     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
173
174     private static final String TAG = "ImsManager";
175     private static final boolean DBG = true;
176
177     private static HashMap<Integer, ImsManager> sImsManagerInstances =
178             new HashMap<Integer, ImsManager>();
179
180     private Context mContext;
181     private CarrierConfigManager mConfigManager;
182     private int mPhoneId;
183     private final boolean mConfigDynamicBind;
184     private ImsServiceProxyCompat mImsServiceProxy = null;
185     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
186     // Ut interface for the supplementary service configuration
187     private ImsUt mUt = null;
188     // Interface to get/set ims config items
189     private ImsConfig mConfig = null;
190     private boolean mConfigUpdated = false;
191
192     private ImsConfigListener mImsConfigListener;
193
194     // ECBM interface
195     private ImsEcbm mEcbm = null;
196
197     private ImsMultiEndpoint mMultiEndpoint = null;
198
199     private Set<ImsServiceProxy.INotifyStatusChanged> mStatusCallbacks = new HashSet<>();
200
201     // Keep track of the ImsRegistrationListenerProxys that have been created so that we can
202     // remove them from the ImsService.
203     private Set<ImsRegistrationListenerProxy> mRegistrationListeners = new HashSet<>();
204
205     // SystemProperties used as cache
206     private static final String VOLTE_PROVISIONED_PROP = "net.lte.ims.volte.provisioned";
207     private static final String WFC_PROVISIONED_PROP = "net.lte.ims.wfc.provisioned";
208     private static final String VT_PROVISIONED_PROP = "net.lte.ims.vt.provisioned";
209     // Flag indicating data enabled or not. This flag should be in sync with
210     // DcTracker.isDataEnabled(). The flag will be set later during boot up.
211     private static final String DATA_ENABLED_PROP = "net.lte.ims.data.enabled";
212
213     public static final String TRUE = "true";
214     public static final String FALSE = "false";
215
216     // mRecentDisconnectReasons stores the last 16 disconnect reasons
217     private static final int MAX_RECENT_DISCONNECT_REASONS = 16;
218     private ConcurrentLinkedDeque<ImsReasonInfo> mRecentDisconnectReasons =
219             new ConcurrentLinkedDeque<>();
220
221     /**
222      * Gets a manager instance.
223      *
224      * @param context application context for creating the manager object
225      * @param phoneId the phone ID for the IMS Service
226      * @return the manager instance corresponding to the phoneId
227      */
228     public static ImsManager getInstance(Context context, int phoneId) {
229         synchronized (sImsManagerInstances) {
230             if (sImsManagerInstances.containsKey(phoneId)) {
231                 return sImsManagerInstances.get(phoneId);
232             }
233
234             ImsManager mgr = new ImsManager(context, phoneId);
235             sImsManagerInstances.put(phoneId, mgr);
236
237             return mgr;
238         }
239     }
240
241     /**
242      * Returns the user configuration of Enhanced 4G LTE Mode setting.
243      *
244      * @deprecated Doesn't support MSIM devices. Use
245      * {@link #isEnhanced4gLteModeSettingEnabledByUserForSlot} instead.
246      */
247     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
248         // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
249         // If user changes SIM from editable mode to uneditable mode, need to return true.
250         if (!getBooleanCarrierConfig(context,
251                     CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
252             return true;
253         }
254         int enabled = android.provider.Settings.Global.getInt(
255                 context.getContentResolver(),
256                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
257                 ImsConfig.FeatureValueConstants.ON);
258         return (enabled == 1) ? true : false;
259     }
260
261     /**
262      * Returns the user configuration of Enhanced 4G LTE Mode setting for slot.
263      */
264     public boolean isEnhanced4gLteModeSettingEnabledByUserForSlot() {
265         // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
266         // If user changes SIM from editable mode to uneditable mode, need to return true.
267         if (!getBooleanCarrierConfigForSlot(
268                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
269             return true;
270         }
271         int enabled = android.provider.Settings.Global.getInt(
272                 mContext.getContentResolver(),
273                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
274                 ImsConfig.FeatureValueConstants.ON);
275         return (enabled == 1);
276     }
277
278     /**
279      * Change persistent Enhanced 4G LTE Mode setting.
280      *
281      * @deprecated Doesn't support MSIM devices. Use {@link #setEnhanced4gLteModeSettingForSlot}
282      * instead.
283      */
284     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
285         int value = enabled ? 1 : 0;
286         android.provider.Settings.Global.putInt(
287                 context.getContentResolver(),
288                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
289
290         if (isNonTtyOrTtyOnVolteEnabled(context)) {
291             ImsManager imsManager = ImsManager.getInstance(context,
292                     SubscriptionManager.getDefaultVoicePhoneId());
293             if (imsManager != null) {
294                 try {
295                     imsManager.setAdvanced4GMode(enabled);
296                 } catch (ImsException ie) {
297                     // do nothing
298                 }
299             }
300         }
301     }
302
303     /**
304      * Change persistent Enhanced 4G LTE Mode setting. If the the option is not editable
305      * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), this method will
306      * always set the setting to true.
307      *
308      */
309     public void setEnhanced4gLteModeSettingForSlot(boolean enabled) {
310         // If false, we must always keep advanced 4G mode set to true (1).
311         int value = getBooleanCarrierConfigForSlot(
312                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) ? (enabled ? 1: 0) : 1;
313
314         try {
315             int prevSetting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
316                     android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED);
317             if (prevSetting == value) {
318                 // Don't trigger setAdvanced4GMode if the setting hasn't changed.
319                 return;
320             }
321         } catch (Settings.SettingNotFoundException e) {
322             // Setting doesn't exist yet, so set it below.
323         }
324
325         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
326                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
327         if (isNonTtyOrTtyOnVolteEnabledForSlot()) {
328             try {
329                 setAdvanced4GMode(enabled);
330             } catch (ImsException ie) {
331                 // do nothing
332             }
333         }
334     }
335
336     /**
337      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
338      * supported.
339      * @deprecated Does not support MSIM devices. Please use
340      * {@link #isNonTtyOrTtyOnVolteEnabledForSlot} instead.
341      */
342     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
343         if (getBooleanCarrierConfig(context,
344                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
345             return true;
346         }
347
348         return Settings.Secure.getInt(context.getContentResolver(),
349                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
350                 == TelecomManager.TTY_MODE_OFF;
351     }
352
353     /**
354      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
355      * supported on a per slot basis.
356      */
357     public boolean isNonTtyOrTtyOnVolteEnabledForSlot() {
358         if (getBooleanCarrierConfigForSlot(
359                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
360             return true;
361         }
362
363         return Settings.Secure.getInt(mContext.getContentResolver(),
364                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
365                 == TelecomManager.TTY_MODE_OFF;
366     }
367
368     /**
369      * Returns a platform configuration for VoLTE which may override the user setting.
370      * @deprecated Does not support MSIM devices. Please use
371      * {@link #isVolteEnabledByPlatformForSlot()} instead.
372      */
373     public static boolean isVolteEnabledByPlatform(Context context) {
374         if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
375                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
376             return true;
377         }
378
379         return context.getResources().getBoolean(
380                 com.android.internal.R.bool.config_device_volte_available)
381                 && getBooleanCarrierConfig(context,
382                         CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
383                 && isGbaValid(context);
384     }
385
386     /**
387      * Returns a platform configuration for VoLTE which may override the user setting on a per Slot
388      * basis.
389      */
390     public boolean isVolteEnabledByPlatformForSlot() {
391         if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
392                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
393             return true;
394         }
395
396         return mContext.getResources().getBoolean(
397                 com.android.internal.R.bool.config_device_volte_available)
398                 && getBooleanCarrierConfigForSlot(
399                         CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
400                 && isGbaValidForSlot();
401     }
402
403     /**
404      * Indicates whether VoLTE is provisioned on device.
405      *
406      * @deprecated Does not support MSIM devices. Please use
407      * {@link #isVolteProvisionedOnDeviceForSlot()} instead.
408      */
409     public static boolean isVolteProvisionedOnDevice(Context context) {
410         if (getBooleanCarrierConfig(context,
411                     CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
412             ImsManager mgr = ImsManager.getInstance(context,
413                     SubscriptionManager.getDefaultVoicePhoneId());
414             if (mgr != null) {
415                 return mgr.isVolteProvisioned();
416             }
417         }
418
419         return true;
420     }
421
422     /**
423      * Indicates whether VoLTE is provisioned on this slot.
424      */
425     public boolean isVolteProvisionedOnDeviceForSlot() {
426         if (getBooleanCarrierConfigForSlot(
427                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
428             return isVolteProvisioned();
429         }
430
431         return true;
432     }
433
434     /**
435      * Indicates whether VoWifi is provisioned on device
436      *
437      * @deprecated Does not support MSIM devices. Please use
438      * {@link #isWfcProvisionedOnDeviceForSlot()} instead.
439      */
440     public static boolean isWfcProvisionedOnDevice(Context context) {
441         if (getBooleanCarrierConfig(context,
442                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
443             ImsManager mgr = ImsManager.getInstance(context,
444                     SubscriptionManager.getDefaultVoicePhoneId());
445             if (mgr != null) {
446                 return mgr.isWfcProvisioned();
447             }
448         }
449
450         return true;
451     }
452
453     /**
454      * Indicates whether VoWifi is provisioned on slot.
455      */
456     public boolean isWfcProvisionedOnDeviceForSlot() {
457         if (getBooleanCarrierConfigForSlot(
458                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
459                 return isWfcProvisioned();
460         }
461
462         return true;
463     }
464
465     /**
466      * Indicates whether VT is provisioned on device
467      *
468      * @deprecated Does not support MSIM devices. Please use
469      * {@link #isVtProvisionedOnDeviceForSlot()} instead.
470      */
471     public static boolean isVtProvisionedOnDevice(Context context) {
472         if (getBooleanCarrierConfig(context,
473                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
474             ImsManager mgr = ImsManager.getInstance(context,
475                     SubscriptionManager.getDefaultVoicePhoneId());
476             if (mgr != null) {
477                 return mgr.isVtProvisioned();
478             }
479         }
480
481         return true;
482     }
483
484     /**
485      * Indicates whether VT is provisioned on slot.
486      */
487     public boolean isVtProvisionedOnDeviceForSlot() {
488         if (getBooleanCarrierConfigForSlot(
489                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
490             return isVtProvisioned();
491         }
492
493         return true;
494     }
495
496     /**
497      * Returns a platform configuration for VT which may override the user setting.
498      *
499      * Note: VT presumes that VoLTE is enabled (these are configuration settings
500      * which must be done correctly).
501      *
502      * @deprecated Does not support MSIM devices. Please use
503      * {@link #isVtEnabledByPlatformForSlot()} instead.
504      */
505     public static boolean isVtEnabledByPlatform(Context context) {
506         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
507                 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
508             return true;
509         }
510
511         return
512                 context.getResources().getBoolean(
513                         com.android.internal.R.bool.config_device_vt_available) &&
514                 getBooleanCarrierConfig(context,
515                         CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
516                 isGbaValid(context);
517     }
518
519     /**
520      * Returns a platform configuration for VT which may override the user setting.
521      *
522      * Note: VT presumes that VoLTE is enabled (these are configuration settings
523      * which must be done correctly).
524      */
525     public boolean isVtEnabledByPlatformForSlot() {
526         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
527                 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
528             return true;
529         }
530
531         return mContext.getResources().getBoolean(
532                 com.android.internal.R.bool.config_device_vt_available) &&
533                 getBooleanCarrierConfigForSlot(
534                         CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
535                 isGbaValidForSlot();
536     }
537
538     /**
539      * Returns the user configuration of VT setting
540      * @deprecated Does not support MSIM devices. Please use
541      * {@link #isVtEnabledByUserForSlot()} instead.
542      */
543     public static boolean isVtEnabledByUser(Context context) {
544         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
545                 android.provider.Settings.Global.VT_IMS_ENABLED,
546                 ImsConfig.FeatureValueConstants.ON);
547         return (enabled == 1) ? true : false;
548     }
549
550     /**
551      * Returns the user configuration of VT setting per slot.
552      */
553     public boolean isVtEnabledByUserForSlot() {
554         int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
555                 android.provider.Settings.Global.VT_IMS_ENABLED,
556                 ImsConfig.FeatureValueConstants.ON);
557         return (enabled == 1);
558     }
559
560     /**
561      * Change persistent VT enabled setting
562      *
563      * @deprecated Does not support MSIM devices. Please use
564      * {@link #setVtSettingForSlot} instead.
565      */
566     public static void setVtSetting(Context context, boolean enabled) {
567         int value = enabled ? 1 : 0;
568         android.provider.Settings.Global.putInt(context.getContentResolver(),
569                 android.provider.Settings.Global.VT_IMS_ENABLED, value);
570
571         ImsManager imsManager = ImsManager.getInstance(context,
572                 SubscriptionManager.getDefaultVoicePhoneId());
573         if (imsManager != null) {
574             try {
575                 ImsConfig config = imsManager.getConfigInterface();
576                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
577                         TelephonyManager.NETWORK_TYPE_LTE,
578                         enabled ? ImsConfig.FeatureValueConstants.ON
579                                 : ImsConfig.FeatureValueConstants.OFF,
580                         imsManager.mImsConfigListener);
581
582                 if (enabled) {
583                     log("setVtSetting() : turnOnIms");
584                     imsManager.turnOnIms();
585                 } else if (isTurnOffImsAllowedByPlatform(context)
586                         && (!isVolteEnabledByPlatform(context)
587                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
588                     log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
589                     imsManager.turnOffIms();
590                 }
591             } catch (ImsException e) {
592                 loge("setVtSetting(): ", e);
593             }
594         }
595     }
596
597     /**
598      * Change persistent VT enabled setting for slot.
599      */
600     public void setVtSettingForSlot(boolean enabled) {
601         int value = enabled ? 1 : 0;
602         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
603                 android.provider.Settings.Global.VT_IMS_ENABLED, value);
604
605         try {
606             ImsConfig config = getConfigInterface();
607             config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
608                     TelephonyManager.NETWORK_TYPE_LTE,
609                     enabled ? ImsConfig.FeatureValueConstants.ON
610                             : ImsConfig.FeatureValueConstants.OFF,
611                     mImsConfigListener);
612
613             if (enabled) {
614                 log("setVtSettingForSlot() : turnOnIms");
615                 turnOnIms();
616             } else if (isVolteEnabledByPlatformForSlot()
617                     && (!isVolteEnabledByPlatformForSlot()
618                     || !isEnhanced4gLteModeSettingEnabledByUserForSlot())) {
619                 log("setVtSettingForSlot() : imsServiceAllowTurnOff -> turnOffIms");
620                 turnOffIms();
621             }
622         } catch (ImsException e) {
623             loge("setVtSettingForSlot(): ", e);
624         }
625     }
626
627     /**
628      * Returns whether turning off ims is allowed by platform.
629      * The platform property may override the carrier config.
630      *
631      * @deprecated Does not support MSIM devices. Please use
632      * {@link #isTurnOffImsAllowedByPlatformForSlot} instead.
633      */
634     private static boolean isTurnOffImsAllowedByPlatform(Context context) {
635         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
636                 PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
637             return true;
638         }
639         return getBooleanCarrierConfig(context,
640                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
641     }
642
643     /**
644      * Returns whether turning off ims is allowed by platform.
645      * The platform property may override the carrier config.
646      */
647     private boolean isTurnOffImsAllowedByPlatformForSlot() {
648         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
649                 PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
650             return true;
651         }
652         return getBooleanCarrierConfigForSlot(
653                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
654     }
655
656     /**
657      * Returns the user configuration of WFC setting
658      *
659      * @deprecated Does not support MSIM devices. Please use
660      * {@link #isTurnOffImsAllowedByPlatformForSlot} instead.
661      */
662     public static boolean isWfcEnabledByUser(Context context) {
663         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
664                 android.provider.Settings.Global.WFC_IMS_ENABLED,
665                 getBooleanCarrierConfig(context,
666                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
667                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
668         return (enabled == 1) ? true : false;
669     }
670
671     /**
672      * Returns the user configuration of WFC setting for slot.
673      */
674     public boolean isWfcEnabledByUserForSlot() {
675         int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
676                 android.provider.Settings.Global.WFC_IMS_ENABLED,
677                 getBooleanCarrierConfigForSlot(
678                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
679                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
680         return enabled == 1;
681     }
682
683     /**
684      * Change persistent WFC enabled setting.
685      * @deprecated Does not support MSIM devices. Please use
686      * {@link #setWfcSettingForSlot} instead.
687      */
688     public static void setWfcSetting(Context context, boolean enabled) {
689         int value = enabled ? 1 : 0;
690         android.provider.Settings.Global.putInt(context.getContentResolver(),
691                 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
692
693         ImsManager imsManager = ImsManager.getInstance(context,
694                 SubscriptionManager.getDefaultVoicePhoneId());
695         if (imsManager != null) {
696             try {
697                 ImsConfig config = imsManager.getConfigInterface();
698                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
699                         TelephonyManager.NETWORK_TYPE_IWLAN,
700                         enabled ? ImsConfig.FeatureValueConstants.ON
701                                 : ImsConfig.FeatureValueConstants.OFF,
702                         imsManager.mImsConfigListener);
703
704                 if (enabled) {
705                     log("setWfcSetting() : turnOnIms");
706                     imsManager.turnOnIms();
707                 } else if (isTurnOffImsAllowedByPlatform(context)
708                         && (!isVolteEnabledByPlatform(context)
709                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
710                     log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
711                     imsManager.turnOffIms();
712                 }
713
714                 TelephonyManager tm = (TelephonyManager) context
715                         .getSystemService(Context.TELEPHONY_SERVICE);
716                 setWfcModeInternal(context, enabled
717                         // Choose wfc mode per current roaming preference
718                         ? getWfcMode(context, tm.isNetworkRoaming())
719                         // Force IMS to register over LTE when turning off WFC
720                         : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
721             } catch (ImsException e) {
722                 loge("setWfcSetting(): ", e);
723             }
724         }
725     }
726
727     /**
728      * Change persistent WFC enabled setting for slot.
729      */
730     public void setWfcSettingForSlot(boolean enabled) {
731         int value = enabled ? 1 : 0;
732         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
733                 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
734
735         try {
736             ImsConfig config = getConfigInterface();
737             config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
738                     TelephonyManager.NETWORK_TYPE_IWLAN,
739                     enabled ? ImsConfig.FeatureValueConstants.ON
740                             : ImsConfig.FeatureValueConstants.OFF,
741                     mImsConfigListener);
742
743             if (enabled) {
744                 log("setWfcSettingForSlot() : turnOnIms");
745                 turnOnIms();
746             } else if (isTurnOffImsAllowedByPlatformForSlot()
747                     && (!isVolteEnabledByPlatformForSlot()
748                     || !isEnhanced4gLteModeSettingEnabledByUserForSlot())) {
749                 log("setWfcSettingForSlot() : imsServiceAllowTurnOff -> turnOffIms");
750                 turnOffIms();
751             }
752
753             // Force IMS to register over LTE when turning off WFC
754             setWfcModeInternalForSlot(enabled
755                     ? getWfcModeForSlot()
756                     : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
757         } catch (ImsException e) {
758             loge("setWfcSettingForSlot(): ", e);
759         }
760     }
761
762     /**
763      * Returns the user configuration of WFC preference setting.
764      *
765      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcModeForSlot} instead.
766      */
767     public static int getWfcMode(Context context) {
768         int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
769                 android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
770                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
771         if (DBG) log("getWfcMode - setting=" + setting);
772         return setting;
773     }
774
775     /**
776      * Returns the user configuration of WFC preference setting
777      */
778     public int getWfcModeForSlot() {
779         int setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
780                 android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfigForSlot(
781                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
782         if (DBG) log("getWfcMode - setting=" + setting);
783         return setting;
784     }
785
786     /**
787      * Change persistent WFC preference setting.
788      *
789      * @deprecated Doesn't support MSIM devices. Use {@link #setWfcModeForSlot} instead.
790      */
791     public static void setWfcMode(Context context, int wfcMode) {
792         if (DBG) log("setWfcMode - setting=" + wfcMode);
793         android.provider.Settings.Global.putInt(context.getContentResolver(),
794                 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
795
796         setWfcModeInternal(context, wfcMode);
797     }
798
799     /**
800      * Change persistent WFC preference setting for slot.
801      */
802     public void setWfcModeForSlot(int wfcMode) {
803         if (DBG) log("setWfcModeForSlot - setting=" + wfcMode);
804         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
805                 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
806
807         setWfcModeInternalForSlot(wfcMode);
808     }
809
810     /**
811      * Returns the user configuration of WFC preference setting
812      *
813      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
814      *
815      * @deprecated Doesn't support MSIM devices. Use {@link #getWfcModeForSlot} instead.
816      */
817     public static int getWfcMode(Context context, boolean roaming) {
818         int setting = 0;
819         if (!roaming) {
820             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
821                     android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
822                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
823             if (DBG) log("getWfcMode - setting=" + setting);
824         } else {
825             setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
826                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
827                     getIntCarrierConfig(context,
828                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
829             if (DBG) log("getWfcMode (roaming) - setting=" + setting);
830         }
831         return setting;
832     }
833
834     /**
835      * Returns the user configuration of WFC preference setting for slot
836      *
837      * @param roaming {@code false} for home network setting, {@code true} for roaming  setting
838      */
839     public int getWfcModeForSlot(boolean roaming) {
840         int setting = 0;
841         if (!roaming) {
842             setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
843                     android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfigForSlot(
844                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
845             if (DBG) log("getWfcModeForSlot - setting=" + setting);
846         } else {
847             setting = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
848                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE,
849                     getIntCarrierConfigForSlot(
850                             CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
851             if (DBG) log("getWfcModeForSlot (roaming) - setting=" + setting);
852         }
853         return setting;
854     }
855
856     /**
857      * Change persistent WFC preference setting
858      *
859      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
860      *
861      * @deprecated Doesn't support MSIM devices. Please use {@link #setWfcModeForSlot} instead.
862      */
863     public static void setWfcMode(Context context, int wfcMode, boolean roaming) {
864         if (!roaming) {
865             if (DBG) log("setWfcMode - setting=" + wfcMode);
866             android.provider.Settings.Global.putInt(context.getContentResolver(),
867                     android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
868         } else {
869             if (DBG) log("setWfcMode (roaming) - setting=" + wfcMode);
870             android.provider.Settings.Global.putInt(context.getContentResolver(),
871                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
872         }
873
874         TelephonyManager tm = (TelephonyManager)
875                 context.getSystemService(Context.TELEPHONY_SERVICE);
876         if (roaming == tm.isNetworkRoaming()) {
877             setWfcModeInternal(context, wfcMode);
878         }
879     }
880
881     /**
882      * Change persistent WFC preference setting
883      *
884      * @param roaming {@code false} for home network setting, {@code true} for roaming setting
885      */
886     public void setWfcModeForSlot(int wfcMode, boolean roaming) {
887         if (!roaming) {
888             if (DBG) log("setWfcModeForSlot - setting=" + wfcMode);
889             android.provider.Settings.Global.putInt(mContext.getContentResolver(),
890                     android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
891         } else {
892             if (DBG) log("setWfcModeForSlot (roaming) - setting=" + wfcMode);
893             android.provider.Settings.Global.putInt(mContext.getContentResolver(),
894                     android.provider.Settings.Global.WFC_IMS_ROAMING_MODE, wfcMode);
895         }
896
897         int[] subIds = SubscriptionManager.getSubId(mPhoneId);
898         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
899         if (subIds != null && subIds.length >= 1) {
900             subId = subIds[0];
901         }
902         TelephonyManager tm = (TelephonyManager)
903                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
904         if (roaming == tm.isNetworkRoaming(subId)) {
905             setWfcModeInternalForSlot(wfcMode);
906         }
907     }
908
909     private static void setWfcModeInternal(Context context, int wfcMode) {
910         final ImsManager imsManager = ImsManager.getInstance(context,
911                 SubscriptionManager.getDefaultVoicePhoneId());
912         if (imsManager != null) {
913             final int value = wfcMode;
914             Thread thread = new Thread(new Runnable() {
915                 public void run() {
916                     try {
917                         imsManager.getConfigInterface().setProvisionedValue(
918                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
919                                 value);
920                     } catch (ImsException e) {
921                         // do nothing
922                     }
923                 }
924             });
925             thread.start();
926         }
927     }
928
929     private void setWfcModeInternalForSlot(int wfcMode) {
930         final int value = wfcMode;
931         Thread thread = new Thread(() -> {
932                 try {
933                     getConfigInterface().setProvisionedValue(
934                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
935                             value);
936                 } catch (ImsException e) {
937                     // do nothing
938                 }
939         });
940         thread.start();
941     }
942
943     /**
944      * Returns the user configuration of WFC roaming setting
945      *
946      * @deprecated Does not support MSIM devices. Please use
947      * {@link #isWfcRoamingEnabledByUserForSlot} instead.
948      */
949     public static boolean isWfcRoamingEnabledByUser(Context context) {
950         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
951                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
952                 getBooleanCarrierConfig(context,
953                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
954                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
955         return (enabled == 1) ? true : false;
956     }
957
958     /**
959      * Returns the user configuration of WFC roaming setting for slot
960      */
961     public boolean isWfcRoamingEnabledByUserForSlot() {
962         int enabled = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
963                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
964                 getBooleanCarrierConfigForSlot(
965                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
966                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
967         return (enabled == 1);
968     }
969
970     /**
971      * Change persistent WFC roaming enabled setting
972      */
973     public static void setWfcRoamingSetting(Context context, boolean enabled) {
974         android.provider.Settings.Global.putInt(context.getContentResolver(),
975                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
976                 enabled ? ImsConfig.FeatureValueConstants.ON
977                         : ImsConfig.FeatureValueConstants.OFF);
978
979         final ImsManager imsManager = ImsManager.getInstance(context,
980                 SubscriptionManager.getDefaultVoicePhoneId());
981         if (imsManager != null) {
982             imsManager.setWfcRoamingSettingInternal(enabled);
983         }
984     }
985
986     /**
987      * Change persistent WFC roaming enabled setting
988      */
989     public void setWfcRoamingSettingForSlot(boolean enabled) {
990         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
991                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
992                 enabled ? ImsConfig.FeatureValueConstants.ON
993                         : ImsConfig.FeatureValueConstants.OFF);
994
995         setWfcRoamingSettingInternal(enabled);
996     }
997
998     private void setWfcRoamingSettingInternal(boolean enabled) {
999         final int value = enabled
1000                 ? ImsConfig.FeatureValueConstants.ON
1001                 : ImsConfig.FeatureValueConstants.OFF;
1002         Thread thread = new Thread(() -> {
1003                 try {
1004                     getConfigInterface().setProvisionedValue(
1005                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
1006                             value);
1007                 } catch (ImsException e) {
1008                     // do nothing
1009                 }
1010         });
1011         thread.start();
1012     }
1013
1014     /**
1015      * Returns a platform configuration for WFC which may override the user
1016      * setting. Note: WFC presumes that VoLTE is enabled (these are
1017      * configuration settings which must be done correctly).
1018      *
1019      * @deprecated Doesn't work for MSIM devices. Use {@link #isWfcEnabledByPlatformForSlot}
1020      * instead.
1021      */
1022     public static boolean isWfcEnabledByPlatform(Context context) {
1023         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
1024                 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
1025             return true;
1026         }
1027
1028         return
1029                context.getResources().getBoolean(
1030                        com.android.internal.R.bool.config_device_wfc_ims_available) &&
1031                getBooleanCarrierConfig(context,
1032                        CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1033                isGbaValid(context);
1034     }
1035
1036     /**
1037      * Returns a platform configuration for WFC which may override the user
1038      * setting per slot. Note: WFC presumes that VoLTE is enabled (these are
1039      * configuration settings which must be done correctly).
1040      */
1041     public boolean isWfcEnabledByPlatformForSlot() {
1042         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
1043                 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
1044             return true;
1045         }
1046
1047         return mContext.getResources().getBoolean(
1048                 com.android.internal.R.bool.config_device_wfc_ims_available) &&
1049                 getBooleanCarrierConfigForSlot(
1050                         CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
1051                 isGbaValidForSlot();
1052     }
1053
1054     /**
1055      * If carrier requires that IMS is only available if GBA capable SIM is used,
1056      * then this function checks GBA bit in EF IST.
1057      *
1058      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1059      *
1060      * @deprecated Use {@link #isGbaValidForSlot} instead
1061      */
1062     private static boolean isGbaValid(Context context) {
1063         if (getBooleanCarrierConfig(context,
1064                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1065             final TelephonyManager telephonyManager = TelephonyManager.getDefault();
1066             String efIst = telephonyManager.getIsimIst();
1067             if (efIst == null) {
1068                 loge("ISF is NULL");
1069                 return true;
1070             }
1071             boolean result = efIst != null && efIst.length() > 1 &&
1072                     (0x02 & (byte)efIst.charAt(1)) != 0;
1073             if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
1074             return result;
1075         }
1076         return true;
1077     }
1078
1079     /**
1080      * If carrier requires that IMS is only available if GBA capable SIM is used,
1081      * then this function checks GBA bit in EF IST.
1082      *
1083      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
1084      */
1085     private boolean isGbaValidForSlot() {
1086         if (getBooleanCarrierConfigForSlot(
1087                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
1088             final TelephonyManager telephonyManager = TelephonyManager.getDefault();
1089             String efIst = telephonyManager.getIsimIst();
1090             if (efIst == null) {
1091                 loge("isGbaValidForSlot - ISF is NULL");
1092                 return true;
1093             }
1094             boolean result = efIst != null && efIst.length() > 1 &&
1095                     (0x02 & (byte)efIst.charAt(1)) != 0;
1096             if (DBG) log("isGbaValidForSlot - GBA capable=" + result + ", ISF=" + efIst);
1097             return result;
1098         }
1099         return true;
1100     }
1101
1102     /**
1103      * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
1104      *
1105      * We cannot register receiver in ImsManager because this would lead to resource leak.
1106      * ImsManager can be created in different processes and it is not notified when that process
1107      * is about to be terminated.
1108      *
1109      * @hide
1110      * */
1111     public static void onProvisionedValueChanged(Context context, int item, String value) {
1112         if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
1113         ImsManager mgr = ImsManager.getInstance(context,
1114                 SubscriptionManager.getDefaultVoicePhoneId());
1115
1116         switch (item) {
1117             case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
1118                 mgr.setVolteProvisionedProperty(value.equals("1"));
1119                 if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
1120                 break;
1121
1122             case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
1123                 mgr.setWfcProvisionedProperty(value.equals("1"));
1124                 if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
1125                 break;
1126
1127             case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
1128                 mgr.setVtProvisionedProperty(value.equals("1"));
1129                 if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
1130                 break;
1131
1132         }
1133     }
1134
1135     private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Void> {
1136         @Override
1137         protected Void doInBackground(Void... params) {
1138             // disable on any error
1139             setVolteProvisionedProperty(false);
1140             setWfcProvisionedProperty(false);
1141             setVtProvisionedProperty(false);
1142
1143             try {
1144                 ImsConfig config = getConfigInterface();
1145                 if (config != null) {
1146                     setVolteProvisionedProperty(getProvisionedBool(config,
1147                             ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
1148                     if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
1149
1150                     setWfcProvisionedProperty(getProvisionedBool(config,
1151                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
1152                     if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
1153
1154                     setVtProvisionedProperty(getProvisionedBool(config,
1155                             ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
1156                     if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
1157
1158                 }
1159             } catch (ImsException ie) {
1160                 Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
1161             }
1162
1163             return null;
1164         }
1165
1166         private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
1167             return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
1168         }
1169     }
1170
1171     /** Asynchronously get VoLTE, WFC, VT provisioning statuses */
1172     private void updateProvisionedValues() {
1173         if (getBooleanCarrierConfigForSlot(
1174                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
1175
1176             new AsyncUpdateProvisionedValues().execute();
1177         }
1178     }
1179
1180     /**
1181      * Sync carrier config and user settings with ImsConfig.
1182      *
1183      * @param context for the manager object
1184      * @param phoneId phone id
1185      * @param force update
1186      *
1187      * @deprecated Doesn't support MSIM devices. Use {@link #updateImsServiceConfigForSlot} instead.
1188      */
1189     public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
1190         if (!force) {
1191             if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
1192                 log("updateImsServiceConfig: SIM not ready");
1193                 // Don't disable IMS if SIM is not ready
1194                 return;
1195             }
1196         }
1197
1198         final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
1199         if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
1200             try {
1201                 imsManager.updateProvisionedValues();
1202
1203                 // TODO: Extend ImsConfig API and set all feature values in single function call.
1204
1205                 // Note: currently the order of updates is set to produce different order of
1206                 // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
1207                 // differentiate this code path from vendor code perspective.
1208                 boolean isImsUsed = imsManager.updateVolteFeatureValue();
1209                 isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
1210                 isImsUsed |= imsManager.updateVideoCallFeatureValue();
1211
1212                 if (isImsUsed || !isTurnOffImsAllowedByPlatform(context)) {
1213                     // Turn on IMS if it is used.
1214                     // Also, if turning off is not allowed for current carrier,
1215                     // we need to turn IMS on because it might be turned off before
1216                     // phone switched to current carrier.
1217                     log("updateImsServiceConfig: turnOnIms");
1218                     imsManager.turnOnIms();
1219                 } else {
1220                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
1221                     log("updateImsServiceConfig: turnOffIms");
1222                     imsManager.turnOffIms();
1223                 }
1224
1225                 imsManager.mConfigUpdated = true;
1226             } catch (ImsException e) {
1227                 loge("updateImsServiceConfig: ", e);
1228                 imsManager.mConfigUpdated = false;
1229             }
1230         }
1231     }
1232
1233     /**
1234      * Sync carrier config and user settings with ImsConfig.
1235      *
1236      * @param context for the manager object
1237      * @param phoneId phone id
1238      * @param force update
1239      */
1240     public void updateImsServiceConfigForSlot(boolean force) {
1241         if (!force) {
1242             if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
1243                 log("updateImsServiceConfigForSlot: SIM not ready");
1244                 // Don't disable IMS if SIM is not ready
1245                 return;
1246             }
1247         }
1248
1249         if (!mConfigUpdated || force) {
1250             try {
1251                 updateProvisionedValues();
1252
1253                 // TODO: Extend ImsConfig API and set all feature values in single function call.
1254
1255                 // Note: currently the order of updates is set to produce different order of
1256                 // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
1257                 // differentiate this code path from vendor code perspective.
1258                 boolean isImsUsed = updateVolteFeatureValue();
1259                 isImsUsed |= updateWfcFeatureAndProvisionedValues();
1260                 isImsUsed |= updateVideoCallFeatureValue();
1261
1262                 if (isImsUsed || !isTurnOffImsAllowedByPlatformForSlot()) {
1263                     // Turn on IMS if it is used.
1264                     // Also, if turning off is not allowed for current carrier,
1265                     // we need to turn IMS on because it might be turned off before
1266                     // phone switched to current carrier.
1267                     log("updateImsServiceConfigForSlot: turnOnIms");
1268                     turnOnIms();
1269                 } else {
1270                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
1271                     log("updateImsServiceConfigForSlot: turnOffIms");
1272                     turnOffIms();
1273                 }
1274
1275                 mConfigUpdated = true;
1276             } catch (ImsException e) {
1277                 loge("updateImsServiceConfigForSlot: ", e);
1278                 mConfigUpdated = false;
1279             }
1280         }
1281     }
1282
1283     /**
1284      * Update VoLTE config
1285      * @return whether feature is On
1286      * @throws ImsException
1287      */
1288     private boolean updateVolteFeatureValue() throws ImsException {
1289         boolean available = isVolteEnabledByPlatformForSlot();
1290         boolean enabled = isEnhanced4gLteModeSettingEnabledByUserForSlot();
1291         boolean isNonTty = isNonTtyOrTtyOnVolteEnabledForSlot();
1292         boolean isFeatureOn = available && enabled && isNonTty;
1293
1294         log("updateVolteFeatureValue: available = " + available
1295                 + ", enabled = " + enabled
1296                 + ", nonTTY = " + isNonTty);
1297
1298         getConfigInterface().setFeatureValue(
1299                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
1300                 TelephonyManager.NETWORK_TYPE_LTE,
1301                 isFeatureOn ?
1302                         ImsConfig.FeatureValueConstants.ON :
1303                         ImsConfig.FeatureValueConstants.OFF,
1304                 mImsConfigListener);
1305
1306         return isFeatureOn;
1307     }
1308
1309     /**
1310      * Update video call over LTE config
1311      * @return whether feature is On
1312      * @throws ImsException
1313      */
1314     private boolean updateVideoCallFeatureValue() throws ImsException {
1315         boolean available = isVtEnabledByPlatformForSlot();
1316         boolean enabled = isVtEnabledByUserForSlot();
1317         boolean isNonTty = isNonTtyOrTtyOnVolteEnabledForSlot();
1318         boolean isDataEnabled = isDataEnabled();
1319         boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
1320                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1321
1322         boolean isFeatureOn = available && enabled && isNonTty
1323                 && (ignoreDataEnabledChanged || isDataEnabled);
1324
1325         log("updateVideoCallFeatureValue: available = " + available
1326                 + ", enabled = " + enabled
1327                 + ", nonTTY = " + isNonTty
1328                 + ", data enabled = " + isDataEnabled);
1329
1330         getConfigInterface().setFeatureValue(
1331                 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
1332                 TelephonyManager.NETWORK_TYPE_LTE,
1333                 isFeatureOn ?
1334                         ImsConfig.FeatureValueConstants.ON :
1335                         ImsConfig.FeatureValueConstants.OFF,
1336                 mImsConfigListener);
1337
1338         return isFeatureOn;
1339     }
1340
1341     /**
1342      * Update WFC config
1343      * @return whether feature is On
1344      * @throws ImsException
1345      */
1346     private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
1347         boolean isNetworkRoaming = TelephonyManager.getDefault().isNetworkRoaming();
1348         boolean available = isWfcEnabledByPlatformForSlot();
1349         boolean enabled = isWfcEnabledByUserForSlot();
1350         int mode = getWfcModeForSlot(isNetworkRoaming);
1351         boolean roaming = isWfcRoamingEnabledByUserForSlot();
1352         boolean isFeatureOn = available && enabled;
1353
1354         log("updateWfcFeatureAndProvisionedValues: available = " + available
1355                 + ", enabled = " + enabled
1356                 + ", mode = " + mode
1357                 + ", roaming = " + roaming);
1358
1359         getConfigInterface().setFeatureValue(
1360                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
1361                 TelephonyManager.NETWORK_TYPE_IWLAN,
1362                 isFeatureOn ?
1363                         ImsConfig.FeatureValueConstants.ON :
1364                         ImsConfig.FeatureValueConstants.OFF,
1365                 mImsConfigListener);
1366
1367         if (!isFeatureOn) {
1368             mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
1369             roaming = false;
1370         }
1371         setWfcModeInternal(mContext, mode);
1372         setWfcRoamingSettingInternal(roaming);
1373
1374         return isFeatureOn;
1375     }
1376
1377     /**
1378      * Do NOT use this directly, instead use {@link #getInstance}.
1379      */
1380     @VisibleForTesting
1381     public ImsManager(Context context, int phoneId) {
1382         mContext = context;
1383         mPhoneId = phoneId;
1384         mConfigDynamicBind = mContext.getResources().getBoolean(
1385                 com.android.internal.R.bool.config_dynamic_bind_ims);
1386         mConfigManager = (CarrierConfigManager) context.getSystemService(
1387                 Context.CARRIER_CONFIG_SERVICE);
1388         createImsService();
1389     }
1390
1391     /**
1392      * @return Whether or not ImsManager is configured to Dynamically bind or not to support legacy
1393      * devices.
1394      */
1395     public boolean isDynamicBinding() {
1396         return mConfigDynamicBind;
1397     }
1398
1399     /*
1400      * Returns a flag indicating whether the IMS service is available.
1401      */
1402     public boolean isServiceAvailable() {
1403         if (mImsServiceProxy == null) {
1404             createImsService();
1405         }
1406         // mImsServiceProxy will always create an ImsServiceProxy.
1407         return mImsServiceProxy.isBinderAlive();
1408     }
1409
1410     public void setImsConfigListener(ImsConfigListener listener) {
1411         mImsConfigListener = listener;
1412     }
1413
1414     public void addNotifyStatusChangedCallback(ImsServiceProxy.INotifyStatusChanged c) {
1415         if (c != null) {
1416             mStatusCallbacks.add(c);
1417         }
1418     }
1419
1420     /**
1421      * Opens the IMS service for making calls and/or receiving generic IMS calls.
1422      * The caller may make subsquent calls through {@link #makeCall}.
1423      * The IMS service will register the device to the operator's network with the credentials
1424      * (from ISIM) periodically in order to receive calls from the operator's network.
1425      * When the IMS service receives a new call, it will send out an intent with
1426      * the provided action string.
1427      * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
1428      *
1429      * @param serviceClass a service class specified in {@link ImsServiceClass}
1430      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
1431      * @param incomingCallPendingIntent When an incoming call is received,
1432      *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
1433      *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
1434      *        as the result code and the intent to fill in the call ID; It cannot be null
1435      * @param listener To listen to IMS registration events; It cannot be null
1436      * @return identifier (greater than 0) for the specified service
1437      * @throws NullPointerException if {@code incomingCallPendingIntent}
1438      *      or {@code listener} is null
1439      * @throws ImsException if calling the IMS service results in an error
1440      * @see #getCallId
1441      * @see #getImsSessionId
1442      */
1443     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
1444             ImsConnectionStateListener listener) throws ImsException {
1445         checkAndThrowExceptionIfServiceUnavailable();
1446
1447         if (incomingCallPendingIntent == null) {
1448             throw new NullPointerException("incomingCallPendingIntent can't be null");
1449         }
1450
1451         if (listener == null) {
1452             throw new NullPointerException("listener can't be null");
1453         }
1454
1455         int result = 0;
1456
1457         try {
1458             result = mImsServiceProxy.startSession(incomingCallPendingIntent,
1459                     createRegistrationListenerProxy(serviceClass, listener));
1460         } catch (RemoteException e) {
1461             throw new ImsException("open()", e,
1462                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1463         }
1464
1465         if (result <= 0) {
1466             // If the return value is a minus value,
1467             // it means that an error occurred in the service.
1468             // So, it needs to convert to the reason code specified in ImsReasonInfo.
1469             throw new ImsException("open()", (result * (-1)));
1470         }
1471
1472         return result;
1473     }
1474
1475     /**
1476      * Adds registration listener to the IMS service.
1477      *
1478      * @param serviceClass a service class specified in {@link ImsServiceClass}
1479      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
1480      * @param listener To listen to IMS registration events; It cannot be null
1481      * @throws NullPointerException if {@code listener} is null
1482      * @throws ImsException if calling the IMS service results in an error
1483      */
1484     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
1485             throws ImsException {
1486         checkAndThrowExceptionIfServiceUnavailable();
1487
1488         if (listener == null) {
1489             throw new NullPointerException("listener can't be null");
1490         }
1491
1492         try {
1493             ImsRegistrationListenerProxy p = createRegistrationListenerProxy(serviceClass,
1494                     listener);
1495             mRegistrationListeners.add(p);
1496             mImsServiceProxy.addRegistrationListener(p);
1497         } catch (RemoteException e) {
1498             throw new ImsException("addRegistrationListener()", e,
1499                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1500         }
1501     }
1502
1503     /**
1504      * Removes the registration listener from the IMS service.
1505      *
1506      * @param listener Previously registered listener that will be removed. Can not be null.
1507      * @throws NullPointerException if {@code listener} is null
1508      * @throws ImsException if calling the IMS service results in an error
1509      * instead.
1510      */
1511     public void removeRegistrationListener(ImsConnectionStateListener listener)
1512             throws ImsException {
1513         checkAndThrowExceptionIfServiceUnavailable();
1514
1515         if (listener == null) {
1516             throw new NullPointerException("listener can't be null");
1517         }
1518
1519         try {
1520             Optional<ImsRegistrationListenerProxy> optionalProxy = mRegistrationListeners.stream()
1521                     .filter(l -> listener.equals(l.mListener)).findFirst();
1522             if(optionalProxy.isPresent()) {
1523                 ImsRegistrationListenerProxy p = optionalProxy.get();
1524                 mRegistrationListeners.remove(p);
1525                 mImsServiceProxy.removeRegistrationListener(p);
1526             }
1527         } catch (RemoteException e) {
1528             throw new ImsException("removeRegistrationListener()", e,
1529                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1530         }
1531     }
1532
1533     /**
1534      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
1535      * All the resources that were allocated to the service are also released.
1536      *
1537      * @param sessionId a session id to be closed which is obtained from {@link ImsManager#open}
1538      * @throws ImsException if calling the IMS service results in an error
1539      */
1540     public void close(int sessionId) throws ImsException {
1541         checkAndThrowExceptionIfServiceUnavailable();
1542
1543         try {
1544             mImsServiceProxy.endSession(sessionId);
1545         } catch (RemoteException e) {
1546             throw new ImsException("close()", e,
1547                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1548         } finally {
1549             mUt = null;
1550             mConfig = null;
1551             mEcbm = null;
1552             mMultiEndpoint = null;
1553         }
1554     }
1555
1556     /**
1557      * Gets the configuration interface to provision / withdraw the supplementary service settings.
1558      *
1559      * @return the Ut interface instance
1560      * @throws ImsException if getting the Ut interface results in an error
1561      */
1562     public ImsUtInterface getSupplementaryServiceConfiguration()
1563             throws ImsException {
1564         // FIXME: manage the multiple Ut interfaces based on the session id
1565         if (mUt == null || !mImsServiceProxy.isBinderAlive()) {
1566             checkAndThrowExceptionIfServiceUnavailable();
1567
1568             try {
1569                 IImsUt iUt = mImsServiceProxy.getUtInterface();
1570
1571                 if (iUt == null) {
1572                     throw new ImsException("getSupplementaryServiceConfiguration()",
1573                             ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
1574                 }
1575
1576                 mUt = new ImsUt(iUt);
1577             } catch (RemoteException e) {
1578                 throw new ImsException("getSupplementaryServiceConfiguration()", e,
1579                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1580             }
1581         }
1582
1583         return mUt;
1584     }
1585
1586     /**
1587      * Checks if the IMS service has successfully registered to the IMS network
1588      * with the specified service & call type.
1589      *
1590      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1591      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1592      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1593      * @param callType a call type that is specified in {@link ImsCallProfile}
1594      *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
1595      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1596      *        {@link ImsCallProfile#CALL_TYPE_VT}
1597      *        {@link ImsCallProfile#CALL_TYPE_VS}
1598      * @return true if the specified service id is connected to the IMS network;
1599      *        false otherwise
1600      * @throws ImsException if calling the IMS service results in an error
1601      */
1602     public boolean isConnected(int serviceType, int callType)
1603             throws ImsException {
1604         checkAndThrowExceptionIfServiceUnavailable();
1605
1606         try {
1607             return mImsServiceProxy.isConnected(serviceType, callType);
1608         } catch (RemoteException e) {
1609             throw new ImsException("isServiceConnected()", e,
1610                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1611         }
1612     }
1613
1614     /**
1615      * Checks if the specified IMS service is opend.
1616      *
1617      * @return true if the specified service id is opened; false otherwise
1618      * @throws ImsException if calling the IMS service results in an error
1619      */
1620     public boolean isOpened() throws ImsException {
1621         checkAndThrowExceptionIfServiceUnavailable();
1622
1623         try {
1624             return mImsServiceProxy.isOpened();
1625         } catch (RemoteException e) {
1626             throw new ImsException("isOpened()", e,
1627                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1628         }
1629     }
1630
1631     /**
1632      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
1633      *
1634      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1635      * @param serviceType a service type that is specified in {@link ImsCallProfile}
1636      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
1637      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
1638      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
1639      * @param callType a call type that is specified in {@link ImsCallProfile}
1640      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
1641      *        {@link ImsCallProfile#CALL_TYPE_VT}
1642      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
1643      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
1644      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
1645      *        {@link ImsCallProfile#CALL_TYPE_VS}
1646      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
1647      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
1648      * @return a {@link ImsCallProfile} object
1649      * @throws ImsException if calling the IMS service results in an error
1650      */
1651     public ImsCallProfile createCallProfile(int sessionId, int serviceType, int callType)
1652             throws ImsException {
1653         checkAndThrowExceptionIfServiceUnavailable();
1654
1655         try {
1656             return mImsServiceProxy.createCallProfile(sessionId, serviceType, callType);
1657         } catch (RemoteException e) {
1658             throw new ImsException("createCallProfile()", e,
1659                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1660         }
1661     }
1662
1663     /**
1664      * Creates a {@link ImsCall} to make a call.
1665      *
1666      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1667      * @param profile a call profile to make the call
1668      *      (it contains service type, call type, media information, etc.)
1669      * @param participants participants to invite the conference call
1670      * @param listener listen to the call events from {@link ImsCall}
1671      * @return a {@link ImsCall} object
1672      * @throws ImsException if calling the IMS service results in an error
1673      */
1674     public ImsCall makeCall(int sessionId, ImsCallProfile profile, String[] callees,
1675             ImsCall.Listener listener) throws ImsException {
1676         if (DBG) {
1677             log("makeCall :: sessionId=" + sessionId
1678                     + ", profile=" + profile);
1679         }
1680
1681         checkAndThrowExceptionIfServiceUnavailable();
1682
1683         ImsCall call = new ImsCall(mContext, profile);
1684
1685         call.setListener(listener);
1686         ImsCallSession session = createCallSession(sessionId, profile);
1687
1688         if ((callees != null) && (callees.length == 1)) {
1689             call.start(session, callees[0]);
1690         } else {
1691             call.start(session, callees);
1692         }
1693
1694         return call;
1695     }
1696
1697     /**
1698      * Creates a {@link ImsCall} to take an incoming call.
1699      *
1700      * @param sessionId a session id which is obtained from {@link ImsManager#open}
1701      * @param incomingCallIntent the incoming call broadcast intent
1702      * @param listener to listen to the call events from {@link ImsCall}
1703      * @return a {@link ImsCall} object
1704      * @throws ImsException if calling the IMS service results in an error
1705      */
1706     public ImsCall takeCall(int sessionId, Intent incomingCallIntent,
1707             ImsCall.Listener listener) throws ImsException {
1708         if (DBG) {
1709             log("takeCall :: sessionId=" + sessionId
1710                     + ", incomingCall=" + incomingCallIntent);
1711         }
1712
1713         checkAndThrowExceptionIfServiceUnavailable();
1714
1715         if (incomingCallIntent == null) {
1716             throw new ImsException("Can't retrieve session with null intent",
1717                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1718         }
1719
1720         int incomingServiceId = getImsSessionId(incomingCallIntent);
1721
1722         if (sessionId != incomingServiceId) {
1723             throw new ImsException("Service id is mismatched in the incoming call intent",
1724                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1725         }
1726
1727         String callId = getCallId(incomingCallIntent);
1728
1729         if (callId == null) {
1730             throw new ImsException("Call ID missing in the incoming call intent",
1731                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
1732         }
1733
1734         try {
1735             IImsCallSession session = mImsServiceProxy.getPendingCallSession(sessionId, callId);
1736
1737             if (session == null) {
1738                 throw new ImsException("No pending session for the call",
1739                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
1740             }
1741
1742             ImsCall call = new ImsCall(mContext, session.getCallProfile());
1743
1744             call.attachSession(new ImsCallSession(session));
1745             call.setListener(listener);
1746
1747             return call;
1748         } catch (Throwable t) {
1749             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
1750         }
1751     }
1752
1753     /**
1754      * Gets the config interface to get/set service/capability parameters.
1755      *
1756      * @return the ImsConfig instance.
1757      * @throws ImsException if getting the setting interface results in an error.
1758      */
1759     public ImsConfig getConfigInterface() throws ImsException {
1760
1761         if (mConfig == null || !mImsServiceProxy.isBinderAlive()) {
1762             checkAndThrowExceptionIfServiceUnavailable();
1763
1764             try {
1765                 IImsConfig config = mImsServiceProxy.getConfigInterface();
1766                 if (config == null) {
1767                     throw new ImsException("getConfigInterface()",
1768                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
1769                 }
1770                 mConfig = new ImsConfig(config, mContext);
1771             } catch (RemoteException e) {
1772                 throw new ImsException("getConfigInterface()", e,
1773                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1774             }
1775         }
1776         if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
1777         return mConfig;
1778     }
1779
1780     public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete)
1781             throws ImsException {
1782
1783         checkAndThrowExceptionIfServiceUnavailable();
1784
1785         try {
1786             mImsServiceProxy.setUiTTYMode(uiTtyMode, onComplete);
1787         } catch (RemoteException e) {
1788             throw new ImsException("setTTYMode()", e,
1789                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1790         }
1791
1792         if (!getBooleanCarrierConfigForSlot(
1793                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
1794             setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
1795                     isEnhanced4gLteModeSettingEnabledByUserForSlot());
1796         }
1797     }
1798
1799     private ImsReasonInfo makeACopy(ImsReasonInfo imsReasonInfo) {
1800         Parcel p = Parcel.obtain();
1801         imsReasonInfo.writeToParcel(p, 0);
1802         p.setDataPosition(0);
1803         ImsReasonInfo clonedReasonInfo = ImsReasonInfo.CREATOR.createFromParcel(p);
1804         p.recycle();
1805         return clonedReasonInfo;
1806     }
1807
1808     /**
1809      * Get Recent IMS Disconnect Reasons.
1810      *
1811      * @return ArrayList of ImsReasonInfo objects. MAX size of the arraylist
1812      * is MAX_RECENT_DISCONNECT_REASONS. The objects are in the
1813      * chronological order.
1814      */
1815     public ArrayList<ImsReasonInfo> getRecentImsDisconnectReasons() {
1816         ArrayList<ImsReasonInfo> disconnectReasons = new ArrayList<>();
1817
1818         for (ImsReasonInfo reason : mRecentDisconnectReasons) {
1819             disconnectReasons.add(makeACopy(reason));
1820         }
1821         return disconnectReasons;
1822     }
1823
1824     public int getImsServiceStatus() throws ImsException {
1825         checkAndThrowExceptionIfServiceUnavailable();
1826
1827         return mImsServiceProxy.getFeatureStatus();
1828     }
1829
1830     /**
1831      * Get the boolean config from carrier config manager.
1832      *
1833      * @param context the context to get carrier service
1834      * @param key config key defined in CarrierConfigManager
1835      * @return boolean value of corresponding key.
1836      *
1837      * @deprecated Does not support MSIM devices. Use
1838      * {@link #getBooleanCarrierConfigForSlot(Context, String)} instead.
1839      */
1840     private static boolean getBooleanCarrierConfig(Context context, String key) {
1841         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1842                 Context.CARRIER_CONFIG_SERVICE);
1843         PersistableBundle b = null;
1844         if (configManager != null) {
1845             b = configManager.getConfig();
1846         }
1847         if (b != null) {
1848             return b.getBoolean(key);
1849         } else {
1850             // Return static default defined in CarrierConfigManager.
1851             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1852         }
1853     }
1854
1855     /**
1856      * Get the boolean config from carrier config manager.
1857      *
1858      * @param key config key defined in CarrierConfigManager
1859      * @return boolean value of corresponding key.
1860      */
1861     private boolean getBooleanCarrierConfigForSlot(String key) {
1862         int[] subIds = SubscriptionManager.getSubId(mPhoneId);
1863         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1864         if (subIds != null && subIds.length >= 1) {
1865             subId = subIds[0];
1866         }
1867         PersistableBundle b = null;
1868         if (mConfigManager != null) {
1869             // If an invalid subId is used, this bundle will contain default values.
1870             b = mConfigManager.getConfigForSubId(subId);
1871         }
1872         if (b != null) {
1873             return b.getBoolean(key);
1874         } else {
1875             // Return static default defined in CarrierConfigManager.
1876             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
1877         }
1878     }
1879
1880     /**
1881      * Get the int config from carrier config manager.
1882      *
1883      * @param context the context to get carrier service
1884      * @param key config key defined in CarrierConfigManager
1885      * @return integer value of corresponding key.
1886      *
1887      * @deprecated Doesn't support MSIM devices. Use {@link #getIntCarrierConfigForSlot} instead.
1888      */
1889     private static int getIntCarrierConfig(Context context, String key) {
1890         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
1891                 Context.CARRIER_CONFIG_SERVICE);
1892         PersistableBundle b = null;
1893         if (configManager != null) {
1894             b = configManager.getConfig();
1895         }
1896         if (b != null) {
1897             return b.getInt(key);
1898         } else {
1899             // Return static default defined in CarrierConfigManager.
1900             return CarrierConfigManager.getDefaultConfig().getInt(key);
1901         }
1902     }
1903
1904     /**
1905      * Get the int config from carrier config manager.
1906      *
1907      * @param key config key defined in CarrierConfigManager
1908      * @return integer value of corresponding key.
1909      */
1910     private int getIntCarrierConfigForSlot(String key) {
1911         int[] subIds = SubscriptionManager.getSubId(mPhoneId);
1912         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1913         if (subIds != null && subIds.length >= 1) {
1914             subId = subIds[0];
1915         }
1916         PersistableBundle b = null;
1917         if (mConfigManager != null) {
1918             // If an invalid subId is used, this bundle will contain default values.
1919             b = mConfigManager.getConfigForSubId(subId);
1920         }
1921         if (b != null) {
1922             return b.getInt(key);
1923         } else {
1924             // Return static default defined in CarrierConfigManager.
1925             return CarrierConfigManager.getDefaultConfig().getInt(key);
1926         }
1927     }
1928
1929     /**
1930      * Gets the call ID from the specified incoming call broadcast intent.
1931      *
1932      * @param incomingCallIntent the incoming call broadcast intent
1933      * @return the call ID or null if the intent does not contain it
1934      */
1935     private static String getCallId(Intent incomingCallIntent) {
1936         if (incomingCallIntent == null) {
1937             return null;
1938         }
1939
1940         return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
1941     }
1942
1943     /**
1944      * Gets the service type from the specified incoming call broadcast intent.
1945      *
1946      * @param incomingCallIntent the incoming call broadcast intent
1947      * @return the session identifier or -1 if the intent does not contain it
1948      */
1949     private static int getImsSessionId(Intent incomingCallIntent) {
1950         if (incomingCallIntent == null) {
1951             return (-1);
1952         }
1953
1954         return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
1955     }
1956
1957     /**
1958      * Binds the IMS service only if the service is not created.
1959      */
1960     private void checkAndThrowExceptionIfServiceUnavailable()
1961             throws ImsException {
1962         if (mImsServiceProxy == null || !mImsServiceProxy.isBinderAlive()) {
1963             createImsService();
1964
1965             if (mImsServiceProxy == null) {
1966                 throw new ImsException("Service is unavailable",
1967                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
1968             }
1969         }
1970     }
1971
1972     /**
1973      * Binds the IMS service to make/receive the call. Supports two methods of exposing an
1974      * ImsService:
1975      * 1) com.android.ims.ImsService implementation in ServiceManager (deprecated).
1976      * 2) android.telephony.ims.ImsService implementation through ImsResolver.
1977      */
1978     private void createImsService() {
1979         if (!mConfigDynamicBind) {
1980             // Old method of binding
1981             Rlog.i(TAG, "Creating ImsService using ServiceManager");
1982             mImsServiceProxy = getServiceProxyCompat();
1983         } else {
1984             Rlog.i(TAG, "Creating ImsService using ImsResolver");
1985             mImsServiceProxy = getServiceProxy();
1986         }
1987     }
1988
1989     // Deprecated method of binding with the ImsService defined in the ServiceManager.
1990     private ImsServiceProxyCompat getServiceProxyCompat() {
1991         IBinder binder = ServiceManager.checkService(IMS_SERVICE);
1992
1993         if (binder != null) {
1994             try {
1995                 binder.linkToDeath(mDeathRecipient, 0);
1996             } catch (RemoteException e) {
1997             }
1998         }
1999
2000         return new ImsServiceProxyCompat(mPhoneId, binder);
2001     }
2002
2003     // New method of binding with the ImsResolver
2004     private ImsServiceProxy getServiceProxy() {
2005         TelephonyManager tm = (TelephonyManager)
2006                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
2007         ImsServiceProxy serviceProxy = new ImsServiceProxy(mPhoneId, ImsFeature.MMTEL);
2008         // Returns null if the service is not available.
2009         IImsServiceController b = tm.getImsServiceControllerAndListen(mPhoneId,
2010                 ImsFeature.MMTEL, serviceProxy.getListener());
2011         if (b != null) {
2012             serviceProxy.setBinder(b.asBinder());
2013             serviceProxy.setStatusCallback(() -> mStatusCallbacks.forEach(
2014                             ImsServiceProxy.INotifyStatusChanged::notifyStatusChanged));
2015             // Trigger the cache to be updated for feature status.
2016             serviceProxy.getFeatureStatus();
2017         } else {
2018             Rlog.w(TAG, "getServiceProxy: b is null! Phone Id: " + mPhoneId);
2019         }
2020         return serviceProxy;
2021     }
2022
2023     /**
2024      * Creates a {@link ImsCallSession} with the specified call profile.
2025      * Use other methods, if applicable, instead of interacting with
2026      * {@link ImsCallSession} directly.
2027      *
2028      * @param serviceId a service id which is obtained from {@link ImsManager#open}
2029      * @param profile a call profile to make the call
2030      */
2031     private ImsCallSession createCallSession(int serviceId,
2032             ImsCallProfile profile) throws ImsException {
2033         try {
2034             return new ImsCallSession(mImsServiceProxy.createCallSession(serviceId, profile, null));
2035         } catch (RemoteException e) {
2036             return null;
2037         }
2038     }
2039
2040     private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
2041             ImsConnectionStateListener listener) {
2042         ImsRegistrationListenerProxy proxy =
2043                 new ImsRegistrationListenerProxy(serviceClass, listener);
2044         return proxy;
2045     }
2046
2047     private static void log(String s) {
2048         Rlog.d(TAG, s);
2049     }
2050
2051     private static void loge(String s) {
2052         Rlog.e(TAG, s);
2053     }
2054
2055     private static void loge(String s, Throwable t) {
2056         Rlog.e(TAG, s, t);
2057     }
2058
2059     /**
2060      * Used for turning on IMS.if its off already
2061      */
2062     private void turnOnIms() throws ImsException {
2063         checkAndThrowExceptionIfServiceUnavailable();
2064
2065         try {
2066             mImsServiceProxy.turnOnIms();
2067         } catch (RemoteException e) {
2068             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2069         }
2070     }
2071
2072     private boolean isImsTurnOffAllowed() {
2073         return isTurnOffImsAllowedByPlatformForSlot()
2074                 && (!isWfcEnabledByPlatformForSlot()
2075                 || !isWfcEnabledByUserForSlot());
2076     }
2077
2078     private void setLteFeatureValues(boolean turnOn) {
2079         log("setLteFeatureValues: " + turnOn);
2080         try {
2081             ImsConfig config = getConfigInterface();
2082             if (config != null) {
2083                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
2084                         TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
2085
2086                 if (isVolteEnabledByPlatformForSlot()) {
2087                     boolean ignoreDataEnabledChanged = getBooleanCarrierConfig(mContext,
2088                             CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
2089                     boolean enableViLte = turnOn && isVtEnabledByUserForSlot() &&
2090                             (ignoreDataEnabledChanged || isDataEnabled());
2091                     config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
2092                             TelephonyManager.NETWORK_TYPE_LTE,
2093                             enableViLte ? 1 : 0,
2094                             mImsConfigListener);
2095                 }
2096             }
2097         } catch (ImsException e) {
2098             loge("setLteFeatureValues: exception ", e);
2099         }
2100     }
2101
2102     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
2103         checkAndThrowExceptionIfServiceUnavailable();
2104
2105         // if turnOn: first set feature values then call turnOnIms()
2106         // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
2107         // allowed, first call turnOffIms() then set feature values
2108         if (turnOn) {
2109             setLteFeatureValues(turnOn);
2110             log("setAdvanced4GMode: turnOnIms");
2111             turnOnIms();
2112         } else {
2113             if (isImsTurnOffAllowed()) {
2114                 log("setAdvanced4GMode: turnOffIms");
2115                 turnOffIms();
2116             }
2117             setLteFeatureValues(turnOn);
2118         }
2119     }
2120
2121     /**
2122      * Used for turning off IMS completely in order to make the device CSFB'ed.
2123      * Once turned off, all calls will be over CS.
2124      */
2125     private void turnOffIms() throws ImsException {
2126         checkAndThrowExceptionIfServiceUnavailable();
2127
2128         try {
2129             mImsServiceProxy.turnOffIms();
2130         } catch (RemoteException e) {
2131             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2132         }
2133     }
2134
2135     private void addToRecentDisconnectReasons(ImsReasonInfo reason) {
2136         if (reason == null) return;
2137         while (mRecentDisconnectReasons.size() >= MAX_RECENT_DISCONNECT_REASONS) {
2138             mRecentDisconnectReasons.removeFirst();
2139         }
2140         mRecentDisconnectReasons.addLast(reason);
2141     }
2142
2143     /**
2144      * Death recipient class for monitoring IMS service.
2145      */
2146     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
2147         @Override
2148         public void binderDied() {
2149             mImsServiceProxy = null;
2150             mUt = null;
2151             mConfig = null;
2152             mEcbm = null;
2153             mMultiEndpoint = null;
2154         }
2155     }
2156
2157     /**
2158      * Adapter class for {@link IImsRegistrationListener}.
2159      */
2160     private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
2161         private int mServiceClass;
2162         private ImsConnectionStateListener mListener;
2163
2164         public ImsRegistrationListenerProxy(int serviceClass,
2165                 ImsConnectionStateListener listener) {
2166             mServiceClass = serviceClass;
2167             mListener = listener;
2168         }
2169
2170         public boolean isSameProxy(int serviceClass) {
2171             return (mServiceClass == serviceClass);
2172         }
2173
2174         @Deprecated
2175         public void registrationConnected() {
2176             if (DBG) {
2177                 log("registrationConnected ::");
2178             }
2179
2180             if (mListener != null) {
2181                 mListener.onImsConnected(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
2182             }
2183         }
2184
2185         @Deprecated
2186         public void registrationProgressing() {
2187             if (DBG) {
2188                 log("registrationProgressing ::");
2189             }
2190
2191             if (mListener != null) {
2192                 mListener.onImsProgressing(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
2193             }
2194         }
2195
2196         @Override
2197         public void registrationConnectedWithRadioTech(int imsRadioTech) {
2198             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
2199             //       values in ServiceState.java.
2200             if (DBG) {
2201                 log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
2202             }
2203
2204             if (mListener != null) {
2205                 mListener.onImsConnected(imsRadioTech);
2206             }
2207         }
2208
2209         @Override
2210         public void registrationProgressingWithRadioTech(int imsRadioTech) {
2211             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
2212             //       values in ServiceState.java.
2213             if (DBG) {
2214                 log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
2215             }
2216
2217             if (mListener != null) {
2218                 mListener.onImsProgressing(imsRadioTech);
2219             }
2220         }
2221
2222         @Override
2223         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
2224             if (DBG) {
2225                 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
2226             }
2227
2228             addToRecentDisconnectReasons(imsReasonInfo);
2229
2230             if (mListener != null) {
2231                 mListener.onImsDisconnected(imsReasonInfo);
2232             }
2233         }
2234
2235         @Override
2236         public void registrationResumed() {
2237             if (DBG) {
2238                 log("registrationResumed ::");
2239             }
2240
2241             if (mListener != null) {
2242                 mListener.onImsResumed();
2243             }
2244         }
2245
2246         @Override
2247         public void registrationSuspended() {
2248             if (DBG) {
2249                 log("registrationSuspended ::");
2250             }
2251
2252             if (mListener != null) {
2253                 mListener.onImsSuspended();
2254             }
2255         }
2256
2257         @Override
2258         public void registrationServiceCapabilityChanged(int serviceClass, int event) {
2259             log("registrationServiceCapabilityChanged :: serviceClass=" +
2260                     serviceClass + ", event=" + event);
2261
2262             if (mListener != null) {
2263                 mListener.onImsConnected(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
2264             }
2265         }
2266
2267         @Override
2268         public void registrationFeatureCapabilityChanged(int serviceClass,
2269                 int[] enabledFeatures, int[] disabledFeatures) {
2270             log("registrationFeatureCapabilityChanged :: serviceClass=" +
2271                     serviceClass);
2272             if (mListener != null) {
2273                 mListener.onFeatureCapabilityChanged(serviceClass,
2274                         enabledFeatures, disabledFeatures);
2275             }
2276         }
2277
2278         @Override
2279         public void voiceMessageCountUpdate(int count) {
2280             log("voiceMessageCountUpdate :: count=" + count);
2281
2282             if (mListener != null) {
2283                 mListener.onVoiceMessageCountChanged(count);
2284             }
2285         }
2286
2287         @Override
2288         public void registrationAssociatedUriChanged(Uri[] uris) {
2289             if (DBG) log("registrationAssociatedUriChanged ::");
2290
2291             if (mListener != null) {
2292                 mListener.registrationAssociatedUriChanged(uris);
2293             }
2294         }
2295
2296         @Override
2297         public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo) {
2298             if (DBG) log("registrationChangeFailed :: targetAccessTech=" + targetAccessTech +
2299                     ", imsReasonInfo=" + imsReasonInfo);
2300
2301             if (mListener != null) {
2302                 mListener.onRegistrationChangeFailed(targetAccessTech, imsReasonInfo);
2303             }
2304         }
2305     }
2306
2307     /**
2308      * Gets the ECBM interface to request ECBM exit.
2309      *
2310      * @param serviceId a service id which is obtained from {@link ImsManager#open}
2311      * @return the ECBM interface instance
2312      * @throws ImsException if getting the ECBM interface results in an error
2313      */
2314     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
2315         if (mEcbm == null || !mImsServiceProxy.isBinderAlive()) {
2316             checkAndThrowExceptionIfServiceUnavailable();
2317
2318             try {
2319                 IImsEcbm iEcbm = mImsServiceProxy.getEcbmInterface();
2320
2321                 if (iEcbm == null) {
2322                     throw new ImsException("getEcbmInterface()",
2323                             ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
2324                 }
2325                 mEcbm = new ImsEcbm(iEcbm);
2326             } catch (RemoteException e) {
2327                 throw new ImsException("getEcbmInterface()", e,
2328                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2329             }
2330         }
2331         return mEcbm;
2332     }
2333
2334     /**
2335      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
2336      *
2337      * @param serviceId a service id which is obtained from {@link ImsManager#open}
2338      * @return the multi-endpoint interface instance
2339      * @throws ImsException if getting the multi-endpoint interface results in an error
2340      */
2341     public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
2342         if (mMultiEndpoint == null || !mImsServiceProxy.isBinderAlive()) {
2343             checkAndThrowExceptionIfServiceUnavailable();
2344
2345             try {
2346                 IImsMultiEndpoint iImsMultiEndpoint = mImsServiceProxy.getMultiEndpointInterface();
2347
2348                 if (iImsMultiEndpoint == null) {
2349                     throw new ImsException("getMultiEndpointInterface()",
2350                             ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
2351                 }
2352                 mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
2353             } catch (RemoteException e) {
2354                 throw new ImsException("getMultiEndpointInterface()", e,
2355                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
2356             }
2357         }
2358         return mMultiEndpoint;
2359     }
2360
2361     /**
2362      * Resets ImsManager settings back to factory defaults.
2363      *
2364      * @deprecated Doesn't support MSIM devices. Use {@link #factoryResetSlot()} instead.
2365      *
2366      * @hide
2367      */
2368     public static void factoryReset(Context context) {
2369         // Set VoLTE to default
2370         android.provider.Settings.Global.putInt(context.getContentResolver(),
2371                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
2372                 ImsConfig.FeatureValueConstants.ON);
2373
2374         // Set VoWiFi to default
2375         android.provider.Settings.Global.putInt(context.getContentResolver(),
2376                 android.provider.Settings.Global.WFC_IMS_ENABLED,
2377                 getBooleanCarrierConfig(context,
2378                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
2379                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2380
2381         // Set VoWiFi mode to default
2382         android.provider.Settings.Global.putInt(context.getContentResolver(),
2383                 android.provider.Settings.Global.WFC_IMS_MODE,
2384                 getIntCarrierConfig(context,
2385                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
2386
2387         // Set VoWiFi roaming to default
2388         android.provider.Settings.Global.putInt(context.getContentResolver(),
2389                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
2390                 getBooleanCarrierConfig(context,
2391                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
2392                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2393
2394         // Set VT to default
2395         android.provider.Settings.Global.putInt(context.getContentResolver(),
2396                 android.provider.Settings.Global.VT_IMS_ENABLED,
2397                 ImsConfig.FeatureValueConstants.ON);
2398
2399         // Push settings to ImsConfig
2400         ImsManager.updateImsServiceConfig(context,
2401                 SubscriptionManager.getDefaultVoicePhoneId(), true);
2402     }
2403
2404     /**
2405      * Resets ImsManager settings back to factory defaults.
2406      *
2407      * @hide
2408      */
2409     public void factoryResetSlot() {
2410         // Set VoLTE to default
2411         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2412                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
2413                 ImsConfig.FeatureValueConstants.ON);
2414
2415         // Set VoWiFi to default
2416         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2417                 android.provider.Settings.Global.WFC_IMS_ENABLED,
2418                 getBooleanCarrierConfigForSlot(
2419                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
2420                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2421
2422         // Set VoWiFi mode to default
2423         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2424                 android.provider.Settings.Global.WFC_IMS_MODE,
2425                 getIntCarrierConfigForSlot(
2426                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
2427
2428         // Set VoWiFi roaming to default
2429         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2430                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
2431                 getBooleanCarrierConfigForSlot(
2432                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
2433                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
2434
2435         // Set VT to default
2436         android.provider.Settings.Global.putInt(mContext.getContentResolver(),
2437                 android.provider.Settings.Global.VT_IMS_ENABLED,
2438                 ImsConfig.FeatureValueConstants.ON);
2439
2440         // Push settings to ImsConfig
2441         updateImsServiceConfigForSlot(true);
2442     }
2443
2444     private boolean isDataEnabled() {
2445         return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
2446     }
2447
2448     /**
2449      * Set data enabled/disabled flag.
2450      * @param enabled True if data is enabled, otherwise disabled.
2451      */
2452     public void setDataEnabled(boolean enabled) {
2453         log("setDataEnabled: " + enabled);
2454         SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
2455     }
2456
2457     private boolean isVolteProvisioned() {
2458         return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
2459     }
2460
2461     private void setVolteProvisionedProperty(boolean provisioned) {
2462         SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2463     }
2464
2465     private boolean isWfcProvisioned() {
2466         return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
2467     }
2468
2469     private void setWfcProvisionedProperty(boolean provisioned) {
2470         SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2471     }
2472
2473     private boolean isVtProvisioned() {
2474         return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
2475     }
2476
2477     private void setVtProvisionedProperty(boolean provisioned) {
2478         SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
2479     }
2480
2481     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2482         pw.println("ImsManager:");
2483         pw.println("  mPhoneId = " + mPhoneId);
2484         pw.println("  mConfigUpdated = " + mConfigUpdated);
2485         pw.println("  mImsServiceProxy = " + mImsServiceProxy);
2486         pw.println("  mDataEnabled = " + isDataEnabled());
2487         pw.println("  ignoreDataEnabledChanged = " + getBooleanCarrierConfig(mContext,
2488                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS));
2489
2490         pw.println("  isGbaValid = " + isGbaValidForSlot());
2491         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
2492         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabledForSlot());
2493
2494         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatformForSlot());
2495         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDeviceForSlot());
2496         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
2497                 isEnhanced4gLteModeSettingEnabledByUserForSlot());
2498         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatformForSlot());
2499         pw.println("  isVtEnabledByUser = " + isVtEnabledByUserForSlot());
2500
2501         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatformForSlot());
2502         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUserForSlot());
2503         pw.println("  getWfcMode = " + getWfcModeForSlot());
2504         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUserForSlot());
2505
2506         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDeviceForSlot());
2507         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDeviceForSlot());
2508         pw.flush();
2509     }
2510 }