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