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