1 /* 2 * Copyright (C) 2018 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 android.telephony.ims; 18 19 20 import android.Manifest; 21 import android.annotation.CallbackExecutor; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.RequiresFeature; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SuppressAutoDoc; 27 import android.annotation.SuppressLint; 28 import android.annotation.SystemApi; 29 import android.content.Context; 30 import android.content.pm.PackageManager; 31 import android.os.Binder; 32 import android.os.RemoteException; 33 import android.os.ServiceSpecificException; 34 import android.telephony.AccessNetworkConstants; 35 import android.telephony.BinderCacheManager; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.SubscriptionManager; 38 import android.telephony.TelephonyFrameworkInitializer; 39 import android.telephony.ims.aidl.IImsCapabilityCallback; 40 import android.telephony.ims.feature.ImsFeature; 41 import android.telephony.ims.feature.MmTelFeature; 42 import android.telephony.ims.stub.ImsRegistrationImplBase; 43 import android.util.Log; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.telephony.IIntegerConsumer; 47 import com.android.internal.telephony.ITelephony; 48 49 import java.lang.annotation.Retention; 50 import java.lang.annotation.RetentionPolicy; 51 import java.util.Objects; 52 import java.util.concurrent.Executor; 53 import java.util.function.Consumer; 54 55 /** 56 * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated 57 * subscription. 58 * 59 * Allows a user to query the IMS MmTel feature information for a subscription, register for 60 * registration and MmTel capability status callbacks, as well as query/modify user settings for the 61 * associated subscription. 62 * 63 * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this 64 * manager. 65 */ 66 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) 67 public class ImsMmTelManager implements RegistrationManager { 68 private static final String TAG = "ImsMmTelManager"; 69 70 /** 71 * @hide 72 */ 73 @Retention(RetentionPolicy.SOURCE) 74 @IntDef(prefix = "WIFI_MODE_", value = { 75 WIFI_MODE_UNKNOWN, 76 WIFI_MODE_WIFI_ONLY, 77 WIFI_MODE_CELLULAR_PREFERRED, 78 WIFI_MODE_WIFI_PREFERRED 79 }) 80 public @interface WiFiCallingMode {} 81 82 /** 83 * Wifi calling mode is unknown. This is for initialization only. 84 * @hide 85 */ 86 public static final int WIFI_MODE_UNKNOWN = -1; 87 88 /** 89 * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE 90 * registration if signal quality degrades. 91 */ 92 public static final int WIFI_MODE_WIFI_ONLY = 0; 93 94 /** 95 * Prefer registering for IMS over LTE if LTE signal quality is high enough. 96 */ 97 public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; 98 99 /** 100 * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough. 101 */ 102 public static final int WIFI_MODE_WIFI_PREFERRED = 2; 103 104 /** 105 * Callback class for receiving IMS network Registration callback events. 106 * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) 107 * @see #unregisterImsRegistrationCallback(RegistrationCallback) 108 * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead. 109 * @hide 110 */ 111 // Do not add to this class, add to RegistrationManager.RegistrationCallback instead. 112 @Deprecated 113 @SystemApi 114 public static class RegistrationCallback extends RegistrationManager.RegistrationCallback { 115 116 /** 117 * Notifies the framework when the IMS Provider is registered to the IMS network. 118 * 119 * @param imsTransportType the radio access technology. 120 */ 121 @Override onRegistered(@ccessNetworkConstants.TransportType int imsTransportType)122 public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { 123 } 124 125 /** 126 * Notifies the framework when the IMS Provider is trying to register the IMS network. 127 * 128 * @param imsTransportType the radio access technology. 129 */ 130 @Override onRegistering(@ccessNetworkConstants.TransportType int imsTransportType)131 public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { 132 } 133 134 /** 135 * Notifies the framework when the IMS Provider is deregistered from the IMS network. 136 * 137 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 138 */ 139 @Override onUnregistered(@onNull ImsReasonInfo info)140 public void onUnregistered(@NonNull ImsReasonInfo info) { 141 } 142 143 /** 144 * A failure has occurred when trying to handover registration to another technology type. 145 * 146 * @param imsTransportType The transport type that has failed to handover registration to. 147 * @param info A {@link ImsReasonInfo} that identifies the reason for failure. 148 */ 149 @Override onTechnologyChangeFailed( @ccessNetworkConstants.TransportType int imsTransportType, @NonNull ImsReasonInfo info)150 public void onTechnologyChangeFailed( 151 @AccessNetworkConstants.TransportType int imsTransportType, 152 @NonNull ImsReasonInfo info) { 153 } 154 } 155 156 /** 157 * Receives IMS capability status updates from the ImsService. 158 * 159 * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback) 160 * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) 161 */ 162 public static class CapabilityCallback { 163 164 private static class CapabilityBinder extends IImsCapabilityCallback.Stub { 165 166 private final CapabilityCallback mLocalCallback; 167 private Executor mExecutor; 168 CapabilityBinder(CapabilityCallback c)169 CapabilityBinder(CapabilityCallback c) { 170 mLocalCallback = c; 171 } 172 173 @Override onCapabilitiesStatusChanged(int config)174 public void onCapabilitiesStatusChanged(int config) { 175 if (mLocalCallback == null) return; 176 177 final long callingIdentity = Binder.clearCallingIdentity(); 178 try { 179 mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( 180 new MmTelFeature.MmTelCapabilities(config))); 181 } finally { 182 restoreCallingIdentity(callingIdentity); 183 } 184 } 185 186 @Override onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled)187 public void onQueryCapabilityConfiguration(int capability, int radioTech, 188 boolean isEnabled) { 189 // This is not used for public interfaces. 190 } 191 192 @Override onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason)193 public void onChangeCapabilityConfigurationError(int capability, int radioTech, 194 @ImsFeature.ImsCapabilityError int reason) { 195 // This is not used for public interfaces 196 } 197 setExecutor(Executor executor)198 private void setExecutor(Executor executor) { 199 mExecutor = executor; 200 } 201 } 202 203 private final CapabilityBinder mBinder = new CapabilityBinder(this); 204 205 /** 206 * The status of the feature's capabilities has changed to either available or unavailable. 207 * If unavailable, the feature is not able to support the unavailable capability at this 208 * time. 209 * 210 * @param capabilities The new availability of the capabilities. 211 */ onCapabilitiesStatusChanged( @onNull MmTelFeature.MmTelCapabilities capabilities)212 public void onCapabilitiesStatusChanged( 213 @NonNull MmTelFeature.MmTelCapabilities capabilities) { 214 } 215 216 /**@hide*/ getBinder()217 public final IImsCapabilityCallback getBinder() { 218 return mBinder; 219 } 220 221 /**@hide*/ 222 // Only exposed as public method for compatibility with deprecated ImsManager APIs. 223 // TODO: clean up dependencies and change back to private visibility. setExecutor(Executor executor)224 public final void setExecutor(Executor executor) { 225 mBinder.setExecutor(executor); 226 } 227 } 228 229 private final Context mContext; 230 private final int mSubId; 231 private final BinderCacheManager<ITelephony> mBinderCache; 232 233 // Cache Telephony Binder interfaces, one cache per process. 234 private static final BinderCacheManager<ITelephony> sTelephonyCache = 235 new BinderCacheManager<>(ImsMmTelManager::getITelephonyInterface); 236 237 /** 238 * Create an instance of {@link ImsMmTelManager} for the subscription id specified. 239 * 240 * @param subId The ID of the subscription that this ImsMmTelManager will use. 241 * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() 242 * 243 * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 244 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 245 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 246 * 247 * @throws IllegalArgumentException if the subscription is invalid. 248 * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an 249 * instance of this class. 250 * @hide 251 */ 252 @SystemApi 253 @Deprecated 254 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 255 @RequiresPermission(anyOf = { 256 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 257 android.Manifest.permission.READ_PRECISE_PHONE_STATE 258 }) 259 @SuppressLint("ManagerLookup") createForSubscriptionId(int subId)260 public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) { 261 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 262 throw new IllegalArgumentException("Invalid subscription ID"); 263 } 264 265 return new ImsMmTelManager(subId, sTelephonyCache); 266 } 267 268 /** 269 * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. 270 * @hide 271 */ 272 @VisibleForTesting ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache)273 public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { 274 this(null, subId, binderCache); 275 } 276 277 /** 278 * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. 279 * @hide 280 */ 281 @VisibleForTesting ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache)282 public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) { 283 mContext = context; 284 mSubId = subId; 285 mBinderCache = binderCache; 286 } 287 288 /** 289 * Registers a {@link RegistrationCallback} with the system, which will provide registration 290 * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use 291 * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed 292 * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. 293 * 294 * When the callback is registered, it will initiate the callback c to be called with the 295 * current registration state. 296 * 297 * @param executor The executor the callback events should be run on. 298 * @param c The {@link RegistrationCallback} to be added. 299 * @see #unregisterImsRegistrationCallback(RegistrationCallback) 300 * @throws IllegalArgumentException if the subscription associated with this callback is not 301 * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or 302 * {@link CapabilityCallback} callback. 303 * @throws ImsException if the subscription associated with this callback is valid, but 304 * the {@link ImsService} associated with the subscription is not available. This can happen if 305 * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed 306 * reason. 307 * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor, 308 * RegistrationManager.RegistrationCallback)} instead. 309 * @hide 310 */ 311 @Deprecated 312 @SystemApi 313 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) registerImsRegistrationCallback(@onNull @allbackExecutor Executor executor, @NonNull RegistrationCallback c)314 public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, 315 @NonNull RegistrationCallback c) throws ImsException { 316 if (c == null) { 317 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 318 } 319 if (executor == null) { 320 throw new IllegalArgumentException("Must include a non-null Executor."); 321 } 322 c.setExecutor(executor); 323 324 ITelephony iTelephony = getITelephony(); 325 if (iTelephony == null) { 326 throw new ImsException("Could not find Telephony Service.", 327 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 328 } 329 330 try { 331 iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); 332 } catch (ServiceSpecificException e) { 333 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 334 // Rethrow as runtime error to keep API compatible. 335 throw new IllegalArgumentException(e.getMessage()); 336 } else { 337 throw new ImsException(e.getMessage(), e.errorCode); 338 } 339 } catch (RemoteException | IllegalStateException e) { 340 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 341 } 342 } 343 344 /** 345 * 346 * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 347 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 348 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 349 * 350 * {@inheritDoc} 351 * 352 */ 353 @Override 354 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 355 @RequiresPermission(anyOf = { 356 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 357 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) registerImsRegistrationCallback(@onNull @allbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c)358 public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, 359 @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { 360 if (c == null) { 361 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 362 } 363 if (executor == null) { 364 throw new IllegalArgumentException("Must include a non-null Executor."); 365 } 366 c.setExecutor(executor); 367 368 ITelephony iTelephony = getITelephony(); 369 if (iTelephony == null) { 370 throw new ImsException("Could not find Telephony Service.", 371 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 372 } 373 374 try { 375 iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); 376 } catch (ServiceSpecificException e) { 377 throw new ImsException(e.getMessage(), e.errorCode); 378 } catch (RemoteException | IllegalStateException e) { 379 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 380 } 381 } 382 383 /** 384 * Removes an existing {@link RegistrationCallback}. 385 * 386 * When the subscription associated with this callback is removed (SIM removed, ESIM swap, 387 * etc...), this callback will automatically be removed. If this method is called for an 388 * inactive subscription, it will result in a no-op. 389 * 390 * @param c The {@link RegistrationCallback} to be removed. 391 * @see SubscriptionManager.OnSubscriptionsChangedListener 392 * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) 393 * @deprecated Use {@link #unregisterImsRegistrationCallback( 394 * RegistrationManager.RegistrationCallback)}. 395 * @hide 396 */ 397 @Deprecated 398 @SystemApi 399 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) unregisterImsRegistrationCallback(@onNull RegistrationCallback c)400 public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { 401 if (c == null) { 402 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 403 } 404 405 ITelephony iTelephony = getITelephony(); 406 if (iTelephony == null) { 407 throw new RuntimeException("Could not find Telephony Service."); 408 } 409 410 try { 411 iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); 412 } catch (RemoteException e) { 413 throw e.rethrowAsRuntimeException(); 414 } 415 } 416 417 /** 418 * 419 * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 420 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 421 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 422 * Access by profile owners is deprecated and will be removed in a future release. 423 * 424 *{@inheritDoc} 425 */ 426 @Override 427 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 428 @RequiresPermission(anyOf = { 429 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 430 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) unregisterImsRegistrationCallback( @onNull RegistrationManager.RegistrationCallback c)431 public void unregisterImsRegistrationCallback( 432 @NonNull RegistrationManager.RegistrationCallback c) { 433 if (c == null) { 434 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 435 } 436 437 ITelephony iTelephony = getITelephony(); 438 if (iTelephony == null) { 439 throw new RuntimeException("Could not find Telephony Service."); 440 } 441 442 try { 443 iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); 444 } catch (RemoteException e) { 445 throw e.rethrowAsRuntimeException(); 446 } 447 } 448 449 /** 450 * {@inheritDoc} 451 * @hide 452 */ 453 @Override 454 @SystemApi 455 @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getRegistrationState(@onNull @allbackExecutor Executor executor, @NonNull @ImsRegistrationState Consumer<Integer> stateCallback)456 public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, 457 @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) { 458 if (stateCallback == null) { 459 throw new IllegalArgumentException("Must include a non-null callback."); 460 } 461 if (executor == null) { 462 throw new IllegalArgumentException("Must include a non-null Executor."); 463 } 464 465 ITelephony iTelephony = getITelephony(); 466 if (iTelephony == null) { 467 throw new RuntimeException("Could not find Telephony Service."); 468 } 469 470 try { 471 iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() { 472 @Override 473 public void accept(int result) { 474 final long identity = Binder.clearCallingIdentity(); 475 try { 476 executor.execute(() -> stateCallback.accept(result)); 477 } finally { 478 Binder.restoreCallingIdentity(identity); 479 } 480 } 481 }); 482 } catch (ServiceSpecificException | RemoteException e) { 483 Log.w("ImsMmTelManager", "Error getting registration state: " + e); 484 executor.execute(() -> stateCallback.accept(REGISTRATION_STATE_NOT_REGISTERED)); 485 } 486 } 487 488 /** 489 * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 490 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 491 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 492 * Access by profile owners is deprecated and will be removed in a future release. 493 * 494 *{@inheritDoc} 495 */ 496 @Override 497 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 498 @RequiresPermission(anyOf = { 499 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 500 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) getRegistrationTransportType(@onNull @allbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback)501 public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, 502 @NonNull @AccessNetworkConstants.TransportType 503 Consumer<Integer> transportTypeCallback) { 504 if (transportTypeCallback == null) { 505 throw new IllegalArgumentException("Must include a non-null callback."); 506 } 507 if (executor == null) { 508 throw new IllegalArgumentException("Must include a non-null Executor."); 509 } 510 511 ITelephony iTelephony = getITelephony(); 512 if (iTelephony == null) { 513 throw new RuntimeException("Could not find Telephony Service."); 514 } 515 516 try { 517 iTelephony.getImsMmTelRegistrationTransportType(mSubId, 518 new IIntegerConsumer.Stub() { 519 @Override 520 public void accept(int result) { 521 final long identity = Binder.clearCallingIdentity(); 522 try { 523 executor.execute(() -> transportTypeCallback.accept(result)); 524 } finally { 525 Binder.restoreCallingIdentity(identity); 526 } 527 } 528 }); 529 } catch (ServiceSpecificException | RemoteException e) { 530 Log.w("ImsMmTelManager", "Error getting transport type: " + e); 531 executor.execute(() -> transportTypeCallback.accept( 532 AccessNetworkConstants.TRANSPORT_TYPE_INVALID)); 533 } 534 } 535 536 /** 537 * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service 538 * availability updates for the subscription specified in 539 * {@link ImsManager#getImsMmTelManager(int)}. 540 * 541 * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to 542 * subscription changed events and call 543 * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up. 544 * <p>This API requires one of the following: 545 * <ul> 546 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 547 * <li>If the caller is the device or profile owner, the caller holds the 548 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 549 * <li>The caller has carrier privileges (see 550 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 551 * active subscription.</li> 552 * </ul> 553 * <p>The profile owner is an app that owns a managed profile on the device; for more details 554 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 555 * Access by profile owners is deprecated and will be removed in a future release. 556 * 557 * When the callback is registered, it will initiate the callback c to be called with the 558 * current capabilities. 559 * 560 * @param executor The executor the callback events should be run on. 561 * @param c The MmTel {@link CapabilityCallback} to be registered. 562 * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) 563 * @throws ImsException if the subscription associated with this callback is valid, but 564 * the {@link ImsService} associated with the subscription is not available. This can happen if 565 * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed 566 * reason. 567 */ 568 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 569 @RequiresPermission(anyOf = { 570 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 571 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) registerMmTelCapabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull CapabilityCallback c)572 public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor, 573 @NonNull CapabilityCallback c) throws ImsException { 574 if (c == null) { 575 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 576 } 577 if (executor == null) { 578 throw new IllegalArgumentException("Must include a non-null Executor."); 579 } 580 c.setExecutor(executor); 581 582 ITelephony iTelephony = getITelephony(); 583 if (iTelephony == null) { 584 throw new ImsException("Could not find Telephony Service.", 585 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 586 } 587 588 try { 589 iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder()); 590 } catch (ServiceSpecificException e) { 591 throw new ImsException(e.getMessage(), e.errorCode); 592 } catch (RemoteException e) { 593 throw e.rethrowAsRuntimeException(); 594 } catch (IllegalStateException e) { 595 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 596 } 597 } 598 599 /** 600 * Removes an existing MmTel {@link CapabilityCallback}. 601 * 602 * When the subscription associated with this callback is removed (SIM removed, ESIM swap, 603 * etc...), this callback will automatically be removed. If this method is called for an 604 * inactive subscription, it will result in a no-op. 605 * <p>This API requires one of the following: 606 * <ul> 607 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 608 * <li>If the caller is the device or profile owner, the caller holds the 609 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 610 * <li>The caller has carrier privileges (see 611 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 612 * active subscription.</li> 613 * </ul> 614 * <p>The profile owner is an app that owns a managed profile on the device; for more details 615 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 616 * Access by profile owners is deprecated and will be removed in a future release. 617 * 618 * @param c The MmTel {@link CapabilityCallback} to be removed. 619 * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) 620 */ 621 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 622 @RequiresPermission(anyOf = { 623 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 624 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) unregisterMmTelCapabilityCallback(@onNull CapabilityCallback c)625 public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { 626 if (c == null) { 627 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 628 } 629 630 ITelephony iTelephony = getITelephony(); 631 if (iTelephony == null) { 632 Log.w("ImsMmTelManager", "Could not find Telephony Service."); 633 return; 634 } 635 try { 636 iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder()); 637 } catch (RemoteException e) { 638 throw e.rethrowAsRuntimeException(); 639 } 640 } 641 642 /** 643 * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to 644 * enable MmTel IMS features, depending on the carrier configuration for the current 645 * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will 646 * be enabled as long as the carrier has provisioned these services for the specified 647 * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on 648 * carrier requirements. 649 * <p> 650 * Note: If the carrier configuration for advanced calling is not editable or hidden, this 651 * method will always return the default value. 652 * <p>This API requires one of the following: 653 * <ul> 654 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 655 * <li>If the caller is the device or profile owner, the caller holds the 656 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 657 * <li>The caller has carrier privileges (see 658 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 659 * active subscription.</li> 660 * </ul> 661 * <p>The profile owner is an app that owns a managed profile on the device; for more details 662 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 663 * Access by profile owners is deprecated and will be removed in a future release. 664 * 665 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL 666 * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL 667 * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL 668 * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL 669 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL 670 * @throws IllegalArgumentException if the subscription associated with this operation is not 671 * active (SIM is not inserted, ESIM inactive) or invalid. 672 * @return true if the user's setting for advanced calling is enabled, false otherwise. 673 */ 674 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 675 @RequiresPermission(anyOf = { 676 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 677 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) isAdvancedCallingSettingEnabled()678 public boolean isAdvancedCallingSettingEnabled() { 679 ITelephony iTelephony = getITelephony(); 680 if (iTelephony == null) { 681 throw new RuntimeException("Could not find Telephony Service."); 682 } 683 684 try { 685 return iTelephony.isAdvancedCallingSettingEnabled(mSubId); 686 } catch (ServiceSpecificException e) { 687 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 688 // Rethrow as runtime error to keep API compatible. 689 throw new IllegalArgumentException(e.getMessage()); 690 } else { 691 throw new RuntimeException(e.getMessage()); 692 } 693 } catch (RemoteException e) { 694 throw e.rethrowAsRuntimeException(); 695 } 696 } 697 698 /** 699 * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to 700 * enable MmTel IMS features, depending on the carrier configuration for the current 701 * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will 702 * be enabled as long as the carrier has provisioned these services for the specified 703 * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on 704 * carrier requirements. 705 * 706 * Modifying this value may also trigger an IMS registration or deregistration, depending on 707 * whether or not the new value is enabled or disabled. 708 * 709 * Note: If the carrier configuration for advanced calling is not editable or hidden, this 710 * method will do nothing and will instead always use the default value. 711 * 712 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL 713 * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL 714 * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL 715 * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL 716 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL 717 * @see #isAdvancedCallingSettingEnabled() 718 * @throws IllegalArgumentException if the subscription associated with this operation is not 719 * active (SIM is not inserted, ESIM inactive) or invalid. 720 * @hide 721 */ 722 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) 723 @SystemApi setAdvancedCallingSettingEnabled(boolean isEnabled)724 public void setAdvancedCallingSettingEnabled(boolean isEnabled) { 725 ITelephony iTelephony = getITelephony(); 726 if (iTelephony == null) { 727 throw new RuntimeException("Could not find Telephony Service."); 728 } 729 730 try { 731 iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled); 732 } catch (ServiceSpecificException e) { 733 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 734 // Rethrow as runtime error to keep API compatible. 735 throw new IllegalArgumentException(e.getMessage()); 736 } else { 737 throw new RuntimeException(e.getMessage()); 738 } 739 } catch (RemoteException e) { 740 throw e.rethrowAsRuntimeException(); 741 } 742 } 743 744 /** 745 * Query the IMS MmTel capability for a given registration technology. This does not 746 * necessarily mean that we are registered and the capability is available, but rather the 747 * subscription is capable of this service over IMS. 748 * 749 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL 750 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL 751 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL 752 * @see #isAvailable(int, int) 753 * 754 * @param imsRegTech The IMS registration technology. 755 * @param capability The IMS MmTel capability to query. 756 * @return {@code true} if the MmTel IMS capability is capable for this subscription, false 757 * otherwise. 758 * @hide 759 */ 760 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) 761 @SystemApi isCapable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)762 public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 763 @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { 764 ITelephony iTelephony = getITelephony(); 765 if (iTelephony == null) { 766 throw new RuntimeException("Could not find Telephony Service."); 767 } 768 769 try { 770 return iTelephony.isCapable(mSubId, capability, imsRegTech); 771 } catch (RemoteException e) { 772 throw e.rethrowAsRuntimeException(); 773 } 774 } 775 776 /** 777 * Query the availability of an IMS MmTel capability for a given registration technology. If 778 * a capability is available, IMS is registered and the service is currently available over IMS. 779 * 780 * @see #isCapable(int, int) 781 * 782 * @param imsRegTech The IMS registration technology. 783 * @param capability The IMS MmTel capability to query. 784 * @return {@code true} if the MmTel IMS capability is available for this subscription, false 785 * otherwise. 786 * @hide 787 */ 788 @SystemApi 789 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) isAvailable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)790 public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 791 @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { 792 ITelephony iTelephony = getITelephony(); 793 if (iTelephony == null) { 794 throw new RuntimeException("Could not find Telephony Service."); 795 } 796 797 try { 798 return iTelephony.isAvailable(mSubId, capability, imsRegTech); 799 } catch (RemoteException e) { 800 throw e.rethrowAsRuntimeException(); 801 } 802 } 803 804 /** 805 * Query whether or not the requested MmTel capability is supported by the carrier on the 806 * specified network transport. 807 * <p> 808 * This is a configuration option and does not change. The only time this may change is if a 809 * new IMS configuration is loaded when there is a 810 * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription. 811 * @param capability The capability that is being queried for support on the carrier network. 812 * @param transportType The transport type of the capability to check support for. 813 * @param executor The executor that the callback will be called with. 814 * @param callback A consumer containing a Boolean result specifying whether or not the 815 * capability is supported on this carrier network for the transport specified. 816 * @throws ImsException if the subscription is no longer valid or the IMS service is not 817 * available. 818 * @hide 819 */ 820 @SystemApi 821 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) isSupported(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @AccessNetworkConstants.TransportType int transportType, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)822 public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, 823 @AccessNetworkConstants.TransportType int transportType, 824 @NonNull @CallbackExecutor Executor executor, 825 @NonNull Consumer<Boolean> callback) throws ImsException { 826 if (callback == null) { 827 throw new IllegalArgumentException("Must include a non-null Consumer."); 828 } 829 if (executor == null) { 830 throw new IllegalArgumentException("Must include a non-null Executor."); 831 } 832 833 ITelephony iTelephony = getITelephony(); 834 if (iTelephony == null) { 835 throw new ImsException("Could not find Telephony Service.", 836 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 837 } 838 839 try { 840 iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { 841 @Override 842 public void accept(int result) { 843 final long identity = Binder.clearCallingIdentity(); 844 try { 845 executor.execute(() -> callback.accept(result == 1)); 846 } finally { 847 Binder.restoreCallingIdentity(identity); 848 } 849 } 850 }, capability, transportType); 851 } catch (ServiceSpecificException sse) { 852 throw new ImsException(sse.getMessage(), sse.errorCode); 853 } catch (RemoteException e) { 854 e.rethrowAsRuntimeException(); 855 } 856 } 857 858 /** 859 * The user's setting for whether or not they have enabled the "Video Calling" setting. 860 * 861 * <p> 862 * Note: If the carrier configuration for advanced calling is not editable or hidden, this 863 * method will always return the default value. 864 * <p>This API requires one of the following: 865 * <ul> 866 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 867 * <li>If the caller is the device or profile owner, the caller holds the 868 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 869 * <li>The caller has carrier privileges (see 870 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 871 * active subscription.</li> 872 * </ul> 873 * <p>The profile owner is an app that owns a managed profile on the device; for more details 874 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 875 * Access by profile owners is deprecated and will be removed in a future release. 876 * 877 * @throws IllegalArgumentException if the subscription associated with this operation is not 878 * active (SIM is not inserted, ESIM inactive) or invalid. 879 * @return true if the user’s “Video Calling” setting is currently enabled. 880 */ 881 @RequiresPermission(anyOf = { 882 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 883 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) 884 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). isVtSettingEnabled()885 public boolean isVtSettingEnabled() { 886 ITelephony iTelephony = getITelephony(); 887 if (iTelephony == null) { 888 throw new RuntimeException("Could not find Telephony Service."); 889 } 890 891 try { 892 return iTelephony.isVtSettingEnabled(mSubId); 893 } catch (ServiceSpecificException e) { 894 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 895 // Rethrow as runtime error to keep API compatible. 896 throw new IllegalArgumentException(e.getMessage()); 897 } else { 898 throw new RuntimeException(e.getMessage()); 899 } 900 } catch (RemoteException e) { 901 throw e.rethrowAsRuntimeException(); 902 } 903 } 904 905 /** 906 * Change the user's setting for Video Telephony and enable the Video Telephony capability. 907 * 908 * @throws IllegalArgumentException if the subscription associated with this operation is not 909 * active (SIM is not inserted, ESIM inactive) or invalid. 910 * @see #isVtSettingEnabled() 911 * @hide 912 */ 913 @SystemApi 914 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVtSettingEnabled(boolean isEnabled)915 public void setVtSettingEnabled(boolean isEnabled) { 916 ITelephony iTelephony = getITelephony(); 917 if (iTelephony == null) { 918 throw new RuntimeException("Could not find Telephony Service."); 919 } 920 921 try { 922 iTelephony.setVtSettingEnabled(mSubId, isEnabled); 923 } catch (ServiceSpecificException e) { 924 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 925 // Rethrow as runtime error to keep API compatible. 926 throw new IllegalArgumentException(e.getMessage()); 927 } else { 928 throw new RuntimeException(e.getMessage()); 929 } 930 } catch (RemoteException e) { 931 throw e.rethrowAsRuntimeException(); 932 } 933 } 934 935 /** 936 * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. 937 * 938 * <p>This API requires one of the following: 939 * <ul> 940 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 941 * <li>If the caller is the device or profile owner, the caller holds the 942 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 943 * <li>The caller has carrier privileges (see 944 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 945 * active subscription.</li> 946 * </ul> 947 * <p>The profile owner is an app that owns a managed profile on the device; for more details 948 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 949 * Access by profile owners is deprecated and will be removed in a future release. 950 * 951 * @throws IllegalArgumentException if the subscription associated with this operation is not 952 * active (SIM is not inserted, ESIM inactive) or invalid. 953 */ 954 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 955 @RequiresPermission(anyOf = { 956 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 957 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) isVoWiFiSettingEnabled()958 public boolean isVoWiFiSettingEnabled() { 959 ITelephony iTelephony = getITelephony(); 960 if (iTelephony == null) { 961 throw new RuntimeException("Could not find Telephony Service."); 962 } 963 964 try { 965 return iTelephony.isVoWiFiSettingEnabled(mSubId); 966 } catch (ServiceSpecificException e) { 967 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 968 // Rethrow as runtime error to keep API compatible. 969 throw new IllegalArgumentException(e.getMessage()); 970 } else { 971 throw new RuntimeException(e.getMessage()); 972 } 973 } catch (RemoteException e) { 974 throw e.rethrowAsRuntimeException(); 975 } 976 } 977 978 /** 979 * Sets the user's setting for whether or not Voice over WiFi is enabled. 980 * 981 * @throws IllegalArgumentException if the subscription associated with this operation is not 982 * active (SIM is not inserted, ESIM inactive) or invalid. 983 * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= 984 * @see #isVoWiFiSettingEnabled() 985 * @hide 986 */ 987 @SystemApi 988 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVoWiFiSettingEnabled(boolean isEnabled)989 public void setVoWiFiSettingEnabled(boolean isEnabled) { 990 ITelephony iTelephony = getITelephony(); 991 if (iTelephony == null) { 992 throw new RuntimeException("Could not find Telephony Service."); 993 } 994 995 try { 996 iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled); 997 } catch (ServiceSpecificException e) { 998 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 999 // Rethrow as runtime error to keep API compatible. 1000 throw new IllegalArgumentException(e.getMessage()); 1001 } else { 1002 throw new RuntimeException(e.getMessage()); 1003 } 1004 } catch (RemoteException e) { 1005 throw e.rethrowAsRuntimeException(); 1006 } 1007 } 1008 1009 /** 1010 * This configuration is meaningful only on dual sim device. 1011 * If enabled, this will result in the device setting up IMS of all other 1012 * active subscriptions over the INTERNET APN of the primary default data subscription 1013 * when any of those subscriptions are roaming or out of service and if wifi is not available 1014 * for VoWifi. This feature will be disabled if 1015 * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. 1016 * <p>Following are the conditions in which system will try to register IMS over 1017 * cross sim 1018 * <ul> 1019 * <li>Wifi is not available, one SIM is roaming and the default data 1020 * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the 1021 * default data subscription </li> 1022 * <li>Wifi is not available, one SIM is out of service and the default data 1023 * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET 1024 * APN of the default data subscription </li> 1025 * </ul> 1026 * <p>This API requires one of the following: 1027 * <ul> 1028 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 1029 * <li>If the caller is the device or profile owner, the caller holds the 1030 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 1031 * <li>The caller has carrier privileges (see 1032 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 1033 * active subscription.</li> 1034 * </ul> 1035 * <p>The profile owner is an app that owns a managed profile on the device; for more details 1036 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 1037 * Access by profile owners is deprecated and will be removed in a future release. 1038 * 1039 * @throws ImsException if the IMS service associated with this subscription is not available or 1040 * the IMS service is not available. 1041 * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not 1042 */ 1043 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 1044 @RequiresPermission(anyOf = { 1045 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 1046 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) isCrossSimCallingEnabled()1047 public boolean isCrossSimCallingEnabled() throws ImsException { 1048 ITelephony iTelephony = getITelephony(); 1049 if (iTelephony == null) { 1050 throw new ImsException("Could not find Telephony Service.", 1051 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1052 } 1053 1054 try { 1055 return iTelephony.isCrossSimCallingEnabledByUser(mSubId); 1056 } catch (ServiceSpecificException sse) { 1057 throw new ImsException(sse.getMessage(), sse.errorCode); 1058 } catch (RemoteException e) { 1059 e.rethrowAsRuntimeException(); 1060 } 1061 // Not reachable. Adding return to make compiler happy. 1062 return false; 1063 } 1064 1065 /** 1066 * Sets the user's setting for whether or not Voice over Cross SIM is enabled. 1067 * If enabled, this will result in the device setting up IMS of all other 1068 * active subscriptions over the INTERNET APN of the primary default data subscription 1069 * when any of those subscriptions are roaming or out of service and if wifi is not available 1070 * for VoWifi. This feature will be disabled if 1071 * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. 1072 * 1073 * <p>Following are the conditions in which system will try to register IMS over 1074 * cross sim 1075 * <ul> 1076 * <li>Wifi is not available, one SIM is roaming and the default data 1077 * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the 1078 * default data subscription </li> 1079 * <li>Wifi is not available, one SIM is out of service and the default data 1080 * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET 1081 * APN of the default data subscription </li> 1082 * </ul> 1083 * @throws ImsException if the IMS service associated with this subscription is not available or 1084 * the IMS service is not available. 1085 * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled, 1086 * false otherwise 1087 * @see #isCrossSimCallingEnabled() 1088 * @hide 1089 */ 1090 @SystemApi 1091 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setCrossSimCallingEnabled(boolean isEnabled)1092 public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException { 1093 ITelephony iTelephony = getITelephony(); 1094 if (iTelephony == null) { 1095 throw new ImsException("Could not find Telephony Service.", 1096 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1097 } 1098 1099 try { 1100 iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled); 1101 } catch (ServiceSpecificException sse) { 1102 throw new ImsException(sse.getMessage(), sse.errorCode); 1103 } catch (RemoteException e) { 1104 e.rethrowAsRuntimeException(); 1105 } 1106 } 1107 1108 /** 1109 * Returns the user's voice over WiFi roaming setting associated with the current subscription. 1110 * 1111 * <p>This API requires one of the following: 1112 * <ul> 1113 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 1114 * <li>If the caller is the device or profile owner, the caller holds the 1115 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 1116 * <li>The caller has carrier privileges (see 1117 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 1118 * active subscription.</li> 1119 * </ul> 1120 * <p>The profile owner is an app that owns a managed profile on the device; for more details 1121 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 1122 * Access by profile owners is deprecated and will be removed in a future release. 1123 * 1124 * @throws IllegalArgumentException if the subscription associated with this operation is not 1125 * active (SIM is not inserted, ESIM inactive) or invalid. 1126 * @return true if the user's setting for Voice over WiFi while roaming is enabled, false 1127 * if disabled. 1128 */ 1129 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 1130 @RequiresPermission(anyOf = { 1131 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 1132 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) isVoWiFiRoamingSettingEnabled()1133 public boolean isVoWiFiRoamingSettingEnabled() { 1134 ITelephony iTelephony = getITelephony(); 1135 if (iTelephony == null) { 1136 throw new RuntimeException("Could not find Telephony Service."); 1137 } 1138 1139 try { 1140 return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId); 1141 } catch (ServiceSpecificException e) { 1142 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1143 // Rethrow as runtime error to keep API compatible. 1144 throw new IllegalArgumentException(e.getMessage()); 1145 } else { 1146 throw new RuntimeException(e.getMessage()); 1147 } 1148 } catch (RemoteException e) { 1149 throw e.rethrowAsRuntimeException(); 1150 } 1151 } 1152 1153 /** 1154 * Change the user's setting for Voice over WiFi while roaming. 1155 * 1156 * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled, 1157 * false otherwise. 1158 * @throws IllegalArgumentException if the subscription associated with this operation is not 1159 * active (SIM is not inserted, ESIM inactive) or invalid. 1160 * @see #isVoWiFiRoamingSettingEnabled() 1161 * @hide 1162 */ 1163 @SystemApi 1164 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVoWiFiRoamingSettingEnabled(boolean isEnabled)1165 public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { 1166 ITelephony iTelephony = getITelephony(); 1167 if (iTelephony == null) { 1168 throw new RuntimeException("Could not find Telephony Service."); 1169 } 1170 1171 try { 1172 iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); 1173 } catch (ServiceSpecificException e) { 1174 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1175 // Rethrow as runtime error to keep API compatible. 1176 throw new IllegalArgumentException(e.getMessage()); 1177 } else { 1178 throw new RuntimeException(e.getMessage()); 1179 } 1180 } catch (RemoteException e) { 1181 throw e.rethrowAsRuntimeException(); 1182 } 1183 } 1184 1185 /** 1186 * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting. 1187 * Typically used during the Voice over WiFi registration process for some carriers. 1188 * 1189 * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false 1190 * otherwise. 1191 * @param mode the Voice over WiFi mode preference to set, which can be one of the following: 1192 * - {@link #WIFI_MODE_WIFI_ONLY} 1193 * - {@link #WIFI_MODE_CELLULAR_PREFERRED} 1194 * - {@link #WIFI_MODE_WIFI_PREFERRED} 1195 * @throws IllegalArgumentException if the subscription associated with this operation is not 1196 * active (SIM is not inserted, ESIM inactive) or invalid. 1197 * @see #setVoWiFiSettingEnabled(boolean) 1198 * @hide 1199 */ 1200 @SystemApi 1201 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVoWiFiNonPersistent(boolean isCapable, int mode)1202 public void setVoWiFiNonPersistent(boolean isCapable, int mode) { 1203 ITelephony iTelephony = getITelephony(); 1204 if (iTelephony == null) { 1205 throw new RuntimeException("Could not find Telephony Service."); 1206 } 1207 1208 try { 1209 iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode); 1210 } catch (ServiceSpecificException e) { 1211 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1212 // Rethrow as runtime error to keep API compatible. 1213 throw new IllegalArgumentException(e.getMessage()); 1214 } else { 1215 throw new RuntimeException(e.getMessage()); 1216 } 1217 } catch (RemoteException e) { 1218 throw e.rethrowAsRuntimeException(); 1219 } 1220 } 1221 1222 /** 1223 * Returns the user's voice over WiFi Roaming mode setting associated with the device. 1224 * 1225 * <p>This API requires one of the following: 1226 * <ul> 1227 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 1228 * <li>If the caller is the device or profile owner, the caller holds the 1229 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 1230 * <li>The caller has carrier privileges (see 1231 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 1232 * active subscription.</li> 1233 * </ul> 1234 * <p>The profile owner is an app that owns a managed profile on the device; for more details 1235 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 1236 * Access by profile owners is deprecated and will be removed in a future release. 1237 * 1238 * @throws IllegalArgumentException if the subscription associated with this operation is not 1239 * active (SIM is not inserted, ESIM inactive) or invalid. 1240 * @return The Voice over WiFi Mode preference set by the user, which can be one of the 1241 * following: 1242 * - {@link #WIFI_MODE_WIFI_ONLY} 1243 * - {@link #WIFI_MODE_CELLULAR_PREFERRED} 1244 * - {@link #WIFI_MODE_WIFI_PREFERRED} 1245 */ 1246 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 1247 @RequiresPermission(anyOf = { 1248 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 1249 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) getVoWiFiModeSetting()1250 public @WiFiCallingMode int getVoWiFiModeSetting() { 1251 ITelephony iTelephony = getITelephony(); 1252 if (iTelephony == null) { 1253 throw new RuntimeException("Could not find Telephony Service."); 1254 } 1255 1256 try { 1257 return iTelephony.getVoWiFiModeSetting(mSubId); 1258 } catch (ServiceSpecificException e) { 1259 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1260 // Rethrow as runtime error to keep API compatible. 1261 throw new IllegalArgumentException(e.getMessage()); 1262 } else { 1263 throw new RuntimeException(e.getMessage()); 1264 } 1265 } catch (RemoteException e) { 1266 throw e.rethrowAsRuntimeException(); 1267 } 1268 } 1269 1270 /** 1271 * Set the user's preference for Voice over WiFi calling mode. 1272 * @param mode The user's preference for the technology to register for IMS over, can be one of 1273 * the following: 1274 * - {@link #WIFI_MODE_WIFI_ONLY} 1275 * - {@link #WIFI_MODE_CELLULAR_PREFERRED} 1276 * - {@link #WIFI_MODE_WIFI_PREFERRED} 1277 * @throws IllegalArgumentException if the subscription associated with this operation is not 1278 * active (SIM is not inserted, ESIM inactive) or invalid. 1279 * @see #getVoWiFiModeSetting() 1280 * @hide 1281 */ 1282 @SystemApi 1283 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVoWiFiModeSetting(@iFiCallingMode int mode)1284 public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { 1285 ITelephony iTelephony = getITelephony(); 1286 if (iTelephony == null) { 1287 throw new RuntimeException("Could not find Telephony Service."); 1288 } 1289 1290 try { 1291 iTelephony.setVoWiFiModeSetting(mSubId, mode); 1292 } catch (ServiceSpecificException e) { 1293 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1294 // Rethrow as runtime error to keep API compatible. 1295 throw new IllegalArgumentException(e.getMessage()); 1296 } else { 1297 throw new RuntimeException(e.getMessage()); 1298 } 1299 } catch (RemoteException e) { 1300 throw e.rethrowAsRuntimeException(); 1301 } 1302 } 1303 1304 /** 1305 * Set the user's preference for Voice over WiFi calling mode while the device is roaming on 1306 * another network. 1307 * 1308 * @return The user's preference for the technology to register for IMS over when roaming on 1309 * another network, can be one of the following: 1310 * - {@link #WIFI_MODE_WIFI_ONLY} 1311 * - {@link #WIFI_MODE_CELLULAR_PREFERRED} 1312 * - {@link #WIFI_MODE_WIFI_PREFERRED} 1313 * @throws IllegalArgumentException if the subscription associated with this operation is not 1314 * active (SIM is not inserted, ESIM inactive) or invalid. 1315 * @see #setVoWiFiRoamingSettingEnabled(boolean) 1316 * @hide 1317 */ 1318 @SystemApi 1319 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getVoWiFiRoamingModeSetting()1320 public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { 1321 ITelephony iTelephony = getITelephony(); 1322 if (iTelephony == null) { 1323 throw new RuntimeException("Could not find Telephony Service."); 1324 } 1325 1326 try { 1327 return iTelephony.getVoWiFiRoamingModeSetting(mSubId); 1328 } catch (ServiceSpecificException e) { 1329 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1330 // Rethrow as runtime error to keep API compatible. 1331 throw new IllegalArgumentException(e.getMessage()); 1332 } else { 1333 throw new RuntimeException(e.getMessage()); 1334 } 1335 } catch (RemoteException e) { 1336 throw e.rethrowAsRuntimeException(); 1337 } 1338 } 1339 1340 /** 1341 * Set the user's preference for Voice over WiFi mode while the device is roaming on another 1342 * network. 1343 * 1344 * @param mode The user's preference for the technology to register for IMS over when roaming on 1345 * another network, can be one of the following: 1346 * - {@link #WIFI_MODE_WIFI_ONLY} 1347 * - {@link #WIFI_MODE_CELLULAR_PREFERRED} 1348 * - {@link #WIFI_MODE_WIFI_PREFERRED} 1349 * @throws IllegalArgumentException if the subscription associated with this operation is not 1350 * active (SIM is not inserted, ESIM inactive) or invalid. 1351 * @see #getVoWiFiRoamingModeSetting() 1352 * @hide 1353 */ 1354 @SystemApi 1355 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setVoWiFiRoamingModeSetting(@iFiCallingMode int mode)1356 public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { 1357 ITelephony iTelephony = getITelephony(); 1358 if (iTelephony == null) { 1359 throw new RuntimeException("Could not find Telephony Service."); 1360 } 1361 1362 try { 1363 iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode); 1364 } catch (ServiceSpecificException e) { 1365 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1366 // Rethrow as runtime error to keep API compatible. 1367 throw new IllegalArgumentException(e.getMessage()); 1368 } else { 1369 throw new RuntimeException(e.getMessage()); 1370 } 1371 } catch (RemoteException e) { 1372 throw e.rethrowAsRuntimeException(); 1373 } 1374 } 1375 1376 /** 1377 * Sets the capability of RTT for IMS calls placed on this subscription. 1378 * 1379 * Note: This does not affect the value of 1380 * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting 1381 * for RTT. That value is enabled/disabled separately by the user through the Accessibility 1382 * settings. 1383 * @throws IllegalArgumentException if the subscription associated with this operation is not 1384 * active (SIM is not inserted, ESIM inactive) or invalid. 1385 * @param isEnabled if true RTT should be enabled during calls made on this subscription. 1386 * @hide 1387 */ 1388 @SystemApi 1389 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setRttCapabilitySetting(boolean isEnabled)1390 public void setRttCapabilitySetting(boolean isEnabled) { 1391 ITelephony iTelephony = getITelephony(); 1392 if (iTelephony == null) { 1393 throw new RuntimeException("Could not find Telephony Service."); 1394 } 1395 1396 try { 1397 iTelephony.setRttCapabilitySetting(mSubId, isEnabled); 1398 } catch (ServiceSpecificException e) { 1399 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1400 // Rethrow as runtime error to keep API compatible. 1401 throw new IllegalArgumentException(e.getMessage()); 1402 } else { 1403 throw new RuntimeException(e.getMessage()); 1404 } 1405 } catch (RemoteException e) { 1406 throw e.rethrowAsRuntimeException(); 1407 } 1408 } 1409 1410 /** 1411 * @return true if TTY over VoLTE is supported 1412 * 1413 * <p>This API requires one of the following: 1414 * <ul> 1415 * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> 1416 * <li>If the caller is the device or profile owner, the caller holds the 1417 * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> 1418 * <li>The caller has carrier privileges (see 1419 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any 1420 * active subscription.</li> 1421 * </ul> 1422 * <p>The profile owner is an app that owns a managed profile on the device; for more details 1423 * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. 1424 * Access by profile owners is deprecated and will be removed in a future release. 1425 * 1426 * @throws IllegalArgumentException if the subscription associated with this operation is not 1427 * active (SIM is not inserted, ESIM inactive) or invalid. 1428 * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL 1429 */ 1430 @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). 1431 @RequiresPermission(anyOf = { 1432 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 1433 android.Manifest.permission.READ_PRECISE_PHONE_STATE}) isTtyOverVolteEnabled()1434 public boolean isTtyOverVolteEnabled() { 1435 ITelephony iTelephony = getITelephony(); 1436 if (iTelephony == null) { 1437 throw new RuntimeException("Could not find Telephony Service."); 1438 } 1439 1440 try { 1441 return iTelephony.isTtyOverVolteEnabled(mSubId); 1442 } catch (ServiceSpecificException e) { 1443 if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { 1444 // Rethrow as runtime error to keep API compatible. 1445 throw new IllegalArgumentException(e.getMessage()); 1446 } else { 1447 throw new RuntimeException(e.getMessage()); 1448 } 1449 } catch (RemoteException e) { 1450 throw e.rethrowAsRuntimeException(); 1451 } 1452 } 1453 1454 /** 1455 * Get the status of the MmTel Feature registered on this subscription. 1456 * @param executor The executor that will be used to call the callback. 1457 * @param callback A callback containing an Integer describing the current state of the 1458 * MmTel feature, Which will be one of the following: 1459 * {@link ImsFeature#STATE_UNAVAILABLE}, 1460 * {@link ImsFeature#STATE_INITIALIZING}, 1461 * {@link ImsFeature#STATE_READY}. Will be called using the executor 1462 * specified when the service state has been retrieved from the IMS service. 1463 * @throws ImsException if the IMS service associated with this subscription is not available or 1464 * the IMS service is not available. 1465 * @hide 1466 */ 1467 @SystemApi 1468 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getFeatureState(@onNull @allbackExecutor Executor executor, @NonNull @ImsFeature.ImsState Consumer<Integer> callback)1469 public void getFeatureState(@NonNull @CallbackExecutor Executor executor, 1470 @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException { 1471 if (executor == null) { 1472 throw new IllegalArgumentException("Must include a non-null Executor."); 1473 } 1474 if (callback == null) { 1475 throw new IllegalArgumentException("Must include a non-null Consumer."); 1476 } 1477 1478 ITelephony iTelephony = getITelephony(); 1479 if (iTelephony == null) { 1480 throw new ImsException("Could not find Telephony Service.", 1481 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1482 } 1483 1484 try { 1485 iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { 1486 @Override 1487 public void accept(int result) { 1488 final long identity = Binder.clearCallingIdentity(); 1489 try { 1490 executor.execute(() -> callback.accept(result)); 1491 } finally { 1492 Binder.restoreCallingIdentity(identity); 1493 } 1494 } 1495 }); 1496 } catch (ServiceSpecificException sse) { 1497 throw new ImsException(sse.getMessage(), sse.errorCode); 1498 } catch (RemoteException e) { 1499 e.rethrowAsRuntimeException(); 1500 } 1501 } 1502 1503 /** 1504 * Register a new callback, which is used to notify the registrant of changes to 1505 * the state of the underlying IMS service that is attached to telephony to 1506 * implement IMS functionality. If the manager is created for 1507 * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, 1508 * this throws an {@link ImsException}. 1509 * 1510 * <p>Requires Permission: 1511 * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} 1512 * or that the calling app has carrier privileges 1513 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 1514 * 1515 * @param executor the Executor that will be used to call the {@link ImsStateCallback}. 1516 * @param callback The callback instance being registered. 1517 * @throws ImsException in the case that the callback can not be registered. 1518 * See {@link ImsException#getCode} for more information on when this is called. 1519 */ 1520 @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, 1521 Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) registerImsStateCallback(@onNull Executor executor, @NonNull ImsStateCallback callback)1522 public void registerImsStateCallback(@NonNull Executor executor, 1523 @NonNull ImsStateCallback callback) throws ImsException { 1524 Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); 1525 Objects.requireNonNull(executor, "Must include a non-null Executor."); 1526 1527 callback.init(executor); 1528 ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied); 1529 if (telephony == null) { 1530 throw new ImsException("Telephony server is down", 1531 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1532 } 1533 1534 try { 1535 telephony.registerImsStateCallback( 1536 mSubId, ImsFeature.FEATURE_MMTEL, 1537 callback.getCallbackBinder(), getOpPackageName()); 1538 } catch (ServiceSpecificException e) { 1539 throw new ImsException(e.getMessage(), e.errorCode); 1540 } catch (RemoteException | IllegalStateException e) { 1541 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1542 } 1543 } 1544 1545 /** 1546 * Unregisters a previously registered callback. 1547 * 1548 * @param callback The callback instance to be unregistered. 1549 */ unregisterImsStateCallback(@onNull ImsStateCallback callback)1550 public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { 1551 Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); 1552 1553 ITelephony telephony = mBinderCache.removeRunnable(callback); 1554 try { 1555 if (telephony != null) { 1556 telephony.unregisterImsStateCallback(callback.getCallbackBinder()); 1557 } 1558 } catch (RemoteException ignore) { 1559 // ignore it 1560 } 1561 } 1562 getOpPackageName()1563 private String getOpPackageName() { 1564 if (mContext != null) { 1565 return mContext.getOpPackageName(); 1566 } else { 1567 return null; 1568 } 1569 } 1570 getITelephony()1571 private ITelephony getITelephony() { 1572 return mBinderCache.getBinder(); 1573 } 1574 getITelephonyInterface()1575 private static ITelephony getITelephonyInterface() { 1576 ITelephony binder = ITelephony.Stub.asInterface( 1577 TelephonyFrameworkInitializer 1578 .getTelephonyServiceManager() 1579 .getTelephonyServiceRegisterer() 1580 .get()); 1581 return binder; 1582 } 1583 1584 /** 1585 * Convert Wi-Fi calling mode to string. 1586 * 1587 * @param mode Wi-Fi calling mode. 1588 * @return The Wi-Fi calling mode in string format. 1589 * 1590 * @hide 1591 */ 1592 @NonNull wifiCallingModeToString(@msMmTelManager.WiFiCallingMode int mode)1593 public static String wifiCallingModeToString(@ImsMmTelManager.WiFiCallingMode int mode) { 1594 switch (mode) { 1595 case ImsMmTelManager.WIFI_MODE_UNKNOWN: return "UNKNOWN"; 1596 case ImsMmTelManager.WIFI_MODE_WIFI_ONLY: return "WIFI_ONLY"; 1597 case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED: return "CELLULAR_PREFERRED"; 1598 case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: return "WIFI_PREFERRED"; 1599 default: 1600 return "UNKNOWN(" + mode + ")"; 1601 } 1602 } 1603 } 1604