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 static android.telephony.ims.ProvisioningManager.KEY_VOIMS_OPT_IN_STATUS; 20 21 import android.annotation.NonNull; 22 import android.app.PendingIntent; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.os.Build; 28 import android.os.Message; 29 import android.os.PersistableBundle; 30 import android.os.RemoteException; 31 import android.os.ServiceSpecificException; 32 import android.os.SystemProperties; 33 import android.provider.Settings; 34 import android.telecom.TelecomManager; 35 import android.telephony.AccessNetworkConstants; 36 import android.telephony.BinderCacheManager; 37 import android.telephony.CarrierConfigManager; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.TelephonyFrameworkInitializer; 40 import android.telephony.TelephonyManager; 41 import android.telephony.ims.ImsCallProfile; 42 import android.telephony.ims.ImsCallSession; 43 import android.telephony.ims.ImsMmTelManager; 44 import android.telephony.ims.ImsReasonInfo; 45 import android.telephony.ims.ImsService; 46 import android.telephony.ims.ProvisioningManager; 47 import android.telephony.ims.RegistrationManager; 48 import android.telephony.ims.RtpHeaderExtensionType; 49 import android.telephony.ims.aidl.IImsCapabilityCallback; 50 import android.telephony.ims.aidl.IImsConfig; 51 import android.telephony.ims.aidl.IImsConfigCallback; 52 import android.telephony.ims.aidl.IImsMmTelFeature; 53 import android.telephony.ims.aidl.IImsRegistration; 54 import android.telephony.ims.aidl.IImsRegistrationCallback; 55 import android.telephony.ims.aidl.IImsSmsListener; 56 import android.telephony.ims.aidl.ISipTransport; 57 import android.telephony.ims.feature.CapabilityChangeRequest; 58 import android.telephony.ims.feature.ImsFeature; 59 import android.telephony.ims.feature.MmTelFeature; 60 import android.telephony.ims.stub.ImsCallSessionImplBase; 61 import android.telephony.ims.stub.ImsConfigImplBase; 62 import android.telephony.ims.stub.ImsRegistrationImplBase; 63 import android.util.SparseArray; 64 65 import com.android.ims.internal.IImsCallSession; 66 import com.android.ims.internal.IImsServiceFeatureCallback; 67 import com.android.internal.annotations.VisibleForTesting; 68 import com.android.internal.telephony.ITelephony; 69 import com.android.telephony.Rlog; 70 71 import java.io.FileDescriptor; 72 import java.io.PrintWriter; 73 import java.util.ArrayList; 74 import java.util.Arrays; 75 import java.util.Set; 76 import java.util.concurrent.BlockingQueue; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.concurrent.Executor; 79 import java.util.concurrent.Executors; 80 import java.util.concurrent.LinkedBlockingDeque; 81 import java.util.concurrent.TimeUnit; 82 import java.util.concurrent.atomic.AtomicReference; 83 import java.util.function.Consumer; 84 85 /** 86 * Provides APIs for MMTEL IMS services, such as initiating IMS calls, and provides access to 87 * the operator's IMS network. This class is the starting point for any IMS MMTEL actions. 88 * You can acquire an instance of it with {@link #getInstance getInstance()}. 89 * {Use {@link RcsFeatureManager} for RCS services}. 90 * For internal use ONLY! Use {@link ImsMmTelManager} instead. 91 * @hide 92 */ 93 public class ImsManager implements FeatureUpdates { 94 95 /* 96 * Debug flag to override configuration flag 97 */ 98 public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr"; 99 public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0; 100 public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr"; 101 public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0; 102 public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr"; 103 public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0; 104 public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off"; 105 public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0; 106 107 /** 108 * The result code to be sent back with the incoming call {@link PendingIntent}. 109 * @see #open(MmTelFeature.Listener) 110 */ 111 public static final int INCOMING_CALL_RESULT_CODE = 101; 112 113 /** 114 * Key to retrieve the call ID from an incoming call intent. No longer used, see 115 * {@link ImsCallSessionImplBase#getCallId()}. 116 * @deprecated Not used in the framework, keeping around symbol to not break old vendor 117 * components. 118 */ 119 @Deprecated 120 public static final String EXTRA_CALL_ID = "android:imsCallID"; 121 122 /** 123 * Action to broadcast when ImsService is up. 124 * Internal use only. 125 * @deprecated 126 * @hide 127 */ 128 public static final String ACTION_IMS_SERVICE_UP = 129 "com.android.ims.IMS_SERVICE_UP"; 130 131 /** 132 * Action to broadcast when ImsService is down. 133 * Internal use only. 134 * @deprecated 135 * @hide 136 */ 137 public static final String ACTION_IMS_SERVICE_DOWN = 138 "com.android.ims.IMS_SERVICE_DOWN"; 139 140 /** 141 * Action to broadcast when ImsService registration fails. 142 * Internal use only. 143 * @hide 144 * @deprecated use {@link android.telephony.ims.ImsManager#ACTION_WFC_IMS_REGISTRATION_ERROR} 145 * instead. 146 */ 147 @Deprecated 148 public static final String ACTION_IMS_REGISTRATION_ERROR = 149 android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR; 150 151 /** 152 * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. 153 * A long value; the phone ID corresponding to the IMS service coming up or down. 154 * Internal use only. 155 * @hide 156 */ 157 public static final String EXTRA_PHONE_ID = "android:phone_id"; 158 159 /** 160 * Action for the incoming call intent for the Phone app. 161 * Internal use only. 162 * @hide 163 */ 164 public static final String ACTION_IMS_INCOMING_CALL = 165 "com.android.ims.IMS_INCOMING_CALL"; 166 167 /** 168 * Part of the ACTION_IMS_INCOMING_CALL intents. 169 * An integer value; service identifier obtained from {@link ImsManager#open}. 170 * Internal use only. 171 * @hide 172 * @deprecated Not used in the system, keeping around to not break old vendor components. 173 */ 174 @Deprecated 175 public static final String EXTRA_SERVICE_ID = "android:imsServiceId"; 176 177 /** 178 * Part of the ACTION_IMS_INCOMING_CALL intents. 179 * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD. 180 * The value "true" indicates that the incoming call is for USSD. 181 * Internal use only. 182 * @deprecated Keeping around to not break old vendor components. Use 183 * {@link MmTelFeature#EXTRA_IS_USSD} instead. 184 * @hide 185 */ 186 public static final String EXTRA_USSD = "android:ussd"; 187 188 /** 189 * Part of the ACTION_IMS_INCOMING_CALL intents. 190 * A boolean value; Flag to indicate whether the call is an unknown 191 * dialing call. Such calls are originated by sending commands (like 192 * AT commands) directly to modem without Android involvement. 193 * Even though they are not incoming calls, they are propagated 194 * to Phone app using same ACTION_IMS_INCOMING_CALL intent. 195 * Internal use only. 196 * @deprecated Keeping around to not break old vendor components. Use 197 * {@link MmTelFeature#EXTRA_IS_UNKNOWN_CALL} instead. 198 * @hide 199 */ 200 public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown"; 201 202 private static final int SUBINFO_PROPERTY_FALSE = 0; 203 204 private static final int SYSTEM_PROPERTY_NOT_SET = -1; 205 206 // -1 indicates a subscriptionProperty value that is never set. 207 private static final int SUB_PROPERTY_NOT_INITIALIZED = -1; 208 209 private static final String TAG = "ImsManager"; 210 private static final boolean DBG = true; 211 212 private static final int RESPONSE_WAIT_TIME_MS = 3000; 213 214 private static final int[] LOCAL_IMS_CONFIG_KEYS = { 215 KEY_VOIMS_OPT_IN_STATUS 216 }; 217 218 /** 219 * Create a Lazy Executor that is not instantiated for this instance unless it is used. This 220 * is to stop threads from being started on ImsManagers that are created to do simple tasks. 221 */ 222 private static class LazyExecutor implements Executor { 223 private Executor mExecutor; 224 225 @Override execute(Runnable runnable)226 public void execute(Runnable runnable) { 227 startExecutorIfNeeded(); 228 mExecutor.execute(runnable); 229 } 230 startExecutorIfNeeded()231 private synchronized void startExecutorIfNeeded() { 232 if (mExecutor != null) return; 233 mExecutor = Executors.newSingleThreadExecutor(); 234 } 235 } 236 237 @VisibleForTesting 238 public interface MmTelFeatureConnectionFactory { create(Context context, int phoneId, IImsMmTelFeature feature, IImsConfig c, IImsRegistration r, ISipTransport s)239 MmTelFeatureConnection create(Context context, int phoneId, IImsMmTelFeature feature, 240 IImsConfig c, IImsRegistration r, ISipTransport s); 241 } 242 243 @VisibleForTesting 244 public interface SettingsProxy { 245 /** @see Settings.Secure#getInt(ContentResolver, String, int) */ getSecureIntSetting(ContentResolver cr, String name, int def)246 int getSecureIntSetting(ContentResolver cr, String name, int def); 247 /** @see Settings.Secure#putInt(ContentResolver, String, int) */ putSecureIntSetting(ContentResolver cr, String name, int value)248 boolean putSecureIntSetting(ContentResolver cr, String name, int value); 249 } 250 251 @VisibleForTesting 252 public interface SubscriptionManagerProxy { isValidSubscriptionId(int subId)253 boolean isValidSubscriptionId(int subId); getSubscriptionIds(int slotIndex)254 int[] getSubscriptionIds(int slotIndex); getDefaultVoicePhoneId()255 int getDefaultVoicePhoneId(); getIntegerSubscriptionProperty(int subId, String propKey, int defValue)256 int getIntegerSubscriptionProperty(int subId, String propKey, int defValue); setSubscriptionProperty(int subId, String propKey, String propValue)257 void setSubscriptionProperty(int subId, String propKey, String propValue); getActiveSubscriptionIdList()258 int[] getActiveSubscriptionIdList(); 259 } 260 261 // Default implementations, which is mocked for testing 262 private static class DefaultSettingsProxy implements SettingsProxy { 263 @Override getSecureIntSetting(ContentResolver cr, String name, int def)264 public int getSecureIntSetting(ContentResolver cr, String name, int def) { 265 return Settings.Secure.getInt(cr, name, def); 266 } 267 268 @Override putSecureIntSetting(ContentResolver cr, String name, int value)269 public boolean putSecureIntSetting(ContentResolver cr, String name, int value) { 270 return Settings.Secure.putInt(cr, name, value); 271 } 272 } 273 274 // Default implementation which is mocked to make static dependency validation easier. 275 private static class DefaultSubscriptionManagerProxy implements SubscriptionManagerProxy { 276 277 private Context mContext; 278 DefaultSubscriptionManagerProxy(Context context)279 public DefaultSubscriptionManagerProxy(Context context) { 280 mContext = context; 281 } 282 283 @Override isValidSubscriptionId(int subId)284 public boolean isValidSubscriptionId(int subId) { 285 return SubscriptionManager.isValidSubscriptionId(subId); 286 } 287 288 @Override getSubscriptionIds(int slotIndex)289 public int[] getSubscriptionIds(int slotIndex) { 290 return getSubscriptionManager().getSubscriptionIds(slotIndex); 291 } 292 293 @Override getDefaultVoicePhoneId()294 public int getDefaultVoicePhoneId() { 295 return SubscriptionManager.getDefaultVoicePhoneId(); 296 } 297 298 @Override getIntegerSubscriptionProperty(int subId, String propKey, int defValue)299 public int getIntegerSubscriptionProperty(int subId, String propKey, int defValue) { 300 return SubscriptionManager.getIntegerSubscriptionProperty(subId, propKey, defValue, 301 mContext); 302 } 303 304 @Override setSubscriptionProperty(int subId, String propKey, String propValue)305 public void setSubscriptionProperty(int subId, String propKey, String propValue) { 306 SubscriptionManager.setSubscriptionProperty(subId, propKey, propValue); 307 } 308 309 @Override getActiveSubscriptionIdList()310 public int[] getActiveSubscriptionIdList() { 311 return getSubscriptionManager().getActiveSubscriptionIdList(); 312 } 313 getSubscriptionManager()314 private SubscriptionManager getSubscriptionManager() { 315 return mContext.getSystemService(SubscriptionManager.class); 316 } 317 } 318 319 /** 320 * Events that will be triggered as part of metrics collection. 321 */ 322 public interface ImsStatsCallback { 323 /** 324 * The MmTel capabilities that are enabled have changed. 325 * @param capability The MmTel capability 326 * @param regTech The IMS registration technology associated with the capability. 327 * @param isEnabled {@code true} if the capability is enabled, {@code false} if it is 328 * disabled. 329 */ onEnabledMmTelCapabilitiesChanged( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int regTech, boolean isEnabled)330 void onEnabledMmTelCapabilitiesChanged( 331 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 332 @ImsRegistrationImplBase.ImsRegistrationTech int regTech, 333 boolean isEnabled); 334 } 335 336 /** 337 * Internally we will create a FeatureConnector when {@link #getInstance(Context, int)} is 338 * called to keep the MmTelFeatureConnection instance fresh as new SIM cards are 339 * inserted/removed and MmTelFeature potentially changes. 340 * <p> 341 * For efficiency purposes, there is only one ImsManager created per-slot when using 342 * {@link #getInstance(Context, int)} and the same instance is returned for multiple callers. 343 * This is due to the ImsManager being a potentially heavyweight object depending on what it is 344 * being used for. 345 */ 346 private static class InstanceManager implements FeatureConnector.Listener<ImsManager> { 347 // If this is the first time connecting, wait a small amount of time in case IMS has already 348 // connected. Otherwise, ImsManager will become ready when the ImsService is connected. 349 private static final int CONNECT_TIMEOUT_MS = 50; 350 351 private final FeatureConnector<ImsManager> mConnector; 352 private final ImsManager mImsManager; 353 354 private final Object mLock = new Object(); 355 private boolean isConnectorActive = false; 356 private CountDownLatch mConnectedLatch; 357 InstanceManager(ImsManager manager)358 public InstanceManager(ImsManager manager) { 359 mImsManager = manager; 360 // Set a special prefix so that logs generated by getInstance are distinguishable. 361 mImsManager.mLogTagPostfix = "IM"; 362 363 ArrayList<Integer> readyFilter = new ArrayList<>(); 364 readyFilter.add(ImsFeature.STATE_READY); 365 readyFilter.add(ImsFeature.STATE_INITIALIZING); 366 readyFilter.add(ImsFeature.STATE_UNAVAILABLE); 367 // Pass a reference of the ImsManager being managed into the connector, allowing it to 368 // update the internal MmTelFeatureConnection as it is being updated. 369 mConnector = new FeatureConnector<>(manager.mContext, manager.mPhoneId, 370 (c,p) -> mImsManager, "InstanceManager", readyFilter, this, 371 manager.getImsThreadExecutor()); 372 } 373 getInstance()374 public ImsManager getInstance() { 375 return mImsManager; 376 } 377 reconnect()378 public void reconnect() { 379 boolean requiresReconnect = false; 380 synchronized (mLock) { 381 if (!isConnectorActive) { 382 requiresReconnect = true; 383 isConnectorActive = true; 384 mConnectedLatch = new CountDownLatch(1); 385 } 386 } 387 if (requiresReconnect) { 388 mConnector.connect(); 389 } 390 try { 391 // If this is during initial reconnect, let all threads wait for connect 392 // (or timeout) 393 mConnectedLatch.await(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 394 } catch (InterruptedException e) { 395 // Do nothing and allow ImsService to attach behind the scenes 396 } 397 } 398 399 @Override connectionReady(ImsManager manager)400 public void connectionReady(ImsManager manager) { 401 synchronized (mLock) { 402 mConnectedLatch.countDown(); 403 } 404 } 405 406 @Override connectionUnavailable(int reason)407 public void connectionUnavailable(int reason) { 408 synchronized (mLock) { 409 // only need to track the connection becoming unavailable due to telephony going 410 // down. 411 if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) { 412 isConnectorActive = false; 413 } 414 mConnectedLatch.countDown(); 415 } 416 417 } 418 } 419 420 // Replaced with single-threaded executor for testing. 421 private final Executor mExecutor; 422 // Replaced With mock for testing 423 private MmTelFeatureConnectionFactory mMmTelFeatureConnectionFactory = 424 MmTelFeatureConnection::new; 425 private final SubscriptionManagerProxy mSubscriptionManagerProxy; 426 private final SettingsProxy mSettingsProxy; 427 428 private Context mContext; 429 private CarrierConfigManager mConfigManager; 430 private int mPhoneId; 431 private AtomicReference<MmTelFeatureConnection> mMmTelConnectionRef = new AtomicReference<>(); 432 // Used for debug purposes only currently 433 private boolean mConfigUpdated = false; 434 private BinderCacheManager<ITelephony> mBinderCache; 435 private ImsConfigListener mImsConfigListener; 436 437 public static final String TRUE = "true"; 438 public static final String FALSE = "false"; 439 // Map of phoneId -> InstanceManager 440 private static final SparseArray<InstanceManager> IMS_MANAGER_INSTANCES = new SparseArray<>(2); 441 // Map of phoneId -> ImsStatsCallback 442 private static final SparseArray<ImsStatsCallback> IMS_STATS_CALLBACKS = new SparseArray<>(2); 443 444 // A log prefix added to some instances of ImsManager to make it distinguishable from others. 445 // - "IM" added to ImsManager for ImsManagers created using getInstance. 446 private String mLogTagPostfix = ""; 447 448 /** 449 * Gets a manager instance and blocks for a limited period of time, connecting to the 450 * corresponding ImsService MmTelFeature if it exists. 451 * <p> 452 * If the ImsService is unavailable or becomes unavailable, the associated methods will fail and 453 * a new ImsManager will need to be requested. Instead, a {@link FeatureConnector} can be 454 * requested using {@link #getConnector}, which will notify the caller when a new ImsManager is 455 * available. 456 * 457 * @param context application context for creating the manager object 458 * @param phoneId the phone ID for the IMS Service 459 * @return the manager instance corresponding to the phoneId 460 */ 461 @UnsupportedAppUsage getInstance(Context context, int phoneId)462 public static ImsManager getInstance(Context context, int phoneId) { 463 InstanceManager instanceManager; 464 synchronized (IMS_MANAGER_INSTANCES) { 465 instanceManager = IMS_MANAGER_INSTANCES.get(phoneId); 466 if (instanceManager == null) { 467 ImsManager m = new ImsManager(context, phoneId); 468 instanceManager = new InstanceManager(m); 469 IMS_MANAGER_INSTANCES.put(phoneId, instanceManager); 470 } 471 } 472 // If the ImsManager became disconnected for some reason, try to reconnect it now. 473 instanceManager.reconnect(); 474 return instanceManager.getInstance(); 475 } 476 477 /** 478 * Retrieve an FeatureConnector for ImsManager, which allows a Listener to listen for when 479 * the ImsManager becomes available or unavailable due to the ImsService MmTelFeature moving to 480 * the READY state or destroyed on a specific phone modem index. 481 * 482 * @param context The Context that will be used to connect the ImsManager. 483 * @param phoneId The modem phone ID that the ImsManager will be created for. 484 * @param logPrefix The log prefix used for debugging purposes. 485 * @param listener The Listener that will deliver ImsManager updates as it becomes available. 486 * @param executor The Executor that the Listener callbacks will be called on. 487 * @return A FeatureConnector instance for generating ImsManagers as the associated 488 * MmTelFeatures become available. 489 */ getConnector(Context context, int phoneId, String logPrefix, FeatureConnector.Listener<ImsManager> listener, Executor executor)490 public static FeatureConnector<ImsManager> getConnector(Context context, 491 int phoneId, String logPrefix, FeatureConnector.Listener<ImsManager> listener, 492 Executor executor) { 493 // Only listen for the READY state from the MmTelFeature here. 494 ArrayList<Integer> readyFilter = new ArrayList<>(); 495 readyFilter.add(ImsFeature.STATE_READY); 496 return new FeatureConnector<>(context, phoneId, ImsManager::new, logPrefix, readyFilter, 497 listener, executor); 498 } 499 isImsSupportedOnDevice(Context context)500 public static boolean isImsSupportedOnDevice(Context context) { 501 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS); 502 } 503 504 /** 505 * Sets the callback that will be called when events related to IMS metric collection occur. 506 * <p> 507 * Note: Subsequent calls to this method will replace the previous stats callback. 508 */ setImsStatsCallback(int phoneId, ImsStatsCallback cb)509 public static void setImsStatsCallback(int phoneId, ImsStatsCallback cb) { 510 synchronized (IMS_STATS_CALLBACKS) { 511 if (cb == null) { 512 IMS_STATS_CALLBACKS.remove(phoneId); 513 } else { 514 IMS_STATS_CALLBACKS.put(phoneId, cb); 515 } 516 } 517 } 518 519 /** 520 * @return the {@link ImsStatsCallback} instance associated with the provided phoneId or 521 * {@link null} if none currently exists. 522 */ getStatsCallback(int phoneId)523 private static ImsStatsCallback getStatsCallback(int phoneId) { 524 synchronized (IMS_STATS_CALLBACKS) { 525 return IMS_STATS_CALLBACKS.get(phoneId); 526 } 527 } 528 529 /** 530 * Returns the user configuration of Enhanced 4G LTE Mode setting. 531 * 532 * @deprecated Doesn't support MSIM devices. Use 533 * {@link #isEnhanced4gLteModeSettingEnabledByUser()} instead. 534 */ 535 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isEnhanced4gLteModeSettingEnabledByUser(Context context)536 public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) { 537 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 538 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 539 if (mgr != null) { 540 return mgr.isEnhanced4gLteModeSettingEnabledByUser(); 541 } 542 Rlog.e(TAG, "isEnhanced4gLteModeSettingEnabledByUser: ImsManager null, returning default" 543 + " value."); 544 return false; 545 } 546 547 /** 548 * Returns the user configuration of Enhanced 4G LTE Mode setting for slot. If the option is 549 * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), 550 * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), the setting is 551 * not initialized, and VoIMS opt-in status disabled, this method will return default value 552 * specified by {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. 553 * 554 * Note that even if the setting was set, it may no longer be editable. If this is the case we 555 * return the default value. 556 */ isEnhanced4gLteModeSettingEnabledByUser()557 public boolean isEnhanced4gLteModeSettingEnabledByUser() { 558 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 559 getSubId(), SubscriptionManager.ENHANCED_4G_MODE_ENABLED, 560 SUB_PROPERTY_NOT_INITIALIZED); 561 boolean onByDefault = getBooleanCarrierConfig( 562 CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL); 563 boolean isUiUnEditable = 564 !getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) 565 || getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL); 566 boolean isSettingNotInitialized = setting == SUB_PROPERTY_NOT_INITIALIZED; 567 568 // If Enhanced 4G LTE Mode is uneditable, hidden, not initialized and VoIMS opt-in disabled 569 // we use the default value. If VoIMS opt-in is enabled, we will always allow the user to 570 // change the IMS enabled setting. 571 if ((isUiUnEditable || isSettingNotInitialized) && !isVoImsOptInEnabled()) { 572 return onByDefault; 573 } else { 574 return (setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED); 575 } 576 } 577 578 /** 579 * Change persistent Enhanced 4G LTE Mode setting. 580 * 581 * @deprecated Doesn't support MSIM devices. Use {@link #setEnhanced4gLteModeSetting(boolean)} 582 * instead. 583 */ setEnhanced4gLteModeSetting(Context context, boolean enabled)584 public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) { 585 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 586 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 587 if (mgr != null) { 588 mgr.setEnhanced4gLteModeSetting(enabled); 589 } 590 Rlog.e(TAG, "setEnhanced4gLteModeSetting: ImsManager null, value not set."); 591 } 592 593 /** 594 * Change persistent Enhanced 4G LTE Mode setting. If the option is not editable 595 * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), 596 * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), and VoIMS opt-in 597 * status disabled, this method will set the setting to the default value specified by 598 * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. 599 */ setEnhanced4gLteModeSetting(boolean enabled)600 public void setEnhanced4gLteModeSetting(boolean enabled) { 601 if (enabled && !isVolteProvisionedOnDevice()) { 602 log("setEnhanced4gLteModeSetting: Not possible to enable VoLTE due to provisioning."); 603 return; 604 } 605 int subId = getSubId(); 606 if (!isSubIdValid(subId)) { 607 loge("setEnhanced4gLteModeSetting: invalid sub id, can not set property in " + 608 " siminfo db; subId=" + subId); 609 return; 610 } 611 // If editable=false or hidden=true, we must keep default advanced 4G mode. 612 boolean isUiUnEditable = 613 !getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) || 614 getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL); 615 616 // If VoIMS opt-in is enabled, we will always allow the user to change the IMS enabled 617 // setting. 618 if (isUiUnEditable && !isVoImsOptInEnabled()) { 619 enabled = getBooleanCarrierConfig( 620 CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL); 621 } 622 623 int prevSetting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(subId, 624 SubscriptionManager.ENHANCED_4G_MODE_ENABLED, SUB_PROPERTY_NOT_INITIALIZED); 625 626 if (prevSetting == (enabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 627 ProvisioningManager.PROVISIONING_VALUE_DISABLED)) { 628 // No change in setting. 629 return; 630 } 631 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 632 SubscriptionManager.ENHANCED_4G_MODE_ENABLED, 633 booleanToPropertyString(enabled)); 634 try { 635 if (enabled) { 636 CapabilityChangeRequest request = new CapabilityChangeRequest(); 637 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(); 638 // This affects voice and video enablement 639 updateVoiceCellFeatureValue(request, isNonTty); 640 updateVideoCallFeatureValue(request, isNonTty); 641 changeMmTelCapability(request); 642 // Ensure IMS is on if this setting is enabled. 643 turnOnIms(); 644 } else { 645 // This may trigger entire IMS interface to be disabled, so recalculate full state. 646 reevaluateCapabilities(); 647 } 648 } catch (ImsException e) { 649 loge("setEnhanced4gLteModeSetting couldn't set config: " + e); 650 } 651 } 652 653 /** 654 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is 655 * supported. 656 * @deprecated Does not support MSIM devices. Please use 657 * {@link #isNonTtyOrTtyOnVolteEnabled()} instead. 658 */ 659 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isNonTtyOrTtyOnVolteEnabled(Context context)660 public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) { 661 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 662 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 663 if (mgr != null) { 664 return mgr.isNonTtyOrTtyOnVolteEnabled(); 665 } 666 Rlog.e(TAG, "isNonTtyOrTtyOnVolteEnabled: ImsManager null, returning default value."); 667 return false; 668 } 669 670 /** 671 * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is 672 * supported on a per slot basis. 673 */ isNonTtyOrTtyOnVolteEnabled()674 public boolean isNonTtyOrTtyOnVolteEnabled() { 675 if (isTtyOnVoLteCapable()) { 676 return true; 677 } 678 679 TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 680 if (tm == null) { 681 logw("isNonTtyOrTtyOnVolteEnabled: telecom not available"); 682 return true; 683 } 684 return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF; 685 } 686 isTtyOnVoLteCapable()687 public boolean isTtyOnVoLteCapable() { 688 return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL); 689 } 690 691 /** 692 * Returns a platform configuration for VoLTE which may override the user setting. 693 * @deprecated Does not support MSIM devices. Please use 694 * {@link #isVolteEnabledByPlatform()} instead. 695 */ 696 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isVolteEnabledByPlatform(Context context)697 public static boolean isVolteEnabledByPlatform(Context context) { 698 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 699 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 700 if (mgr != null) { 701 return mgr.isVolteEnabledByPlatform(); 702 } 703 Rlog.e(TAG, "isVolteEnabledByPlatform: ImsManager null, returning default value."); 704 return false; 705 } 706 707 /** 708 * Asynchronous call to ImsService to determine whether or not a specific MmTel capability is 709 * supported. 710 */ isSupported(int capability, int transportType, Consumer<Boolean> result)711 public void isSupported(int capability, int transportType, Consumer<Boolean> result) { 712 getImsThreadExecutor().execute(() -> { 713 switch(transportType) { 714 // Does not take into account NR, as NR is a superset of LTE support currently. 715 case (AccessNetworkConstants.TRANSPORT_TYPE_WWAN): { 716 switch (capability) { 717 case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE): { 718 result.accept(isVolteEnabledByPlatform()); 719 return; 720 } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO): { 721 result.accept(isVtEnabledByPlatform()); 722 return; 723 }case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT): { 724 result.accept(isSuppServicesOverUtEnabledByPlatform()); 725 return; 726 } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS): { 727 // There is currently no carrier config defined for this. 728 result.accept(true); 729 return; 730 } 731 } 732 break; 733 } case (AccessNetworkConstants.TRANSPORT_TYPE_WLAN): { 734 switch (capability) { 735 case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE) : { 736 result.accept(isWfcEnabledByPlatform()); 737 return; 738 } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO) : { 739 // This is not transport dependent at this time. 740 result.accept(isVtEnabledByPlatform()); 741 return; 742 } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT) : { 743 // This is not transport dependent at this time. 744 result.accept(isSuppServicesOverUtEnabledByPlatform()); 745 return; 746 } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS) : { 747 // There is currently no carrier config defined for this. 748 result.accept(true); 749 return; 750 } 751 } 752 break; 753 } 754 } 755 // false for unknown capability/transport types. 756 result.accept(false); 757 }); 758 759 } 760 761 /** 762 * Returns a platform configuration for VoLTE which may override the user setting on a per Slot 763 * basis. 764 */ isVolteEnabledByPlatform()765 public boolean isVolteEnabledByPlatform() { 766 // We first read the per slot value. If doesn't exist, we read the general value. If still 767 // doesn't exist, we use the hardcoded default value. 768 if (SystemProperties.getInt( 769 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE + Integer.toString(mPhoneId), 770 SYSTEM_PROPERTY_NOT_SET) == 1 || 771 SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE, 772 SYSTEM_PROPERTY_NOT_SET) == 1) { 773 return true; 774 } 775 776 if (getLocalImsConfigKeyInt(KEY_VOIMS_OPT_IN_STATUS) 777 == ProvisioningManager.PROVISIONING_VALUE_ENABLED) { 778 return true; 779 } 780 781 return mContext.getResources().getBoolean( 782 com.android.internal.R.bool.config_device_volte_available) 783 && getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL) 784 && isGbaValid(); 785 } 786 787 /** 788 * @return {@code true} if IMS over NR is enabled by the platform, {@code false} otherwise. 789 */ isImsOverNrEnabledByPlatform()790 public boolean isImsOverNrEnabledByPlatform() { 791 int[] nrCarrierCaps = getIntArrayCarrierConfig( 792 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); 793 if (nrCarrierCaps == null) return false; 794 boolean voNrCarrierSupported = Arrays.stream(nrCarrierCaps) 795 .anyMatch(cap -> cap == CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA); 796 if (!voNrCarrierSupported) return false; 797 return isGbaValid(); 798 } 799 800 /** 801 * Indicates whether VoLTE is provisioned on device. 802 * 803 * @deprecated Does not support MSIM devices. Please use 804 * {@link #isVolteProvisionedOnDevice()} instead. 805 */ isVolteProvisionedOnDevice(Context context)806 public static boolean isVolteProvisionedOnDevice(Context context) { 807 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 808 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 809 if (mgr != null) { 810 return mgr.isVolteProvisionedOnDevice(); 811 } 812 Rlog.e(TAG, "isVolteProvisionedOnDevice: ImsManager null, returning default value."); 813 return true; 814 } 815 816 /** 817 * Indicates whether VoLTE is provisioned on this slot. 818 */ isVolteProvisionedOnDevice()819 public boolean isVolteProvisionedOnDevice() { 820 if (getBooleanCarrierConfig( 821 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) { 822 return isVolteProvisioned(); 823 } 824 825 return true; 826 } 827 828 /** 829 * Indicates whether EAB is provisioned on this slot. 830 */ isEabProvisionedOnDevice()831 public boolean isEabProvisionedOnDevice() { 832 if (getBooleanCarrierConfig( 833 CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL)) { 834 return isEabProvisioned(); 835 } 836 837 return true; 838 } 839 840 /** 841 * Indicates whether VoWifi is provisioned on device. 842 * 843 * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not 844 * provisioned on device, this method returns false. 845 * 846 * @deprecated Does not support MSIM devices. Please use 847 * {@link #isWfcProvisionedOnDevice()} instead. 848 */ isWfcProvisionedOnDevice(Context context)849 public static boolean isWfcProvisionedOnDevice(Context context) { 850 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 851 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 852 if (mgr != null) { 853 return mgr.isWfcProvisionedOnDevice(); 854 } 855 Rlog.e(TAG, "isWfcProvisionedOnDevice: ImsManager null, returning default value."); 856 return true; 857 } 858 859 /** 860 * Indicates whether VoWifi is provisioned on slot. 861 * 862 * When CarrierConfig KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL is true, and VoLTE is not 863 * provisioned on device, this method returns false. 864 */ isWfcProvisionedOnDevice()865 public boolean isWfcProvisionedOnDevice() { 866 if (getBooleanCarrierConfig( 867 CarrierConfigManager.KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL)) { 868 if (!isVolteProvisionedOnDevice()) { 869 return false; 870 } 871 } 872 873 if (getBooleanCarrierConfig( 874 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) { 875 return isWfcProvisioned(); 876 } 877 878 return true; 879 } 880 881 /** 882 * Indicates whether VT is provisioned on device 883 * 884 * @deprecated Does not support MSIM devices. Please use 885 * {@link #isVtProvisionedOnDevice()} instead. 886 */ isVtProvisionedOnDevice(Context context)887 public static boolean isVtProvisionedOnDevice(Context context) { 888 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 889 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 890 if (mgr != null) { 891 return mgr.isVtProvisionedOnDevice(); 892 } 893 Rlog.e(TAG, "isVtProvisionedOnDevice: ImsManager null, returning default value."); 894 return true; 895 } 896 897 /** 898 * Indicates whether VT is provisioned on slot. 899 */ isVtProvisionedOnDevice()900 public boolean isVtProvisionedOnDevice() { 901 if (getBooleanCarrierConfig( 902 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) { 903 return isVtProvisioned(); 904 } 905 906 return true; 907 } 908 909 /** 910 * Returns a platform configuration for VT which may override the user setting. 911 * 912 * Note: VT presumes that VoLTE is enabled (these are configuration settings 913 * which must be done correctly). 914 * 915 * @deprecated Does not support MSIM devices. Please use 916 * {@link #isVtEnabledByPlatform()} instead. 917 */ isVtEnabledByPlatform(Context context)918 public static boolean isVtEnabledByPlatform(Context context) { 919 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 920 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 921 if (mgr != null) { 922 return mgr.isVtEnabledByPlatform(); 923 } 924 Rlog.e(TAG, "isVtEnabledByPlatform: ImsManager null, returning default value."); 925 return false; 926 } 927 928 /** 929 * Returns a platform configuration for VT which may override the user setting. 930 * 931 * Note: VT presumes that VoLTE is enabled (these are configuration settings 932 * which must be done correctly). 933 */ isVtEnabledByPlatform()934 public boolean isVtEnabledByPlatform() { 935 // We first read the per slot value. If doesn't exist, we read the general value. If still 936 // doesn't exist, we use the hardcoded default value. 937 if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE + 938 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1 || 939 SystemProperties.getInt( 940 PROPERTY_DBG_VT_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) { 941 return true; 942 } 943 944 return mContext.getResources().getBoolean( 945 com.android.internal.R.bool.config_device_vt_available) && 946 getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) && 947 isGbaValid(); 948 } 949 950 /** 951 * Returns the user configuration of VT setting 952 * @deprecated Does not support MSIM devices. Please use 953 * {@link #isVtEnabledByUser()} instead. 954 */ isVtEnabledByUser(Context context)955 public static boolean isVtEnabledByUser(Context context) { 956 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 957 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 958 if (mgr != null) { 959 return mgr.isVtEnabledByUser(); 960 } 961 Rlog.e(TAG, "isVtEnabledByUser: ImsManager null, returning default value."); 962 return false; 963 } 964 965 /** 966 * Returns the user configuration of VT setting per slot. If not set, it 967 * returns true as default value. 968 */ isVtEnabledByUser()969 public boolean isVtEnabledByUser() { 970 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 971 getSubId(), SubscriptionManager.VT_IMS_ENABLED, 972 SUB_PROPERTY_NOT_INITIALIZED); 973 974 // If it's never set, by default we return true. 975 return (setting == SUB_PROPERTY_NOT_INITIALIZED 976 || setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED); 977 } 978 979 /** 980 * Returns whether the user sets call composer setting per sub. 981 */ isCallComposerEnabledByUser()982 public boolean isCallComposerEnabledByUser() { 983 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 984 if (tm == null) { 985 loge("isCallComposerEnabledByUser: TelephonyManager is null, returning false"); 986 return false; 987 } 988 return tm.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON; 989 } 990 991 /** 992 * Change persistent VT enabled setting 993 * 994 * @deprecated Does not support MSIM devices. Please use {@link #setVtSetting(boolean)} instead. 995 */ setVtSetting(Context context, boolean enabled)996 public static void setVtSetting(Context context, boolean enabled) { 997 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 998 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 999 if (mgr != null) { 1000 mgr.setVtSetting(enabled); 1001 } 1002 Rlog.e(TAG, "setVtSetting: ImsManager null, can not set value."); 1003 } 1004 1005 /** 1006 * Change persistent VT enabled setting for slot. 1007 */ setVtSetting(boolean enabled)1008 public void setVtSetting(boolean enabled) { 1009 if (enabled && !isVtProvisionedOnDevice()) { 1010 log("setVtSetting: Not possible to enable Vt due to provisioning."); 1011 return; 1012 } 1013 1014 int subId = getSubId(); 1015 if (!isSubIdValid(subId)) { 1016 loge("setVtSetting: sub id invalid, skip modifying vt state in subinfo db; subId=" 1017 + subId); 1018 return; 1019 } 1020 mSubscriptionManagerProxy.setSubscriptionProperty(subId, SubscriptionManager.VT_IMS_ENABLED, 1021 booleanToPropertyString(enabled)); 1022 try { 1023 if (enabled) { 1024 CapabilityChangeRequest request = new CapabilityChangeRequest(); 1025 updateVideoCallFeatureValue(request, isNonTtyOrTtyOnVolteEnabled()); 1026 changeMmTelCapability(request); 1027 // ensure IMS is enabled. 1028 turnOnIms(); 1029 } else { 1030 // This may cause IMS to be disabled, re-evaluate all. 1031 reevaluateCapabilities(); 1032 } 1033 } catch (ImsException e) { 1034 // The ImsService is down. Since the SubscriptionManager already recorded the user's 1035 // preference, it will be resent in updateImsServiceConfig when the ImsPhoneCallTracker 1036 // reconnects. 1037 loge("setVtSetting(b): ", e); 1038 } 1039 } 1040 1041 /** 1042 * Returns whether turning off ims is allowed by platform. 1043 * The platform property may override the carrier config. 1044 */ isTurnOffImsAllowedByPlatform()1045 private boolean isTurnOffImsAllowedByPlatform() { 1046 // We first read the per slot value. If doesn't exist, we read the general value. If still 1047 // doesn't exist, we use the hardcoded default value. 1048 if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE + 1049 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1 || 1050 SystemProperties.getInt( 1051 PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) { 1052 return true; 1053 } 1054 1055 return getBooleanCarrierConfig( 1056 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL); 1057 } 1058 1059 /** 1060 * Returns the user configuration of WFC setting 1061 * 1062 * @deprecated Does not support MSIM devices. Please use 1063 * {@link #isWfcEnabledByUser()} instead. 1064 */ isWfcEnabledByUser(Context context)1065 public static boolean isWfcEnabledByUser(Context context) { 1066 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1067 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1068 if (mgr != null) { 1069 return mgr.isWfcEnabledByUser(); 1070 } 1071 Rlog.e(TAG, "isWfcEnabledByUser: ImsManager null, returning default value."); 1072 return true; 1073 } 1074 1075 /** 1076 * Returns the user configuration of WFC setting for slot. If not set, it 1077 * queries CarrierConfig value as default. 1078 */ isWfcEnabledByUser()1079 public boolean isWfcEnabledByUser() { 1080 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 1081 getSubId(), SubscriptionManager.WFC_IMS_ENABLED, 1082 SUB_PROPERTY_NOT_INITIALIZED); 1083 1084 // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db. 1085 if (setting == SUB_PROPERTY_NOT_INITIALIZED) { 1086 return getBooleanCarrierConfig( 1087 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL); 1088 } else { 1089 return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED; 1090 } 1091 } 1092 1093 /** 1094 * Change persistent WFC enabled setting. 1095 * @deprecated Does not support MSIM devices. Please use 1096 * {@link #setWfcSetting} instead. 1097 */ setWfcSetting(Context context, boolean enabled)1098 public static void setWfcSetting(Context context, boolean enabled) { 1099 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1100 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1101 if (mgr != null) { 1102 mgr.setWfcSetting(enabled); 1103 } 1104 Rlog.e(TAG, "setWfcSetting: ImsManager null, can not set value."); 1105 } 1106 1107 /** 1108 * Change persistent WFC enabled setting for slot. 1109 */ setWfcSetting(boolean enabled)1110 public void setWfcSetting(boolean enabled) { 1111 if (enabled && !isWfcProvisionedOnDevice()) { 1112 log("setWfcSetting: Not possible to enable WFC due to provisioning."); 1113 return; 1114 } 1115 int subId = getSubId(); 1116 if (!isSubIdValid(subId)) { 1117 loge("setWfcSetting: invalid sub id, can not set WFC setting in siminfo db; subId=" 1118 + subId); 1119 return; 1120 } 1121 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 1122 SubscriptionManager.WFC_IMS_ENABLED, booleanToPropertyString(enabled)); 1123 1124 try { 1125 if (enabled) { 1126 CapabilityChangeRequest request = new CapabilityChangeRequest(); 1127 updateVoiceWifiFeatureAndProvisionedValues(request); 1128 changeMmTelCapability(request); 1129 // Ensure IMS is on if this setting is updated. 1130 turnOnIms(); 1131 } else { 1132 // This may cause IMS to be disabled, re-evaluate all caps 1133 reevaluateCapabilities(); 1134 } 1135 } catch (ImsException e) { 1136 loge("setWfcSetting: " + e); 1137 } 1138 } 1139 1140 /** 1141 * @return true if the user's setting for Voice over Cross SIM is enabled and 1142 * false if it is not 1143 */ isCrossSimCallingEnabledByUser()1144 public boolean isCrossSimCallingEnabledByUser() { 1145 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 1146 getSubId(), SubscriptionManager.CROSS_SIM_CALLING_ENABLED, 1147 SUB_PROPERTY_NOT_INITIALIZED); 1148 1149 // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db. 1150 if (setting == SUB_PROPERTY_NOT_INITIALIZED) { 1151 return false; 1152 } else { 1153 return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED; 1154 } 1155 } 1156 1157 /** 1158 * @return true if Voice over Cross SIM is provisioned and enabled by user and platform. 1159 * false if any of them is not true 1160 */ isCrossSimCallingEnabled()1161 public boolean isCrossSimCallingEnabled() { 1162 boolean userEnabled = isCrossSimCallingEnabledByUser(); 1163 boolean platformEnabled = isCrossSimEnabledByPlatform(); 1164 boolean isProvisioned = isWfcProvisionedOnDevice(); 1165 1166 log("isCrossSimCallingEnabled: platformEnabled = " + platformEnabled 1167 + ", provisioned = " + isProvisioned 1168 + ", userEnabled = " + userEnabled); 1169 return userEnabled && platformEnabled && isProvisioned; 1170 } 1171 1172 /** 1173 * Sets the user's setting for whether or not Voice over Cross SIM is enabled. 1174 */ setCrossSimCallingEnabled(boolean enabled)1175 public void setCrossSimCallingEnabled(boolean enabled) { 1176 if (enabled && !isWfcProvisionedOnDevice()) { 1177 log("setCrossSimCallingEnabled: Not possible to enable WFC due to provisioning."); 1178 return; 1179 } 1180 int subId = getSubId(); 1181 if (!isSubIdValid(subId)) { 1182 loge("setCrossSimCallingEnabled: " 1183 + "invalid sub id, can not set Cross SIM setting in siminfo db; subId=" 1184 + subId); 1185 return; 1186 } 1187 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 1188 SubscriptionManager.CROSS_SIM_CALLING_ENABLED, booleanToPropertyString(enabled)); 1189 try { 1190 if (enabled) { 1191 CapabilityChangeRequest request = new CapabilityChangeRequest(); 1192 updateCrossSimFeatureAndProvisionedValues(request); 1193 changeMmTelCapability(request); 1194 turnOnIms(); 1195 } else { 1196 // Recalculate all caps to determine if IMS needs to be disabled. 1197 reevaluateCapabilities(); 1198 } 1199 } catch (ImsException e) { 1200 loge("setCrossSimCallingEnabled(): ", e); 1201 } 1202 } 1203 1204 /** 1205 * Non-persistently change WFC enabled setting and WFC mode for slot 1206 * 1207 * @param enabled If true, WFC and WFC while roaming will be enabled for the associated 1208 * subscription, if supported by the carrier. If false, WFC will be disabled for 1209 * the associated subscription. 1210 * @param wfcMode The WFC preference if WFC is enabled 1211 */ setWfcNonPersistent(boolean enabled, int wfcMode)1212 public void setWfcNonPersistent(boolean enabled, int wfcMode) { 1213 // Force IMS to register over LTE when turning off WFC 1214 int imsWfcModeFeatureValue = 1215 enabled ? wfcMode : ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED; 1216 try { 1217 changeMmTelCapability(enabled, MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1218 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN); 1219 // Set the mode and roaming enabled settings before turning on IMS 1220 setWfcModeInternal(imsWfcModeFeatureValue); 1221 // If enabled is false, shortcut to false because of the ImsService 1222 // implementation for WFC roaming, otherwise use the correct user's setting. 1223 setWfcRoamingSettingInternal(enabled && isWfcRoamingEnabledByUser()); 1224 // Do not re-evaluate all capabilities because this is a temporary override of WFC 1225 // settings. 1226 if (enabled) { 1227 log("setWfcNonPersistent() : turnOnIms"); 1228 // Ensure IMS is turned on if this is enabled. 1229 turnOnIms(); 1230 } 1231 } catch (ImsException e) { 1232 loge("setWfcNonPersistent(): ", e); 1233 } 1234 } 1235 1236 /** 1237 * Returns the user configuration of WFC preference setting. 1238 * 1239 * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean roaming)} instead. 1240 */ getWfcMode(Context context)1241 public static int getWfcMode(Context context) { 1242 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1243 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1244 if (mgr != null) { 1245 return mgr.getWfcMode(); 1246 } 1247 Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value."); 1248 return ImsMmTelManager.WIFI_MODE_WIFI_ONLY; 1249 } 1250 1251 /** 1252 * Returns the user configuration of WFC preference setting 1253 * @deprecated. Use {@link #getWfcMode(boolean roaming)} instead. 1254 */ getWfcMode()1255 public int getWfcMode() { 1256 return getWfcMode(false); 1257 } 1258 1259 /** 1260 * Change persistent WFC preference setting. 1261 * 1262 * @deprecated Doesn't support MSIM devices. Use {@link #setWfcMode(int)} instead. 1263 */ setWfcMode(Context context, int wfcMode)1264 public static void setWfcMode(Context context, int wfcMode) { 1265 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1266 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1267 if (mgr != null) { 1268 mgr.setWfcMode(wfcMode); 1269 } 1270 Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value."); 1271 } 1272 1273 /** 1274 * Change persistent WFC preference setting for slot when not roaming. 1275 * @deprecated Use {@link #setWfcMode(int, boolean)} instead. 1276 */ setWfcMode(int wfcMode)1277 public void setWfcMode(int wfcMode) { 1278 setWfcMode(wfcMode, false /*isRoaming*/); 1279 } 1280 1281 /** 1282 * Returns the user configuration of WFC preference setting 1283 * 1284 * @param roaming {@code false} for home network setting, {@code true} for roaming setting 1285 * 1286 * @deprecated Doesn't support MSIM devices. Use {@link #getWfcMode(boolean)} instead. 1287 */ getWfcMode(Context context, boolean roaming)1288 public static int getWfcMode(Context context, boolean roaming) { 1289 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1290 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1291 if (mgr != null) { 1292 return mgr.getWfcMode(roaming); 1293 } 1294 Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value."); 1295 return ImsMmTelManager.WIFI_MODE_WIFI_ONLY; 1296 } 1297 1298 /** 1299 * Returns the user configuration of WFC preference setting for slot. If not set, it 1300 * queries CarrierConfig value as default. 1301 * 1302 * @param roaming {@code false} for home network setting, {@code true} for roaming setting 1303 */ getWfcMode(boolean roaming)1304 public int getWfcMode(boolean roaming) { 1305 int setting; 1306 if (!roaming) { 1307 // The WFC mode is not editable, return the default setting in the CarrierConfig, not 1308 // the user set value. 1309 if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL)) { 1310 setting = getIntCarrierConfig( 1311 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT); 1312 1313 } else { 1314 setting = getSettingFromSubscriptionManager(SubscriptionManager.WFC_IMS_MODE, 1315 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT); 1316 } 1317 if (DBG) log("getWfcMode - setting=" + setting); 1318 } else { 1319 if (getBooleanCarrierConfig( 1320 CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)) { 1321 setting = getWfcMode(false); 1322 } else if (!getBooleanCarrierConfig( 1323 CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL)) { 1324 setting = getIntCarrierConfig( 1325 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT); 1326 } else { 1327 setting = getSettingFromSubscriptionManager( 1328 SubscriptionManager.WFC_IMS_ROAMING_MODE, 1329 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT); 1330 } 1331 if (DBG) log("getWfcMode (roaming) - setting=" + setting); 1332 } 1333 return setting; 1334 } 1335 1336 /** 1337 * Returns the SubscriptionManager setting for the subSetting string. If it is not set, default 1338 * to the default CarrierConfig value for defaultConfigKey. 1339 */ getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey)1340 private int getSettingFromSubscriptionManager(String subSetting, String defaultConfigKey) { 1341 int result; 1342 result = mSubscriptionManagerProxy.getIntegerSubscriptionProperty(getSubId(), subSetting, 1343 SUB_PROPERTY_NOT_INITIALIZED); 1344 1345 // SUB_PROPERTY_NOT_INITIALIZED indicates it's never set in sub db. 1346 if (result == SUB_PROPERTY_NOT_INITIALIZED) { 1347 result = getIntCarrierConfig(defaultConfigKey); 1348 } 1349 return result; 1350 } 1351 1352 /** 1353 * Change persistent WFC preference setting 1354 * 1355 * @param roaming {@code false} for home network setting, {@code true} for roaming setting 1356 * 1357 * @deprecated Doesn't support MSIM devices. Please use {@link #setWfcMode(int, boolean)} 1358 * instead. 1359 */ setWfcMode(Context context, int wfcMode, boolean roaming)1360 public static void setWfcMode(Context context, int wfcMode, boolean roaming) { 1361 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1362 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1363 if (mgr != null) { 1364 mgr.setWfcMode(wfcMode, roaming); 1365 } 1366 Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value."); 1367 } 1368 1369 /** 1370 * Change persistent WFC preference setting 1371 * 1372 * @param roaming {@code false} for home network setting, {@code true} for roaming setting 1373 */ setWfcMode(int wfcMode, boolean roaming)1374 public void setWfcMode(int wfcMode, boolean roaming) { 1375 int subId = getSubId(); 1376 if (isSubIdValid(subId)) { 1377 if (!roaming) { 1378 if (DBG) log("setWfcMode(i,b) - setting=" + wfcMode); 1379 mSubscriptionManagerProxy.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_MODE, 1380 Integer.toString(wfcMode)); 1381 } else { 1382 if (DBG) log("setWfcMode(i,b) (roaming) - setting=" + wfcMode); 1383 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 1384 SubscriptionManager.WFC_IMS_ROAMING_MODE, Integer.toString(wfcMode)); 1385 } 1386 } else { 1387 loge("setWfcMode(i,b): invalid sub id, skip setting setting in siminfo db; subId=" 1388 + subId); 1389 } 1390 1391 TelephonyManager tm = (TelephonyManager) 1392 mContext.getSystemService(Context.TELEPHONY_SERVICE); 1393 if (tm == null) { 1394 loge("setWfcMode: TelephonyManager is null, can not set WFC."); 1395 return; 1396 } 1397 tm = tm.createForSubscriptionId(getSubId()); 1398 // Unfortunately, the WFC mode is the same for home/roaming (we do not have separate 1399 // config keys), so we have to change the WFC mode when moving home<->roaming. So, only 1400 // call setWfcModeInternal when roaming == telephony roaming status. Otherwise, ignore. 1401 if (roaming == tm.isNetworkRoaming()) { 1402 setWfcModeInternal(wfcMode); 1403 } 1404 } 1405 getSubId()1406 private int getSubId() { 1407 int[] subIds = mSubscriptionManagerProxy.getSubscriptionIds(mPhoneId); 1408 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 1409 if (subIds != null && subIds.length >= 1) { 1410 subId = subIds[0]; 1411 } 1412 return subId; 1413 } 1414 setWfcModeInternal(int wfcMode)1415 private void setWfcModeInternal(int wfcMode) { 1416 final int value = wfcMode; 1417 getImsThreadExecutor().execute(() -> { 1418 try { 1419 getConfigInterface().setConfig( 1420 ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE, value); 1421 } catch (ImsException e) { 1422 // do nothing 1423 } 1424 }); 1425 } 1426 1427 /** 1428 * Returns the user configuration of WFC roaming setting 1429 * 1430 * @deprecated Does not support MSIM devices. Please use 1431 * {@link #isWfcRoamingEnabledByUser()} instead. 1432 */ isWfcRoamingEnabledByUser(Context context)1433 public static boolean isWfcRoamingEnabledByUser(Context context) { 1434 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1435 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1436 if (mgr != null) { 1437 return mgr.isWfcRoamingEnabledByUser(); 1438 } 1439 Rlog.e(TAG, "isWfcRoamingEnabledByUser: ImsManager null, returning default value."); 1440 return false; 1441 } 1442 1443 /** 1444 * Returns the user configuration of WFC roaming setting for slot. If not set, it 1445 * queries CarrierConfig value as default. 1446 */ isWfcRoamingEnabledByUser()1447 public boolean isWfcRoamingEnabledByUser() { 1448 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 1449 getSubId(), SubscriptionManager.WFC_IMS_ROAMING_ENABLED, 1450 SUB_PROPERTY_NOT_INITIALIZED); 1451 if (setting == SUB_PROPERTY_NOT_INITIALIZED) { 1452 return getBooleanCarrierConfig( 1453 CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL); 1454 } else { 1455 return setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED; 1456 } 1457 } 1458 1459 /** 1460 * Change persistent WFC roaming enabled setting 1461 */ setWfcRoamingSetting(Context context, boolean enabled)1462 public static void setWfcRoamingSetting(Context context, boolean enabled) { 1463 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1464 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1465 if (mgr != null) { 1466 mgr.setWfcRoamingSetting(enabled); 1467 } 1468 Rlog.e(TAG, "setWfcRoamingSetting: ImsManager null, value not set."); 1469 } 1470 1471 /** 1472 * Change persistent WFC roaming enabled setting 1473 */ setWfcRoamingSetting(boolean enabled)1474 public void setWfcRoamingSetting(boolean enabled) { 1475 mSubscriptionManagerProxy.setSubscriptionProperty(getSubId(), 1476 SubscriptionManager.WFC_IMS_ROAMING_ENABLED, booleanToPropertyString(enabled) 1477 ); 1478 1479 setWfcRoamingSettingInternal(enabled); 1480 } 1481 setWfcRoamingSettingInternal(boolean enabled)1482 private void setWfcRoamingSettingInternal(boolean enabled) { 1483 final int value = enabled 1484 ? ProvisioningManager.PROVISIONING_VALUE_ENABLED 1485 : ProvisioningManager.PROVISIONING_VALUE_DISABLED; 1486 getImsThreadExecutor().execute(() -> { 1487 try { 1488 getConfigInterface().setConfig( 1489 ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE, value); 1490 } catch (ImsException e) { 1491 // do nothing 1492 } 1493 }); 1494 } 1495 1496 /** 1497 * Returns a platform configuration for WFC which may override the user 1498 * setting. Note: WFC presumes that VoLTE is enabled (these are 1499 * configuration settings which must be done correctly). 1500 * 1501 * @deprecated Doesn't work for MSIM devices. Use {@link #isWfcEnabledByPlatform()} 1502 * instead. 1503 */ isWfcEnabledByPlatform(Context context)1504 public static boolean isWfcEnabledByPlatform(Context context) { 1505 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 1506 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 1507 if (mgr != null) { 1508 return mgr.isWfcEnabledByPlatform(); 1509 } 1510 Rlog.e(TAG, "isWfcEnabledByPlatform: ImsManager null, returning default value."); 1511 return false; 1512 } 1513 1514 /** 1515 * Returns a platform configuration for WFC which may override the user 1516 * setting per slot. Note: WFC presumes that VoLTE is enabled (these are 1517 * configuration settings which must be done correctly). 1518 */ isWfcEnabledByPlatform()1519 public boolean isWfcEnabledByPlatform() { 1520 // We first read the per slot value. If doesn't exist, we read the general value. If still 1521 // doesn't exist, we use the hardcoded default value. 1522 if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE + 1523 Integer.toString(mPhoneId), SYSTEM_PROPERTY_NOT_SET) == 1 || 1524 SystemProperties.getInt( 1525 PROPERTY_DBG_WFC_AVAIL_OVERRIDE, SYSTEM_PROPERTY_NOT_SET) == 1) { 1526 return true; 1527 } 1528 1529 return mContext.getResources().getBoolean( 1530 com.android.internal.R.bool.config_device_wfc_ims_available) && 1531 getBooleanCarrierConfig( 1532 CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) && 1533 isGbaValid(); 1534 } 1535 1536 /** 1537 * Returns a platform configuration for Cross SIM which may override the user 1538 * setting per slot. Note: Cross SIM presumes that VoLTE is enabled (these are 1539 * configuration settings which must be done correctly). 1540 */ isCrossSimEnabledByPlatform()1541 public boolean isCrossSimEnabledByPlatform() { 1542 if (isWfcEnabledByPlatform()) { 1543 return getBooleanCarrierConfig( 1544 CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL); 1545 } 1546 return false; 1547 } 1548 isSuppServicesOverUtEnabledByPlatform()1549 public boolean isSuppServicesOverUtEnabledByPlatform() { 1550 TelephonyManager manager = (TelephonyManager) mContext.getSystemService( 1551 Context.TELEPHONY_SERVICE); 1552 int cardState = manager.getSimState(mPhoneId); 1553 if (cardState != TelephonyManager.SIM_STATE_READY) { 1554 // Do not report enabled until we actually have an active subscription. 1555 return false; 1556 } 1557 return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL) && 1558 isGbaValid(); 1559 } 1560 1561 /** 1562 * If carrier requires that IMS is only available if GBA capable SIM is used, 1563 * then this function checks GBA bit in EF IST. 1564 * 1565 * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7). 1566 */ isGbaValid()1567 private boolean isGbaValid() { 1568 if (getBooleanCarrierConfig( 1569 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) { 1570 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 1571 if (tm == null) { 1572 loge("isGbaValid: TelephonyManager is null, returning false."); 1573 return false; 1574 } 1575 tm = tm.createForSubscriptionId(getSubId()); 1576 String efIst = tm.getIsimIst(); 1577 if (efIst == null) { 1578 loge("isGbaValid - ISF is NULL"); 1579 return true; 1580 } 1581 boolean result = efIst != null && efIst.length() > 1 && 1582 (0x02 & (byte)efIst.charAt(1)) != 0; 1583 if (DBG) log("isGbaValid - GBA capable=" + result + ", ISF=" + efIst); 1584 return result; 1585 } 1586 return true; 1587 } 1588 1589 /** 1590 * Will return with config value or throw an ImsException if we receive an error from 1591 * ImsConfig for that value. 1592 */ getProvisionedBool(ImsConfig config, int item)1593 private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException { 1594 int value = config.getProvisionedValue(item); 1595 if (value == ImsConfigImplBase.CONFIG_RESULT_UNKNOWN) { 1596 throw new ImsException("getProvisionedBool failed with error for item: " + item, 1597 ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR); 1598 } 1599 return value == ProvisioningManager.PROVISIONING_VALUE_ENABLED; 1600 } 1601 1602 /** 1603 * Will set config value or throw an ImsException if we receive an error from ImsConfig for that 1604 * value. 1605 */ setProvisionedBool(ImsConfig config, int item, int value)1606 private void setProvisionedBool(ImsConfig config, int item, int value) throws ImsException { 1607 int result = config.setConfig(item, value); 1608 if (result != ImsConfigImplBase.CONFIG_RESULT_SUCCESS) { 1609 throw new ImsException("setProvisionedBool failed with error for item: " + item, 1610 ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR); 1611 } 1612 } 1613 1614 /** 1615 * Will return with config value or return false if we receive an error from 1616 * ImsConfigImplBase implementation for that value. 1617 */ getProvisionedBoolNoException(int item)1618 private boolean getProvisionedBoolNoException(int item) { 1619 try { 1620 ImsConfig config = getConfigInterface(); 1621 return getProvisionedBool(config, item); 1622 } catch (ImsException ex) { 1623 logw("getProvisionedBoolNoException: operation failed for item=" + item 1624 + ". Exception:" + ex.getMessage() + ". Returning false."); 1625 return false; 1626 } 1627 } 1628 1629 /** 1630 * Will return with config value or return false if we receive an error from 1631 * ImsConfigImplBase implementation for that value. 1632 */ setProvisionedBoolNoException(int item, int value)1633 private boolean setProvisionedBoolNoException(int item, int value) { 1634 try { 1635 ImsConfig config = getConfigInterface(); 1636 setProvisionedBool(config, item, value); 1637 } catch (ImsException ex) { 1638 logw("setProvisionedBoolNoException: operation failed for item=" + item 1639 + ", value=" + value + ". Exception:" + ex.getMessage()); 1640 return false; 1641 } 1642 return true; 1643 } 1644 1645 /** 1646 * Push configuration updates to the ImsService implementation. 1647 */ updateImsServiceConfig()1648 public void updateImsServiceConfig() { 1649 try { 1650 int subId = getSubId(); 1651 if (!isSubIdValid(subId)) { 1652 loge("updateImsServiceConfig: invalid sub id, skipping!"); 1653 return; 1654 } 1655 PersistableBundle imsCarrierConfigs = 1656 mConfigManager.getConfigByComponentForSubId( 1657 CarrierConfigManager.Ims.KEY_PREFIX, subId); 1658 updateImsCarrierConfigs(imsCarrierConfigs); 1659 reevaluateCapabilities(); 1660 mConfigUpdated = true; 1661 } catch (ImsException e) { 1662 loge("updateImsServiceConfig: ", e); 1663 mConfigUpdated = false; 1664 } 1665 } 1666 1667 /** 1668 * Evaluate the state of the IMS capabilities and push the updated state to the ImsService. 1669 */ reevaluateCapabilities()1670 private void reevaluateCapabilities() throws ImsException { 1671 logi("reevaluateCapabilities"); 1672 CapabilityChangeRequest request = new CapabilityChangeRequest(); 1673 boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(); 1674 updateVoiceCellFeatureValue(request, isNonTty); 1675 updateVoiceWifiFeatureAndProvisionedValues(request); 1676 updateCrossSimFeatureAndProvisionedValues(request); 1677 updateVideoCallFeatureValue(request, isNonTty); 1678 updateCallComposerFeatureValue(request); 1679 // Only turn on IMS for RTT if there's an active subscription present. If not, the 1680 // modem will be in emergency-call-only mode and will use separate signaling to 1681 // establish an RTT emergency call. 1682 boolean isImsNeededForRtt = updateRttConfigValue() && isActiveSubscriptionPresent(); 1683 // Supplementary services over UT do not require IMS registration. Do not alter IMS 1684 // registration based on UT. 1685 updateUtFeatureValue(request); 1686 1687 // Send the batched request to the modem. 1688 changeMmTelCapability(request); 1689 1690 if (isImsNeededForRtt || !isTurnOffImsAllowedByPlatform() || isImsNeeded(request)) { 1691 // Turn on IMS if it is used. 1692 // Also, if turning off is not allowed for current carrier, 1693 // we need to turn IMS on because it might be turned off before 1694 // phone switched to current carrier. 1695 log("reevaluateCapabilities: turnOnIms"); 1696 turnOnIms(); 1697 } else { 1698 // Turn off IMS if it is not used AND turning off is allowed for carrier. 1699 log("reevaluateCapabilities: turnOffIms"); 1700 turnOffIms(); 1701 } 1702 } 1703 1704 /** 1705 * @return {@code true} if IMS needs to be turned on for the request, {@code false} if it can 1706 * be disabled. 1707 */ isImsNeeded(CapabilityChangeRequest r)1708 private boolean isImsNeeded(CapabilityChangeRequest r) { 1709 return r.getCapabilitiesToEnable().stream() 1710 .anyMatch(c -> isImsNeededForCapability(c.getCapability())); 1711 } 1712 1713 /** 1714 * @return {@code true} if IMS needs to be turned on for the capability. 1715 */ isImsNeededForCapability(int capability)1716 private boolean isImsNeededForCapability(int capability) { 1717 // UT does not require IMS to be enabled. 1718 return capability != MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT && 1719 // call composer is used as part of calling, so it should not trigger the enablement 1720 // of IMS. 1721 capability != MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER; 1722 } 1723 1724 /** 1725 * Update VoLTE config 1726 */ updateVoiceCellFeatureValue(CapabilityChangeRequest request, boolean isNonTty)1727 private void updateVoiceCellFeatureValue(CapabilityChangeRequest request, boolean isNonTty) { 1728 boolean available = isVolteEnabledByPlatform(); 1729 boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(); 1730 boolean isProvisioned = isVolteProvisionedOnDevice(); 1731 boolean voLteFeatureOn = available && enabled && isNonTty && isProvisioned; 1732 boolean voNrAvailable = isImsOverNrEnabledByPlatform(); 1733 1734 log("updateVoiceCellFeatureValue: available = " + available 1735 + ", enabled = " + enabled 1736 + ", nonTTY = " + isNonTty 1737 + ", provisioned = " + isProvisioned 1738 + ", voLteFeatureOn = " + voLteFeatureOn 1739 + ", voNrAvailable = " + voNrAvailable); 1740 1741 if (voLteFeatureOn) { 1742 request.addCapabilitiesToEnableForTech( 1743 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1744 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1745 } else { 1746 request.addCapabilitiesToDisableForTech( 1747 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1748 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1749 } 1750 if (voLteFeatureOn && voNrAvailable) { 1751 request.addCapabilitiesToEnableForTech( 1752 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1753 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1754 } else { 1755 request.addCapabilitiesToDisableForTech( 1756 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1757 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1758 } 1759 } 1760 1761 /** 1762 * Update video call configuration 1763 */ updateVideoCallFeatureValue(CapabilityChangeRequest request, boolean isNonTty)1764 private void updateVideoCallFeatureValue(CapabilityChangeRequest request, boolean isNonTty) { 1765 boolean available = isVtEnabledByPlatform(); 1766 boolean vtEnabled = isVtEnabledByUser(); 1767 boolean advancedEnabled = isEnhanced4gLteModeSettingEnabledByUser(); 1768 boolean isDataEnabled = isDataEnabled(); 1769 boolean ignoreDataEnabledChanged = getBooleanCarrierConfig( 1770 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS); 1771 boolean isProvisioned = isVtProvisionedOnDevice(); 1772 // TODO: Support carrier config setting about if VT settings should be associated with 1773 // advanced calling settings. 1774 boolean isLteFeatureOn = available && vtEnabled && isNonTty && isProvisioned 1775 && advancedEnabled && (ignoreDataEnabledChanged || isDataEnabled); 1776 boolean nrAvailable = isImsOverNrEnabledByPlatform(); 1777 1778 log("updateVideoCallFeatureValue: available = " + available 1779 + ", vtenabled = " + vtEnabled 1780 + ", advancedCallEnabled = " + advancedEnabled 1781 + ", nonTTY = " + isNonTty 1782 + ", data enabled = " + isDataEnabled 1783 + ", provisioned = " + isProvisioned 1784 + ", isLteFeatureOn = " + isLteFeatureOn 1785 + ", nrAvailable = " + nrAvailable); 1786 1787 if (isLteFeatureOn) { 1788 request.addCapabilitiesToEnableForTech( 1789 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, 1790 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1791 // VT does not differentiate transport today, do not set IWLAN. 1792 } else { 1793 request.addCapabilitiesToDisableForTech( 1794 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, 1795 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1796 // VT does not differentiate transport today, do not set IWLAN. 1797 } 1798 1799 if (isLteFeatureOn && nrAvailable) { 1800 request.addCapabilitiesToEnableForTech( 1801 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, 1802 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1803 } else { 1804 request.addCapabilitiesToDisableForTech( 1805 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, 1806 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1807 } 1808 } 1809 1810 /** 1811 * Update WFC config 1812 */ updateVoiceWifiFeatureAndProvisionedValues(CapabilityChangeRequest request)1813 private void updateVoiceWifiFeatureAndProvisionedValues(CapabilityChangeRequest request) { 1814 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 1815 boolean isNetworkRoaming = false; 1816 if (tm == null) { 1817 loge("updateVoiceWifiFeatureAndProvisionedValues: TelephonyManager is null, assuming" 1818 + " not roaming."); 1819 } else { 1820 tm = tm.createForSubscriptionId(getSubId()); 1821 isNetworkRoaming = tm.isNetworkRoaming(); 1822 } 1823 1824 boolean available = isWfcEnabledByPlatform(); 1825 boolean enabled = isWfcEnabledByUser(); 1826 boolean isProvisioned = isWfcProvisionedOnDevice(); 1827 int mode = getWfcMode(isNetworkRoaming); 1828 boolean roaming = isWfcRoamingEnabledByUser(); 1829 boolean isFeatureOn = available && enabled && isProvisioned; 1830 1831 log("updateWfcFeatureAndProvisionedValues: available = " + available 1832 + ", enabled = " + enabled 1833 + ", mode = " + mode 1834 + ", provisioned = " + isProvisioned 1835 + ", roaming = " + roaming 1836 + ", isFeatureOn = " + isFeatureOn); 1837 1838 if (isFeatureOn) { 1839 request.addCapabilitiesToEnableForTech( 1840 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1841 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN); 1842 } else { 1843 request.addCapabilitiesToDisableForTech( 1844 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1845 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN); 1846 } 1847 1848 if (!isFeatureOn) { 1849 mode = ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED; 1850 roaming = false; 1851 } 1852 setWfcModeInternal(mode); 1853 setWfcRoamingSettingInternal(roaming); 1854 } 1855 1856 /** 1857 * Update Cross SIM config 1858 */ updateCrossSimFeatureAndProvisionedValues(CapabilityChangeRequest request)1859 private void updateCrossSimFeatureAndProvisionedValues(CapabilityChangeRequest request) { 1860 if (isCrossSimCallingEnabled()) { 1861 request.addCapabilitiesToEnableForTech( 1862 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1863 ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM); 1864 } else { 1865 request.addCapabilitiesToDisableForTech( 1866 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, 1867 ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM); 1868 } 1869 } 1870 1871 updateUtFeatureValue(CapabilityChangeRequest request)1872 private void updateUtFeatureValue(CapabilityChangeRequest request) { 1873 boolean isCarrierSupported = isSuppServicesOverUtEnabledByPlatform(); 1874 boolean requiresProvisioning = getBooleanCarrierConfig( 1875 CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL); 1876 // Count as "provisioned" if we do not require provisioning. 1877 boolean isProvisioned = true; 1878 if (requiresProvisioning) { 1879 ITelephony telephony = getITelephony(); 1880 // Only track UT over LTE, since we do not differentiate between UT over LTE and IWLAN 1881 // currently. 1882 try { 1883 if (telephony != null) { 1884 isProvisioned = telephony.isMmTelCapabilityProvisionedInCache(getSubId(), 1885 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, 1886 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1887 } 1888 } catch (RemoteException e) { 1889 loge("updateUtFeatureValue: couldn't reach telephony! returning provisioned"); 1890 } 1891 } 1892 boolean isFeatureOn = isCarrierSupported && isProvisioned; 1893 1894 log("updateUtFeatureValue: available = " + isCarrierSupported 1895 + ", isProvisioned = " + isProvisioned 1896 + ", enabled = " + isFeatureOn); 1897 1898 if (isFeatureOn) { 1899 request.addCapabilitiesToEnableForTech( 1900 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, 1901 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1902 } else { 1903 request.addCapabilitiesToDisableForTech( 1904 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, 1905 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1906 } 1907 } 1908 1909 /** 1910 * Update call composer capability 1911 */ updateCallComposerFeatureValue(CapabilityChangeRequest request)1912 private void updateCallComposerFeatureValue(CapabilityChangeRequest request) { 1913 boolean isUserSetEnabled = isCallComposerEnabledByUser(); 1914 boolean isCarrierConfigEnabled = getBooleanCarrierConfig( 1915 CarrierConfigManager.KEY_SUPPORTS_CALL_COMPOSER_BOOL); 1916 1917 boolean isFeatureOn = isUserSetEnabled && isCarrierConfigEnabled; 1918 boolean nrAvailable = isImsOverNrEnabledByPlatform(); 1919 1920 log("updateCallComposerFeatureValue: isUserSetEnabled = " + isUserSetEnabled 1921 + ", isCarrierConfigEnabled = " + isCarrierConfigEnabled 1922 + ", isFeatureOn = " + isFeatureOn 1923 + ", nrAvailable = " + nrAvailable); 1924 1925 if (isFeatureOn) { 1926 request.addCapabilitiesToEnableForTech( 1927 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER, 1928 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1929 } else { 1930 request.addCapabilitiesToDisableForTech( 1931 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER, 1932 ImsRegistrationImplBase.REGISTRATION_TECH_LTE); 1933 } 1934 if (isFeatureOn && nrAvailable) { 1935 request.addCapabilitiesToEnableForTech( 1936 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER, 1937 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1938 } else { 1939 request.addCapabilitiesToDisableForTech( 1940 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_CALL_COMPOSER, 1941 ImsRegistrationImplBase.REGISTRATION_TECH_NR); 1942 } 1943 } 1944 1945 /** 1946 * Do NOT use this directly, instead use {@link #getInstance(Context, int)}. 1947 */ ImsManager(Context context, int phoneId)1948 private ImsManager(Context context, int phoneId) { 1949 mContext = context; 1950 mPhoneId = phoneId; 1951 mSubscriptionManagerProxy = new DefaultSubscriptionManagerProxy(context); 1952 mSettingsProxy = new DefaultSettingsProxy(); 1953 mConfigManager = (CarrierConfigManager) context.getSystemService( 1954 Context.CARRIER_CONFIG_SERVICE); 1955 mExecutor = new LazyExecutor(); 1956 mBinderCache = new BinderCacheManager<>(ImsManager::getITelephonyInterface); 1957 // Start off with an empty MmTelFeatureConnection, which will be replaced one an 1958 // ImsService is available (ImsManager expects a non-null FeatureConnection) 1959 associate(null /*container*/); 1960 } 1961 1962 /** 1963 * Used for testing only to inject dependencies. 1964 */ 1965 @VisibleForTesting ImsManager(Context context, int phoneId, MmTelFeatureConnectionFactory factory, SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy)1966 public ImsManager(Context context, int phoneId, MmTelFeatureConnectionFactory factory, 1967 SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy) { 1968 mContext = context; 1969 mPhoneId = phoneId; 1970 mMmTelFeatureConnectionFactory = factory; 1971 mSubscriptionManagerProxy = subManagerProxy; 1972 mSettingsProxy = settingsProxy; 1973 mConfigManager = (CarrierConfigManager) context.getSystemService( 1974 Context.CARRIER_CONFIG_SERVICE); 1975 // Do not multithread tests 1976 mExecutor = Runnable::run; 1977 mBinderCache = new BinderCacheManager<>(ImsManager::getITelephonyInterface); 1978 // MmTelFeatureConnection should be replaced for tests with mMmTelFeatureConnectionFactory. 1979 associate(null /*container*/); 1980 } 1981 1982 /* 1983 * Returns a flag indicating whether the IMS service is available. 1984 */ isServiceAvailable()1985 public boolean isServiceAvailable() { 1986 return mMmTelConnectionRef.get().isBinderAlive(); 1987 } 1988 1989 /* 1990 * Returns a flag indicating whether the IMS service is ready to send requests to lower layers. 1991 */ isServiceReady()1992 public boolean isServiceReady() { 1993 return mMmTelConnectionRef.get().isBinderReady(); 1994 } 1995 1996 /** 1997 * Opens the IMS service for making calls and/or receiving generic IMS calls as well as 1998 * register listeners for ECBM, Multiendpoint, and UT if the ImsService supports it. 1999 * <p> 2000 * The caller may make subsequent calls through {@link #makeCall}. 2001 * The IMS service will register the device to the operator's network with the credentials 2002 * (from ISIM) periodically in order to receive calls from the operator's network. 2003 * When the IMS service receives a new call, it will call 2004 * {@link MmTelFeature.Listener#onIncomingCall} 2005 * @param listener A {@link MmTelFeature.Listener}, which is the interface the 2006 * {@link MmTelFeature} uses to notify the framework of updates. 2007 * @param ecbmListener Listener used for ECBM indications. 2008 * @param multiEndpointListener Listener used for multiEndpoint indications. 2009 * @throws NullPointerException if {@code listener} is null 2010 * @throws ImsException if calling the IMS service results in an error 2011 */ open(MmTelFeature.Listener listener, ImsEcbmStateListener ecbmListener, ImsExternalCallStateListener multiEndpointListener)2012 public void open(MmTelFeature.Listener listener, ImsEcbmStateListener ecbmListener, 2013 ImsExternalCallStateListener multiEndpointListener) throws ImsException { 2014 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2015 2016 if (listener == null) { 2017 throw new NullPointerException("listener can't be null"); 2018 } 2019 2020 try { 2021 c.openConnection(listener, ecbmListener, multiEndpointListener); 2022 } catch (RemoteException e) { 2023 throw new ImsException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2024 } 2025 } 2026 2027 /** 2028 * Adds registration listener to the IMS service. 2029 * 2030 * @param serviceClass a service class specified in {@link ImsServiceClass} 2031 * For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}. 2032 * @param listener To listen to IMS registration events; It cannot be null 2033 * @throws NullPointerException if {@code listener} is null 2034 * @throws ImsException if calling the IMS service results in an error 2035 * 2036 * @deprecated Use {@link #addRegistrationListener(ImsConnectionStateListener)} instead. 2037 */ addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)2038 public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener) 2039 throws ImsException { 2040 addRegistrationListener(listener); 2041 } 2042 2043 /** 2044 * Adds registration listener to the IMS service. 2045 * 2046 * @param listener To listen to IMS registration events; It cannot be null 2047 * @throws NullPointerException if {@code listener} is null 2048 * @throws ImsException if calling the IMS service results in an error 2049 * @deprecated use {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback, 2050 * Executor)} instead. 2051 */ addRegistrationListener(ImsConnectionStateListener listener)2052 public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException { 2053 if (listener == null) { 2054 throw new NullPointerException("listener can't be null"); 2055 } 2056 addRegistrationCallback(listener, getImsThreadExecutor()); 2057 // connect the ImsConnectionStateListener to the new CapabilityCallback. 2058 addCapabilitiesCallback(new ImsMmTelManager.CapabilityCallback() { 2059 @Override 2060 public void onCapabilitiesStatusChanged( 2061 MmTelFeature.MmTelCapabilities capabilities) { 2062 listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), capabilities); 2063 } 2064 }, getImsThreadExecutor()); 2065 log("Registration Callback registered."); 2066 } 2067 2068 /** 2069 * Adds a callback that gets called when IMS registration has changed for the slot ID 2070 * associated with this ImsManager. 2071 * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the 2072 * caller when IMS registration status has changed. 2073 * @param executor The Executor that the callback should be called on. 2074 * @throws ImsException when the ImsService connection is not available. 2075 */ addRegistrationCallback(RegistrationManager.RegistrationCallback callback, Executor executor)2076 public void addRegistrationCallback(RegistrationManager.RegistrationCallback callback, 2077 Executor executor) 2078 throws ImsException { 2079 if (callback == null) { 2080 throw new NullPointerException("registration callback can't be null"); 2081 } 2082 2083 try { 2084 callback.setExecutor(executor); 2085 mMmTelConnectionRef.get().addRegistrationCallback(callback.getBinder()); 2086 log("Registration Callback registered."); 2087 // Only record if there isn't a RemoteException. 2088 } catch (IllegalStateException e) { 2089 throw new ImsException("addRegistrationCallback(IRIB)", e, 2090 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2091 } 2092 } 2093 2094 /** 2095 * Removes a previously added registration callback that was added via 2096 * {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback, Executor)} . 2097 * @param callback A {@link RegistrationManager.RegistrationCallback} that was previously added. 2098 */ removeRegistrationListener(RegistrationManager.RegistrationCallback callback)2099 public void removeRegistrationListener(RegistrationManager.RegistrationCallback callback) { 2100 if (callback == null) { 2101 throw new NullPointerException("registration callback can't be null"); 2102 } 2103 mMmTelConnectionRef.get().removeRegistrationCallback(callback.getBinder()); 2104 log("Registration callback removed."); 2105 } 2106 2107 /** 2108 * Adds a callback that gets called when IMS registration has changed for a specific 2109 * subscription. 2110 * 2111 * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the 2112 * caller when IMS registration status has changed. 2113 * @param subId The subscription ID to register this registration callback for. 2114 * @throws RemoteException when the ImsService connection is not available. 2115 */ addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)2116 public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId) 2117 throws RemoteException { 2118 if (callback == null) { 2119 throw new IllegalArgumentException("registration callback can't be null"); 2120 } 2121 mMmTelConnectionRef.get().addRegistrationCallbackForSubscription(callback, subId); 2122 log("Registration Callback registered."); 2123 // Only record if there isn't a RemoteException. 2124 } 2125 2126 /** 2127 * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback 2128 * that is associated with a specific subscription. 2129 */ removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)2130 public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, 2131 int subId) { 2132 if (callback == null) { 2133 throw new IllegalArgumentException("registration callback can't be null"); 2134 } 2135 mMmTelConnectionRef.get().removeRegistrationCallbackForSubscription(callback, subId); 2136 } 2137 2138 /** 2139 * Adds a callback that gets called when MMTel capability status has changed, for example when 2140 * Voice over IMS or VT over IMS is not available currently. 2141 * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller when 2142 * MMTel capability status has changed. 2143 * @param executor The Executor that the callback should be called on. 2144 * @throws ImsException when the ImsService connection is not available. 2145 */ addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback, Executor executor)2146 public void addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback, 2147 Executor executor) throws ImsException { 2148 if (callback == null) { 2149 throw new NullPointerException("capabilities callback can't be null"); 2150 } 2151 2152 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2153 try { 2154 callback.setExecutor(executor); 2155 c.addCapabilityCallback(callback.getBinder()); 2156 log("Capability Callback registered."); 2157 // Only record if there isn't a RemoteException. 2158 } catch (IllegalStateException e) { 2159 throw new ImsException("addCapabilitiesCallback(IF)", e, 2160 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2161 } 2162 } 2163 2164 /** 2165 * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback. 2166 */ removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback)2167 public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback) { 2168 if (callback == null) { 2169 throw new NullPointerException("capabilities callback can't be null"); 2170 } 2171 2172 try { 2173 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2174 c.removeCapabilityCallback(callback.getBinder()); 2175 } catch (ImsException e) { 2176 log("Exception removing Capability , exception=" + e); 2177 } 2178 } 2179 2180 /** 2181 * Adds a callback that gets called when IMS capabilities have changed for a specified 2182 * subscription. 2183 * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller 2184 * when the IMS Capabilities have changed. 2185 * @param subId The subscription that is associated with the callback. 2186 * @throws RemoteException when the ImsService connection is not available. 2187 */ addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)2188 public void addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId) 2189 throws RemoteException { 2190 if (callback == null) { 2191 throw new IllegalArgumentException("registration callback can't be null"); 2192 } 2193 mMmTelConnectionRef.get().addCapabilityCallbackForSubscription(callback, subId); 2194 log("Capability Callback registered for subscription."); 2195 } 2196 2197 /** 2198 * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} that was 2199 * associated with a specific subscription. 2200 */ removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId)2201 public void removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, 2202 int subId) { 2203 if (callback == null) { 2204 throw new IllegalArgumentException("capabilities callback can't be null"); 2205 } 2206 mMmTelConnectionRef.get().removeCapabilityCallbackForSubscription(callback, subId); 2207 } 2208 2209 /** 2210 * Removes the registration listener from the IMS service. 2211 * 2212 * @param listener Previously registered listener that will be removed. Can not be null. 2213 * @throws NullPointerException if {@code listener} is null 2214 * @throws ImsException if calling the IMS service results in an error 2215 * instead. 2216 */ removeRegistrationListener(ImsConnectionStateListener listener)2217 public void removeRegistrationListener(ImsConnectionStateListener listener) 2218 throws ImsException { 2219 if (listener == null) { 2220 throw new NullPointerException("listener can't be null"); 2221 } 2222 2223 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2224 c.removeRegistrationCallback(listener.getBinder()); 2225 log("Registration Callback/Listener registered."); 2226 // Only record if there isn't a RemoteException. 2227 } 2228 2229 /** 2230 * Adds a callback that gets called when Provisioning has changed for a specified subscription. 2231 * @param callback A {@link ProvisioningManager.Callback} that will notify the caller when 2232 * provisioning has changed. 2233 * @param subId The subscription that is associated with the callback. 2234 * @throws IllegalStateException when the {@link ImsService} connection is not available. 2235 * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null. 2236 */ addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)2237 public void addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) { 2238 if (callback == null) { 2239 throw new IllegalArgumentException("provisioning callback can't be null"); 2240 } 2241 2242 mMmTelConnectionRef.get().addProvisioningCallbackForSubscription(callback, subId); 2243 log("Capability Callback registered for subscription."); 2244 } 2245 2246 /** 2247 * Removes a previously registered {@link ProvisioningManager.Callback} that was associated with 2248 * a specific subscription. 2249 * @throws IllegalStateException when the {@link ImsService} connection is not available. 2250 * @throws IllegalArgumentException when the {@link IImsConfigCallback} argument is null. 2251 */ removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)2252 public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId) { 2253 if (callback == null) { 2254 throw new IllegalArgumentException("provisioning callback can't be null"); 2255 } 2256 2257 mMmTelConnectionRef.get().removeProvisioningCallbackForSubscription(callback, subId); 2258 } 2259 getRegistrationTech()2260 public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() { 2261 try { 2262 return mMmTelConnectionRef.get().getRegistrationTech(); 2263 } catch (RemoteException e) { 2264 logw("getRegistrationTech: no connection to ImsService."); 2265 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; 2266 } 2267 } 2268 getRegistrationTech(Consumer<Integer> callback)2269 public void getRegistrationTech(Consumer<Integer> callback) { 2270 getImsThreadExecutor().execute(() -> { 2271 try { 2272 int tech = mMmTelConnectionRef.get().getRegistrationTech(); 2273 callback.accept(tech); 2274 } catch (RemoteException e) { 2275 logw("getRegistrationTech(C): no connection to ImsService."); 2276 callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE); 2277 } 2278 }); 2279 } 2280 2281 /** 2282 * Closes the connection opened in {@link #open} and removes the associated listeners. 2283 */ close()2284 public void close() { 2285 mMmTelConnectionRef.get().closeConnection(); 2286 } 2287 2288 /** 2289 * Create or get the existing configuration interface to provision / withdraw the supplementary 2290 * service settings. 2291 * <p> 2292 * There can only be one connection to the UT interface, so this may only be called by one 2293 * ImsManager instance. Otherwise, an IllegalStateException will be thrown. 2294 * 2295 * @return the Ut interface instance 2296 * @throws ImsException if getting the Ut interface results in an error 2297 */ createOrGetSupplementaryServiceConfiguration()2298 public ImsUtInterface createOrGetSupplementaryServiceConfiguration() throws ImsException { 2299 ImsUt iUt; 2300 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2301 try { 2302 iUt = c.createOrGetUtInterface(); 2303 if (iUt == null) { 2304 throw new ImsException("getSupplementaryServiceConfiguration()", 2305 ImsReasonInfo.CODE_UT_NOT_SUPPORTED); 2306 } 2307 } catch (RemoteException e) { 2308 throw new ImsException("getSupplementaryServiceConfiguration()", e, 2309 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2310 } 2311 return iUt; 2312 } 2313 2314 /** 2315 * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. 2316 * 2317 * @param serviceType a service type that is specified in {@link ImsCallProfile} 2318 * {@link ImsCallProfile#SERVICE_TYPE_NONE} 2319 * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} 2320 * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} 2321 * @param callType a call type that is specified in {@link ImsCallProfile} 2322 * {@link ImsCallProfile#CALL_TYPE_VOICE} 2323 * {@link ImsCallProfile#CALL_TYPE_VT} 2324 * {@link ImsCallProfile#CALL_TYPE_VT_TX} 2325 * {@link ImsCallProfile#CALL_TYPE_VT_RX} 2326 * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} 2327 * {@link ImsCallProfile#CALL_TYPE_VS} 2328 * {@link ImsCallProfile#CALL_TYPE_VS_TX} 2329 * {@link ImsCallProfile#CALL_TYPE_VS_RX} 2330 * @return a {@link ImsCallProfile} object 2331 * @throws ImsException if calling the IMS service results in an error 2332 */ createCallProfile(int serviceType, int callType)2333 public ImsCallProfile createCallProfile(int serviceType, int callType) throws ImsException { 2334 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2335 2336 try { 2337 return c.createCallProfile(serviceType, callType); 2338 } catch (RemoteException e) { 2339 throw new ImsException("createCallProfile()", e, 2340 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2341 } 2342 } 2343 2344 /** 2345 * Informs the {@link ImsService} of the {@link RtpHeaderExtensionType}s which the framework 2346 * intends to use for incoming and outgoing calls. 2347 * <p> 2348 * See {@link RtpHeaderExtensionType} for more information. 2349 * @param types The RTP header extension types to use for incoming and outgoing calls, or 2350 * empty list if none defined. 2351 * @throws ImsException 2352 */ setOfferedRtpHeaderExtensionTypes(@onNull Set<RtpHeaderExtensionType> types)2353 public void setOfferedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType> types) 2354 throws ImsException { 2355 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2356 2357 try { 2358 c.changeOfferedRtpHeaderExtensionTypes(types); 2359 } catch (RemoteException e) { 2360 throw new ImsException("setOfferedRtpHeaderExtensionTypes()", e, 2361 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2362 } 2363 } 2364 2365 /** 2366 * Creates a {@link ImsCall} to make a call. 2367 * 2368 * @param profile a call profile to make the call 2369 * (it contains service type, call type, media information, etc.) 2370 * @param callees participants to invite the conference call 2371 * @param listener listen to the call events from {@link ImsCall} 2372 * @return a {@link ImsCall} object 2373 * @throws ImsException if calling the IMS service results in an error 2374 */ makeCall(ImsCallProfile profile, String[] callees, ImsCall.Listener listener)2375 public ImsCall makeCall(ImsCallProfile profile, String[] callees, 2376 ImsCall.Listener listener) throws ImsException { 2377 if (DBG) { 2378 log("makeCall :: profile=" + profile); 2379 } 2380 2381 // Check we are still alive 2382 getOrThrowExceptionIfServiceUnavailable(); 2383 2384 ImsCall call = new ImsCall(mContext, profile); 2385 2386 call.setListener(listener); 2387 ImsCallSession session = createCallSession(profile); 2388 2389 if ((callees != null) && (callees.length == 1) && !(session.isMultiparty())) { 2390 call.start(session, callees[0]); 2391 } else { 2392 call.start(session, callees); 2393 } 2394 2395 return call; 2396 } 2397 2398 /** 2399 * Creates a {@link ImsCall} to take an incoming call. 2400 * 2401 * @param listener to listen to the call events from {@link ImsCall} 2402 * @return a {@link ImsCall} object 2403 * @throws ImsException if calling the IMS service results in an error 2404 */ takeCall(IImsCallSession session, ImsCall.Listener listener)2405 public ImsCall takeCall(IImsCallSession session, ImsCall.Listener listener) 2406 throws ImsException { 2407 // Check we are still alive 2408 getOrThrowExceptionIfServiceUnavailable(); 2409 try { 2410 if (session == null) { 2411 throw new ImsException("No pending session for the call", 2412 ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL); 2413 } 2414 2415 ImsCall call = new ImsCall(mContext, session.getCallProfile()); 2416 2417 call.attachSession(new ImsCallSession(session)); 2418 call.setListener(listener); 2419 2420 return call; 2421 } catch (Throwable t) { 2422 loge("takeCall caught: ", t); 2423 throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED); 2424 } 2425 } 2426 2427 /** 2428 * Gets the config interface to get/set service/capability parameters. 2429 * 2430 * @return the ImsConfig instance. 2431 * @throws ImsException if getting the setting interface results in an error. 2432 */ 2433 @UnsupportedAppUsage getConfigInterface()2434 public ImsConfig getConfigInterface() throws ImsException { 2435 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2436 2437 IImsConfig config = c.getConfig(); 2438 if (config == null) { 2439 throw new ImsException("getConfigInterface()", 2440 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 2441 } 2442 return new ImsConfig(config); 2443 } 2444 2445 /** 2446 * Enable or disable a capability for multiple radio technologies. 2447 */ changeMmTelCapability(boolean isEnabled, int capability, int... radioTechs)2448 public void changeMmTelCapability(boolean isEnabled, int capability, 2449 int... radioTechs) throws ImsException { 2450 CapabilityChangeRequest request = new CapabilityChangeRequest(); 2451 if (isEnabled) { 2452 for (int tech : radioTechs) { 2453 request.addCapabilitiesToEnableForTech(capability, tech); 2454 } 2455 } else { 2456 for (int tech : radioTechs) { 2457 request.addCapabilitiesToDisableForTech(capability, tech); 2458 } 2459 } 2460 changeMmTelCapability(request); 2461 } 2462 changeMmTelCapability(CapabilityChangeRequest r)2463 private void changeMmTelCapability(CapabilityChangeRequest r) throws ImsException { 2464 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2465 try { 2466 logi("changeMmTelCapability: changing capabilities for sub: " + getSubId() 2467 + ", request: " + r); 2468 c.changeEnabledCapabilities(r, null); 2469 ImsStatsCallback cb = getStatsCallback(mPhoneId); 2470 if (cb == null) { 2471 return; 2472 } 2473 for (CapabilityChangeRequest.CapabilityPair enabledCaps : r.getCapabilitiesToEnable()) { 2474 cb.onEnabledMmTelCapabilitiesChanged(enabledCaps.getCapability(), 2475 enabledCaps.getRadioTech(), true); 2476 } 2477 for (CapabilityChangeRequest.CapabilityPair disabledCaps : 2478 r.getCapabilitiesToDisable()) { 2479 cb.onEnabledMmTelCapabilitiesChanged(disabledCaps.getCapability(), 2480 disabledCaps.getRadioTech(), false); 2481 } 2482 } catch (RemoteException e) { 2483 throw new ImsException("changeMmTelCapability(CCR)", e, 2484 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2485 } 2486 } 2487 updateRttConfigValue()2488 private boolean updateRttConfigValue() { 2489 // If there's no active sub anywhere on the device, enable RTT on the modem so that 2490 // the device can make an emergency call. 2491 2492 boolean isActiveSubscriptionPresent = isActiveSubscriptionPresent(); 2493 boolean isCarrierSupported = 2494 getBooleanCarrierConfig(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL) 2495 || !isActiveSubscriptionPresent; 2496 2497 int defaultRttMode = 2498 getIntCarrierConfig(CarrierConfigManager.KEY_DEFAULT_RTT_MODE_INT); 2499 int rttMode = mSettingsProxy.getSecureIntSetting(mContext.getContentResolver(), 2500 Settings.Secure.RTT_CALLING_MODE, defaultRttMode); 2501 logi("defaultRttMode = " + defaultRttMode + " rttMode = " + rttMode); 2502 boolean isRttAlwaysOnCarrierConfig = getBooleanCarrierConfig( 2503 CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL); 2504 if (isRttAlwaysOnCarrierConfig && rttMode == defaultRttMode) { 2505 mSettingsProxy.putSecureIntSetting(mContext.getContentResolver(), 2506 Settings.Secure.RTT_CALLING_MODE, defaultRttMode); 2507 } 2508 2509 boolean isRttUiSettingEnabled = mSettingsProxy.getSecureIntSetting( 2510 mContext.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0; 2511 2512 boolean shouldImsRttBeOn = isRttUiSettingEnabled || isRttAlwaysOnCarrierConfig; 2513 logi("update RTT: settings value: " + isRttUiSettingEnabled + " always-on carrierconfig: " 2514 + isRttAlwaysOnCarrierConfig 2515 + "isActiveSubscriptionPresent: " + isActiveSubscriptionPresent); 2516 2517 if (isCarrierSupported) { 2518 setRttConfig(shouldImsRttBeOn); 2519 } else { 2520 setRttConfig(false); 2521 } 2522 return isCarrierSupported && shouldImsRttBeOn; 2523 } 2524 setRttConfig(boolean enabled)2525 private void setRttConfig(boolean enabled) { 2526 final int value = enabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 2527 ProvisioningManager.PROVISIONING_VALUE_DISABLED; 2528 getImsThreadExecutor().execute(() -> { 2529 try { 2530 logi("Setting RTT enabled to " + enabled); 2531 getConfigInterface().setProvisionedValue( 2532 ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value); 2533 } catch (ImsException e) { 2534 loge("Unable to set RTT value enabled to " + enabled + ": " + e); 2535 } 2536 }); 2537 } 2538 queryMmTelCapability( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2539 public boolean queryMmTelCapability( 2540 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 2541 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { 2542 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2543 2544 BlockingQueue<Boolean> result = new LinkedBlockingDeque<>(1); 2545 2546 try { 2547 c.queryEnabledCapabilities(capability, radioTech, new IImsCapabilityCallback.Stub() { 2548 @Override 2549 public void onQueryCapabilityConfiguration(int resCap, int resTech, 2550 boolean enabled) { 2551 if (resCap == capability && resTech == radioTech) { 2552 result.offer(enabled); 2553 } 2554 } 2555 2556 @Override 2557 public void onChangeCapabilityConfigurationError(int capability, 2558 int radioTech, int reason) { 2559 2560 } 2561 2562 @Override 2563 public void onCapabilitiesStatusChanged(int config) { 2564 2565 } 2566 }); 2567 } catch (RemoteException e) { 2568 throw new ImsException("queryMmTelCapability()", e, 2569 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2570 } 2571 2572 try { 2573 return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS); 2574 } catch (InterruptedException e) { 2575 logw("queryMmTelCapability: interrupted while waiting for response"); 2576 } 2577 return false; 2578 } 2579 queryMmTelCapabilityStatus( @mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)2580 public boolean queryMmTelCapabilityStatus( 2581 @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 2582 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { 2583 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2584 2585 if (getRegistrationTech() != radioTech) 2586 return false; 2587 2588 try { 2589 2590 MmTelFeature.MmTelCapabilities capabilities = 2591 c.queryCapabilityStatus(); 2592 2593 return capabilities.isCapable(capability); 2594 } catch (RemoteException e) { 2595 throw new ImsException("queryMmTelCapabilityStatus()", e, 2596 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2597 } 2598 } 2599 2600 /** 2601 * Enable the RTT configuration on this device. 2602 */ setRttEnabled(boolean enabled)2603 public void setRttEnabled(boolean enabled) { 2604 if (enabled) { 2605 // Override this setting if RTT is enabled. 2606 setEnhanced4gLteModeSetting(true /*enabled*/); 2607 } 2608 setRttConfig(enabled); 2609 } 2610 2611 /** 2612 * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status) 2613 */ setTtyMode(int ttyMode)2614 public void setTtyMode(int ttyMode) throws ImsException { 2615 boolean isNonTtyOrTtyOnVolteEnabled = isTtyOnVoLteCapable() || 2616 (ttyMode == TelecomManager.TTY_MODE_OFF); 2617 logi("setTtyMode: isNonTtyOrTtyOnVolteEnabled=" + isNonTtyOrTtyOnVolteEnabled); 2618 CapabilityChangeRequest request = new CapabilityChangeRequest(); 2619 updateVoiceCellFeatureValue(request, isNonTtyOrTtyOnVolteEnabled); 2620 updateVideoCallFeatureValue(request, isNonTtyOrTtyOnVolteEnabled); 2621 // update MMTEL caps for the new configuration. 2622 changeMmTelCapability(request); 2623 if (isImsNeeded(request)) { 2624 // Only turn on IMS if voice/video is enabled now in the new configuration. 2625 turnOnIms(); 2626 } 2627 } 2628 2629 /** 2630 * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call 2631 * settings screen. 2632 * @param uiTtyMode TTY Mode, valid options are: 2633 * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} 2634 * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} 2635 * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} 2636 * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} 2637 * @param onComplete A Message that will be called by the ImsService when it has completed this 2638 * operation or null if not waiting for an async response. The Message must contain a 2639 * valid {@link Message#replyTo} {@link android.os.Messenger}, since it will be passed 2640 * through Binder to another process. 2641 */ setUiTTYMode(Context context, int uiTtyMode, Message onComplete)2642 public void setUiTTYMode(Context context, int uiTtyMode, Message onComplete) 2643 throws ImsException { 2644 2645 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2646 try { 2647 c.setUiTTYMode(uiTtyMode, onComplete); 2648 } catch (RemoteException e) { 2649 throw new ImsException("setTTYMode()", e, 2650 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2651 } 2652 } 2653 getImsServiceState()2654 public int getImsServiceState() throws ImsException { 2655 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2656 return c.getFeatureState(); 2657 } 2658 2659 @Override updateFeatureState(int state)2660 public void updateFeatureState(int state) { 2661 mMmTelConnectionRef.get().updateFeatureState(state); 2662 } 2663 2664 @Override updateFeatureCapabilities(long capabilities)2665 public void updateFeatureCapabilities(long capabilities) { 2666 mMmTelConnectionRef.get().updateFeatureCapabilities(capabilities); 2667 } 2668 getImsServiceState(Consumer<Integer> result)2669 public void getImsServiceState(Consumer<Integer> result) { 2670 getImsThreadExecutor().execute(() -> { 2671 try { 2672 result.accept(getImsServiceState()); 2673 } catch (ImsException e) { 2674 // In the case that the ImsService is not available, report unavailable. 2675 result.accept(ImsFeature.STATE_UNAVAILABLE); 2676 } 2677 }); 2678 } 2679 2680 /** 2681 * @return An Executor that should be used to execute potentially long-running operations. 2682 */ getImsThreadExecutor()2683 private Executor getImsThreadExecutor() { 2684 return mExecutor; 2685 } 2686 2687 /** 2688 * Get the boolean config from carrier config manager. 2689 * 2690 * @param key config key defined in CarrierConfigManager 2691 * @return boolean value of corresponding key. 2692 */ getBooleanCarrierConfig(String key)2693 private boolean getBooleanCarrierConfig(String key) { 2694 PersistableBundle b = null; 2695 if (mConfigManager != null) { 2696 // If an invalid subId is used, this bundle will contain default values. 2697 b = mConfigManager.getConfigForSubId(getSubId()); 2698 } 2699 if (b != null) { 2700 return b.getBoolean(key); 2701 } else { 2702 // Return static default defined in CarrierConfigManager. 2703 return CarrierConfigManager.getDefaultConfig().getBoolean(key); 2704 } 2705 } 2706 2707 /** 2708 * Get the int config from carrier config manager. 2709 * 2710 * @param key config key defined in CarrierConfigManager 2711 * @return integer value of corresponding key. 2712 */ getIntCarrierConfig(String key)2713 private int getIntCarrierConfig(String key) { 2714 PersistableBundle b = null; 2715 if (mConfigManager != null) { 2716 // If an invalid subId is used, this bundle will contain default values. 2717 b = mConfigManager.getConfigForSubId(getSubId()); 2718 } 2719 if (b != null) { 2720 return b.getInt(key); 2721 } else { 2722 // Return static default defined in CarrierConfigManager. 2723 return CarrierConfigManager.getDefaultConfig().getInt(key); 2724 } 2725 } 2726 2727 /** 2728 * Get the int[] config from carrier config manager. 2729 * 2730 * @param key config key defined in CarrierConfigManager 2731 * @return int[] values of the corresponding key. 2732 */ getIntArrayCarrierConfig(String key)2733 private int[] getIntArrayCarrierConfig(String key) { 2734 PersistableBundle b = null; 2735 if (mConfigManager != null) { 2736 // If an invalid subId is used, this bundle will contain default values. 2737 b = mConfigManager.getConfigForSubId(getSubId()); 2738 } 2739 if (b != null) { 2740 return b.getIntArray(key); 2741 } else { 2742 // Return static default defined in CarrierConfigManager. 2743 return CarrierConfigManager.getDefaultConfig().getIntArray(key); 2744 } 2745 } 2746 2747 /** 2748 * Checks to see if the ImsService Binder is connected. If it is not, we try to create the 2749 * connection again. 2750 */ getOrThrowExceptionIfServiceUnavailable()2751 private MmTelFeatureConnection getOrThrowExceptionIfServiceUnavailable() 2752 throws ImsException { 2753 if (!isImsSupportedOnDevice(mContext)) { 2754 throw new ImsException("IMS not supported on device.", 2755 ImsReasonInfo.CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE); 2756 } 2757 MmTelFeatureConnection c = mMmTelConnectionRef.get(); 2758 if (c == null || !c.isBinderAlive()) { 2759 throw new ImsException("Service is unavailable", 2760 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2761 } 2762 return c; 2763 } 2764 2765 @Override registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb)2766 public void registerFeatureCallback(int slotId, IImsServiceFeatureCallback cb) { 2767 try { 2768 ITelephony telephony = mBinderCache.listenOnBinder(cb, () -> { 2769 try { 2770 cb.imsFeatureRemoved( 2771 FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 2772 } catch (RemoteException ignore) {} // This is local. 2773 }); 2774 2775 if (telephony != null) { 2776 telephony.registerMmTelFeatureCallback(slotId, cb); 2777 } else { 2778 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 2779 } 2780 } catch (ServiceSpecificException e) { 2781 try { 2782 switch (e.errorCode) { 2783 case android.telephony.ims.ImsException.CODE_ERROR_UNSUPPORTED_OPERATION: 2784 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_IMS_UNSUPPORTED); 2785 break; 2786 default: { 2787 cb.imsFeatureRemoved( 2788 FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 2789 } 2790 } 2791 } catch (RemoteException ignore) {} // Already dead anyway if this happens. 2792 } catch (RemoteException e) { 2793 try { 2794 cb.imsFeatureRemoved(FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE); 2795 } catch (RemoteException ignore) {} // Already dead if this happens. 2796 } 2797 } 2798 2799 @Override unregisterFeatureCallback(IImsServiceFeatureCallback cb)2800 public void unregisterFeatureCallback(IImsServiceFeatureCallback cb) { 2801 try { 2802 ITelephony telephony = mBinderCache.removeRunnable(cb); 2803 if (telephony != null) { 2804 telephony.unregisterImsFeatureCallback(cb); 2805 } 2806 } catch (RemoteException e) { 2807 // This means that telephony died, so do not worry about it. 2808 loge("unregisterImsFeatureCallback (MMTEL), RemoteException: " + e.getMessage()); 2809 } 2810 } 2811 2812 @Override associate(ImsFeatureContainer c)2813 public void associate(ImsFeatureContainer c) { 2814 if (c == null) { 2815 mMmTelConnectionRef.set(mMmTelFeatureConnectionFactory.create( 2816 mContext, mPhoneId, null, null, null, null)); 2817 } else { 2818 mMmTelConnectionRef.set(mMmTelFeatureConnectionFactory.create( 2819 mContext, mPhoneId, IImsMmTelFeature.Stub.asInterface(c.imsFeature), 2820 c.imsConfig, c.imsRegistration, c.sipTransport)); 2821 } 2822 } 2823 2824 @Override invalidate()2825 public void invalidate() { 2826 mMmTelConnectionRef.get().onRemovedOrDied(); 2827 } 2828 getITelephony()2829 private ITelephony getITelephony() { 2830 return mBinderCache.getBinder(); 2831 } 2832 getITelephonyInterface()2833 private static ITelephony getITelephonyInterface() { 2834 return ITelephony.Stub.asInterface( 2835 TelephonyFrameworkInitializer 2836 .getTelephonyServiceManager() 2837 .getTelephonyServiceRegisterer() 2838 .get()); 2839 } 2840 2841 /** 2842 * Creates a {@link ImsCallSession} with the specified call profile. 2843 * Use other methods, if applicable, instead of interacting with 2844 * {@link ImsCallSession} directly. 2845 * 2846 * @param profile a call profile to make the call 2847 */ createCallSession(ImsCallProfile profile)2848 private ImsCallSession createCallSession(ImsCallProfile profile) throws ImsException { 2849 try { 2850 MmTelFeatureConnection c = mMmTelConnectionRef.get(); 2851 // Throws an exception if the ImsService Feature is not ready to accept commands. 2852 return new ImsCallSession(c.createCallSession(profile)); 2853 } catch (RemoteException e) { 2854 logw("CreateCallSession: Error, remote exception: " + e.getMessage()); 2855 throw new ImsException("createCallSession()", e, 2856 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2857 2858 } 2859 } 2860 log(String s)2861 private void log(String s) { 2862 Rlog.d(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s); 2863 } 2864 logi(String s)2865 private void logi(String s) { 2866 Rlog.i(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s); 2867 } 2868 logw(String s)2869 private void logw(String s) { 2870 Rlog.w(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s); 2871 } 2872 loge(String s)2873 private void loge(String s) { 2874 Rlog.e(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s); 2875 } 2876 loge(String s, Throwable t)2877 private void loge(String s, Throwable t) { 2878 Rlog.e(TAG + mLogTagPostfix + " [" + mPhoneId + "]", s, t); 2879 } 2880 2881 /** 2882 * Used for turning on IMS.if its off already 2883 */ turnOnIms()2884 private void turnOnIms() throws ImsException { 2885 TelephonyManager tm = (TelephonyManager) 2886 mContext.getSystemService(Context.TELEPHONY_SERVICE); 2887 tm.enableIms(mPhoneId); 2888 } 2889 isImsTurnOffAllowed()2890 private boolean isImsTurnOffAllowed() { 2891 return isTurnOffImsAllowedByPlatform() 2892 && (!isWfcEnabledByPlatform() 2893 || !isWfcEnabledByUser()); 2894 } 2895 2896 /** 2897 * Used for turning off IMS completely in order to make the device CSFB'ed. 2898 * Once turned off, all calls will be over CS. 2899 */ turnOffIms()2900 private void turnOffIms() throws ImsException { 2901 TelephonyManager tm = (TelephonyManager) 2902 mContext.getSystemService(Context.TELEPHONY_SERVICE); 2903 tm.disableIms(mPhoneId); 2904 } 2905 2906 /** 2907 * Gets the ECBM interface to request ECBM exit. 2908 * <p> 2909 * This should only be called after {@link #open} has been called. 2910 * 2911 * @return the ECBM interface instance 2912 * @throws ImsException if getting the ECBM interface results in an error 2913 */ getEcbmInterface()2914 public ImsEcbm getEcbmInterface() throws ImsException { 2915 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 2916 ImsEcbm iEcbm = c.getEcbmInterface(); 2917 2918 if (iEcbm == null) { 2919 throw new ImsException("getEcbmInterface()", 2920 ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED); 2921 } 2922 return iEcbm; 2923 } 2924 sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)2925 public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, 2926 byte[] pdu) throws ImsException { 2927 try { 2928 mMmTelConnectionRef.get().sendSms(token, messageRef, format, smsc, isRetry, pdu); 2929 } catch (RemoteException e) { 2930 throw new ImsException("sendSms()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2931 } 2932 } 2933 acknowledgeSms(int token, int messageRef, int result)2934 public void acknowledgeSms(int token, int messageRef, int result) throws ImsException { 2935 try { 2936 mMmTelConnectionRef.get().acknowledgeSms(token, messageRef, result); 2937 } catch (RemoteException e) { 2938 throw new ImsException("acknowledgeSms()", e, 2939 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2940 } 2941 } 2942 acknowledgeSmsReport(int token, int messageRef, int result)2943 public void acknowledgeSmsReport(int token, int messageRef, int result) throws ImsException{ 2944 try { 2945 mMmTelConnectionRef.get().acknowledgeSmsReport(token, messageRef, result); 2946 } catch (RemoteException e) { 2947 throw new ImsException("acknowledgeSmsReport()", e, 2948 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2949 } 2950 } 2951 getSmsFormat()2952 public String getSmsFormat() throws ImsException{ 2953 try { 2954 return mMmTelConnectionRef.get().getSmsFormat(); 2955 } catch (RemoteException e) { 2956 throw new ImsException("getSmsFormat()", e, 2957 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2958 } 2959 } 2960 setSmsListener(IImsSmsListener listener)2961 public void setSmsListener(IImsSmsListener listener) throws ImsException { 2962 try { 2963 mMmTelConnectionRef.get().setSmsListener(listener); 2964 } catch (RemoteException e) { 2965 throw new ImsException("setSmsListener()", e, 2966 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2967 } 2968 } 2969 onSmsReady()2970 public void onSmsReady() throws ImsException { 2971 try { 2972 mMmTelConnectionRef.get().onSmsReady(); 2973 } catch (RemoteException e) { 2974 throw new ImsException("onSmsReady()", e, 2975 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2976 } 2977 } 2978 2979 /** 2980 * Determines whether or not a call with the specified numbers should be placed over IMS or over 2981 * CSFB. 2982 * @param isEmergency is at least one call an emergency number. 2983 * @param numbers A {@link String} array containing the numbers in the call being placed. Can 2984 * be multiple numbers in the case of dialing out a conference. 2985 * @return The result of the query, one of the following values: 2986 * - {@link MmTelFeature#PROCESS_CALL_IMS} 2987 * - {@link MmTelFeature#PROCESS_CALL_CSFB} 2988 * @throws ImsException if the ImsService is not available. In this case, we should fall back 2989 * to CSFB anyway. 2990 */ shouldProcessCall(boolean isEmergency, String[] numbers)2991 public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency, 2992 String[] numbers) throws ImsException { 2993 try { 2994 return mMmTelConnectionRef.get().shouldProcessCall(isEmergency, numbers); 2995 } catch (RemoteException e) { 2996 throw new ImsException("shouldProcessCall()", e, 2997 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 2998 } 2999 } 3000 3001 /** 3002 * Resets ImsManager settings back to factory defaults. 3003 * 3004 * @deprecated Doesn't support MSIM devices. Use {@link #factoryReset()} instead. 3005 * 3006 * @hide 3007 */ factoryReset(Context context)3008 public static void factoryReset(Context context) { 3009 DefaultSubscriptionManagerProxy p = new DefaultSubscriptionManagerProxy(context); 3010 ImsManager mgr = ImsManager.getInstance(context, p.getDefaultVoicePhoneId()); 3011 if (mgr != null) { 3012 mgr.factoryReset(); 3013 } 3014 Rlog.e(TAG, "factoryReset: ImsManager null."); 3015 } 3016 3017 /** 3018 * Resets ImsManager settings back to factory defaults. 3019 * 3020 * @hide 3021 */ factoryReset()3022 public void factoryReset() { 3023 int subId = getSubId(); 3024 if (!isSubIdValid(subId)) { 3025 loge("factoryReset: invalid sub id, can not reset siminfo db settings; subId=" 3026 + subId); 3027 return; 3028 } 3029 // Set VoLTE to default 3030 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3031 SubscriptionManager.ENHANCED_4G_MODE_ENABLED, 3032 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3033 3034 // Set VoWiFi to default 3035 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3036 SubscriptionManager.WFC_IMS_ENABLED, 3037 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3038 3039 // Set VoWiFi mode to default 3040 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3041 SubscriptionManager.WFC_IMS_MODE, 3042 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3043 3044 // Set VoWiFi roaming to default 3045 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3046 SubscriptionManager.WFC_IMS_ROAMING_ENABLED, 3047 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3048 3049 // Set VoWiFi roaming mode to default 3050 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3051 SubscriptionManager.WFC_IMS_ROAMING_MODE, 3052 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3053 3054 3055 // Set VT to default 3056 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3057 SubscriptionManager.VT_IMS_ENABLED, 3058 Integer.toString(SUB_PROPERTY_NOT_INITIALIZED)); 3059 3060 // Set RCS UCE to default 3061 mSubscriptionManagerProxy.setSubscriptionProperty(subId, 3062 SubscriptionManager.IMS_RCS_UCE_ENABLED, Integer.toString( 3063 SUBINFO_PROPERTY_FALSE)); 3064 // Push settings 3065 try { 3066 reevaluateCapabilities(); 3067 } catch (ImsException e) { 3068 loge("factoryReset, exception: " + e); 3069 } 3070 } 3071 setVolteProvisioned(boolean isProvisioned)3072 public void setVolteProvisioned(boolean isProvisioned) { 3073 int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 3074 ProvisioningManager.PROVISIONING_VALUE_DISABLED; 3075 setProvisionedBoolNoException(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED, 3076 provisionStatus); 3077 } 3078 setWfcProvisioned(boolean isProvisioned)3079 public void setWfcProvisioned(boolean isProvisioned) { 3080 int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 3081 ProvisioningManager.PROVISIONING_VALUE_DISABLED; 3082 setProvisionedBoolNoException( 3083 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED, provisionStatus); 3084 } 3085 setVtProvisioned(boolean isProvisioned)3086 public void setVtProvisioned(boolean isProvisioned) { 3087 int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 3088 ProvisioningManager.PROVISIONING_VALUE_DISABLED; 3089 setProvisionedBoolNoException(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED, 3090 provisionStatus); 3091 } 3092 setEabProvisioned(boolean isProvisioned)3093 public void setEabProvisioned(boolean isProvisioned) { 3094 int provisionStatus = isProvisioned ? ProvisioningManager.PROVISIONING_VALUE_ENABLED : 3095 ProvisioningManager.PROVISIONING_VALUE_DISABLED; 3096 setProvisionedBoolNoException(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED, 3097 provisionStatus); 3098 } 3099 isDataEnabled()3100 private boolean isDataEnabled() { 3101 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 3102 if (tm == null) { 3103 loge("isDataEnabled: TelephonyManager not available, returning false..."); 3104 return false; 3105 } 3106 tm = tm.createForSubscriptionId(getSubId()); 3107 return tm.isDataConnectionAllowed(); 3108 } 3109 isVolteProvisioned()3110 private boolean isVolteProvisioned() { 3111 return getProvisionedBoolNoException( 3112 ImsConfig.ConfigConstants.VLT_SETTING_ENABLED); 3113 } 3114 isEabProvisioned()3115 private boolean isEabProvisioned() { 3116 return getProvisionedBoolNoException( 3117 ImsConfig.ConfigConstants.EAB_SETTING_ENABLED); 3118 } 3119 isWfcProvisioned()3120 private boolean isWfcProvisioned() { 3121 return getProvisionedBoolNoException( 3122 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED); 3123 } 3124 isVtProvisioned()3125 private boolean isVtProvisioned() { 3126 return getProvisionedBoolNoException( 3127 ImsConfig.ConfigConstants.LVC_SETTING_ENABLED); 3128 } 3129 booleanToPropertyString(boolean bool)3130 private static String booleanToPropertyString(boolean bool) { 3131 return bool ? "1" : "0"; 3132 } 3133 getConfigInt(int key)3134 public int getConfigInt(int key) throws ImsException { 3135 if (isLocalImsConfigKey(key)) { 3136 return getLocalImsConfigKeyInt(key); 3137 } else { 3138 return getConfigInterface().getConfigInt(key); 3139 } 3140 } 3141 getConfigString(int key)3142 public String getConfigString(int key) throws ImsException { 3143 if (isLocalImsConfigKey(key)) { 3144 return getLocalImsConfigKeyString(key); 3145 } else { 3146 return getConfigInterface().getConfigString(key); 3147 } 3148 } 3149 setConfig(int key, int value)3150 public int setConfig(int key, int value) throws ImsException, RemoteException { 3151 if (isLocalImsConfigKey(key)) { 3152 return setLocalImsConfigKeyInt(key, value); 3153 } else { 3154 return getConfigInterface().setConfig(key, value); 3155 } 3156 } 3157 setConfig(int key, String value)3158 public int setConfig(int key, String value) throws ImsException, RemoteException { 3159 if (isLocalImsConfigKey(key)) { 3160 return setLocalImsConfigKeyString(key, value); 3161 } else { 3162 return getConfigInterface().setConfig(key, value); 3163 } 3164 } 3165 3166 /** 3167 * Gets the configuration value that supported in frameworks. 3168 * 3169 * @param key, as defined in com.android.ims.ProvisioningManager. 3170 * @return the value in Integer format 3171 */ getLocalImsConfigKeyInt(int key)3172 private int getLocalImsConfigKeyInt(int key) { 3173 int result = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN; 3174 3175 switch (key) { 3176 case KEY_VOIMS_OPT_IN_STATUS: 3177 result = isVoImsOptInEnabled() ? 1 : 0; 3178 break; 3179 } 3180 log("getLocalImsConfigKeInt() for key:" + key + ", result: " + result); 3181 return result; 3182 } 3183 3184 /** 3185 * Gets the configuration value that supported in frameworks. 3186 * 3187 * @param key, as defined in com.android.ims.ProvisioningManager. 3188 * @return the value in String format 3189 */ getLocalImsConfigKeyString(int key)3190 private String getLocalImsConfigKeyString(int key) { 3191 String result = ""; 3192 3193 switch (key) { 3194 case KEY_VOIMS_OPT_IN_STATUS: 3195 result = booleanToPropertyString(isVoImsOptInEnabled()); 3196 3197 break; 3198 } 3199 log("getLocalImsConfigKeyString() for key:" + key + ", result: " + result); 3200 return result; 3201 } 3202 3203 /** 3204 * Sets the configuration value that supported in frameworks. 3205 * 3206 * @param key, as defined in com.android.ims.ProvisioningManager. 3207 * @param value in Integer format. 3208 * @return as defined in com.android.ims.ProvisioningManager#OperationStatusConstants 3209 */ setLocalImsConfigKeyInt(int key, int value)3210 private int setLocalImsConfigKeyInt(int key, int value) throws ImsException, RemoteException { 3211 int result = ImsConfig.OperationStatusConstants.UNKNOWN; 3212 3213 switch (key) { 3214 case KEY_VOIMS_OPT_IN_STATUS: 3215 result = setVoImsOptInSetting(value); 3216 reevaluateCapabilities(); 3217 break; 3218 } 3219 log("setLocalImsConfigKeyInt() for" + 3220 " key: " + key + 3221 ", value: " + value + 3222 ", result: " + result); 3223 3224 // Notify ims config changed 3225 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 3226 IImsConfig config = c.getConfig(); 3227 config.notifyIntImsConfigChanged(key, value); 3228 3229 return result; 3230 } 3231 3232 /** 3233 * Sets the configuration value that supported in frameworks. 3234 * 3235 * @param key, as defined in com.android.ims.ProvisioningManager. 3236 * @param value in String format. 3237 * @return as defined in com.android.ims.ProvisioningManager#OperationStatusConstants 3238 */ setLocalImsConfigKeyString(int key, String value)3239 private int setLocalImsConfigKeyString(int key, String value) 3240 throws ImsException, RemoteException { 3241 int result = ImsConfig.OperationStatusConstants.UNKNOWN; 3242 3243 switch (key) { 3244 case KEY_VOIMS_OPT_IN_STATUS: 3245 result = setVoImsOptInSetting(Integer.parseInt(value)); 3246 reevaluateCapabilities(); 3247 break; 3248 } 3249 log("setLocalImsConfigKeyString() for" + 3250 " key: " + key + 3251 ", value: " + value + 3252 ", result: " + result); 3253 3254 // Notify ims config changed 3255 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 3256 IImsConfig config = c.getConfig(); 3257 config.notifyStringImsConfigChanged(key, value); 3258 3259 return result; 3260 } 3261 3262 /** 3263 * Check the config whether supported by framework. 3264 * 3265 * @param key, as defined in com.android.ims.ProvisioningManager. 3266 * @return true if the config is supported by framework. 3267 */ isLocalImsConfigKey(int key)3268 private boolean isLocalImsConfigKey(int key) { 3269 return Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(value -> value == key); 3270 } 3271 isVoImsOptInEnabled()3272 private boolean isVoImsOptInEnabled() { 3273 int setting = mSubscriptionManagerProxy.getIntegerSubscriptionProperty( 3274 getSubId(), SubscriptionManager.VOIMS_OPT_IN_STATUS, 3275 SUB_PROPERTY_NOT_INITIALIZED); 3276 return (setting == ProvisioningManager.PROVISIONING_VALUE_ENABLED); 3277 } 3278 setVoImsOptInSetting(int value)3279 private int setVoImsOptInSetting(int value) { 3280 mSubscriptionManagerProxy.setSubscriptionProperty( 3281 getSubId(), 3282 SubscriptionManager.VOIMS_OPT_IN_STATUS, 3283 String.valueOf(value)); 3284 return ImsConfig.OperationStatusConstants.SUCCESS; 3285 } 3286 dump(FileDescriptor fd, PrintWriter pw, String[] args)3287 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3288 pw.println("ImsManager:"); 3289 pw.println(" device supports IMS = " + isImsSupportedOnDevice(mContext)); 3290 pw.println(" mPhoneId = " + mPhoneId); 3291 pw.println(" mConfigUpdated = " + mConfigUpdated); 3292 pw.println(" mImsServiceProxy = " + mMmTelConnectionRef.get()); 3293 pw.println(" mDataEnabled = " + isDataEnabled()); 3294 pw.println(" ignoreDataEnabledChanged = " + getBooleanCarrierConfig( 3295 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)); 3296 3297 pw.println(" isGbaValid = " + isGbaValid()); 3298 pw.println(" isImsTurnOffAllowed = " + isImsTurnOffAllowed()); 3299 pw.println(" isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled()); 3300 3301 pw.println(" isVolteEnabledByPlatform = " + isVolteEnabledByPlatform()); 3302 pw.println(" isVoImsOptInEnabled = " + isVoImsOptInEnabled()); 3303 pw.println(" isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice()); 3304 pw.println(" isEnhanced4gLteModeSettingEnabledByUser = " + 3305 isEnhanced4gLteModeSettingEnabledByUser()); 3306 pw.println(" isVtEnabledByPlatform = " + isVtEnabledByPlatform()); 3307 pw.println(" isVtEnabledByUser = " + isVtEnabledByUser()); 3308 3309 pw.println(" isWfcEnabledByPlatform = " + isWfcEnabledByPlatform()); 3310 pw.println(" isWfcEnabledByUser = " + isWfcEnabledByUser()); 3311 pw.println(" getWfcMode(non-roaming) = " + getWfcMode(false)); 3312 pw.println(" getWfcMode(roaming) = " + getWfcMode(true)); 3313 pw.println(" isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser()); 3314 3315 pw.println(" isVtProvisionedOnDevice = " + isVtProvisionedOnDevice()); 3316 pw.println(" isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice()); 3317 3318 pw.println(" isCrossSimEnabledByPlatform = " + isCrossSimEnabledByPlatform()); 3319 pw.println(" isCrossSimCallingEnabledByUser = " + isCrossSimCallingEnabledByUser()); 3320 pw.println(" isImsOverNrEnabledByPlatform = " + isImsOverNrEnabledByPlatform()); 3321 pw.flush(); 3322 } 3323 3324 /** 3325 * Determines if a sub id is valid. 3326 * Mimics the logic in SubscriptionController.validateSubId. 3327 * @param subId The sub id to check. 3328 * @return {@code true} if valid, {@code false} otherwise. 3329 */ isSubIdValid(int subId)3330 private boolean isSubIdValid(int subId) { 3331 return mSubscriptionManagerProxy.isValidSubscriptionId(subId) && 3332 subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 3333 } 3334 isActiveSubscriptionPresent()3335 private boolean isActiveSubscriptionPresent() { 3336 return mSubscriptionManagerProxy.getActiveSubscriptionIdList().length > 0; 3337 } 3338 updateImsCarrierConfigs(PersistableBundle configs)3339 private void updateImsCarrierConfigs(PersistableBundle configs) throws ImsException { 3340 MmTelFeatureConnection c = getOrThrowExceptionIfServiceUnavailable(); 3341 3342 IImsConfig config = c.getConfig(); 3343 if (config == null) { 3344 throw new ImsException("getConfigInterface()", 3345 ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); 3346 } 3347 try { 3348 config.updateImsCarrierConfigs(configs); 3349 } catch (RemoteException e) { 3350 throw new ImsException("updateImsCarrierConfigs()", e, 3351 ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); 3352 } 3353 } 3354 } 3355