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