/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.telephony.ims; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; import android.telephony.BinderCacheManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated * subscription. * * Allows a user to query the IMS MmTel feature information for a subscription, register for * registration and MmTel capability status callbacks, as well as query/modify user settings for the * associated subscription. * * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this * manager. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public class ImsMmTelManager implements RegistrationManager { private static final String TAG = "ImsMmTelManager"; /** * @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "WIFI_MODE_", value = { WIFI_MODE_UNKNOWN, WIFI_MODE_WIFI_ONLY, WIFI_MODE_CELLULAR_PREFERRED, WIFI_MODE_WIFI_PREFERRED }) public @interface WiFiCallingMode {} /** * Wifi calling mode is unknown. This is for initialization only. * @hide */ public static final int WIFI_MODE_UNKNOWN = -1; /** * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE * registration if signal quality degrades. */ public static final int WIFI_MODE_WIFI_ONLY = 0; /** * Prefer registering for IMS over LTE if LTE signal quality is high enough. */ public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; /** * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough. */ public static final int WIFI_MODE_WIFI_PREFERRED = 2; /** * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) * @see #unregisterImsRegistrationCallback(RegistrationCallback) * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead. * @hide */ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead. @Deprecated @SystemApi public static class RegistrationCallback extends RegistrationManager.RegistrationCallback { /** * Notifies the framework when the IMS Provider is registered to the IMS network. * * @param imsTransportType the radio access technology. */ @Override public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { } /** * Notifies the framework when the IMS Provider is trying to register the IMS network. * * @param imsTransportType the radio access technology. */ @Override public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { } /** * Notifies the framework when the IMS Provider is deregistered from the IMS network. * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. */ @Override public void onUnregistered(@NonNull ImsReasonInfo info) { } /** * A failure has occurred when trying to handover registration to another technology type. * * @param imsTransportType The transport type that has failed to handover registration to. * @param info A {@link ImsReasonInfo} that identifies the reason for failure. */ @Override public void onTechnologyChangeFailed( @AccessNetworkConstants.TransportType int imsTransportType, @NonNull ImsReasonInfo info) { } } /** * Receives IMS capability status updates from the ImsService. * * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback) * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) */ public static class CapabilityCallback { private static class CapabilityBinder extends IImsCapabilityCallback.Stub { private final CapabilityCallback mLocalCallback; private Executor mExecutor; CapabilityBinder(CapabilityCallback c) { mLocalCallback = c; } @Override public void onCapabilitiesStatusChanged(int config) { if (mLocalCallback == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged( new MmTelFeature.MmTelCapabilities(config))); } finally { restoreCallingIdentity(callingIdentity); } } @Override public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled) { // This is not used for public interfaces. } @Override public void onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason) { // This is not used for public interfaces } private void setExecutor(Executor executor) { mExecutor = executor; } } private final CapabilityBinder mBinder = new CapabilityBinder(this); /** * The status of the feature's capabilities has changed to either available or unavailable. * If unavailable, the feature is not able to support the unavailable capability at this * time. * * @param capabilities The new availability of the capabilities. */ public void onCapabilitiesStatusChanged( @NonNull MmTelFeature.MmTelCapabilities capabilities) { } /**@hide*/ public final IImsCapabilityCallback getBinder() { return mBinder; } /**@hide*/ // Only exposed as public method for compatibility with deprecated ImsManager APIs. // TODO: clean up dependencies and change back to private visibility. public final void setExecutor(Executor executor) { mBinder.setExecutor(executor); } } private final Context mContext; private final int mSubId; private final BinderCacheManager mBinderCache; // Cache Telephony Binder interfaces, one cache per process. private static final BinderCacheManager sTelephonyCache = new BinderCacheManager<>(ImsMmTelManager::getITelephonyInterface); /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. * * @param subId The ID of the subscription that this ImsMmTelManager will use. * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() * *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * * @throws IllegalArgumentException if the subscription is invalid. * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an * instance of this class. * @hide */ @SystemApi @Deprecated @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE }) @SuppressLint("ManagerLookup") public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid subscription ID"); } return new ImsMmTelManager(subId, sTelephonyCache); } /** * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. * @hide */ @VisibleForTesting public ImsMmTelManager(int subId, BinderCacheManager binderCache) { this(null, subId, binderCache); } /** * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. * @hide */ @VisibleForTesting public ImsMmTelManager(Context context, int subId, BinderCacheManager binderCache) { mContext = context; mSubId = subId; mBinderCache = binderCache; } /** * Registers a {@link RegistrationCallback} with the system, which will provide registration * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the * current registration state. * * @param executor The executor the callback events should be run on. * @param c The {@link RegistrationCallback} to be added. * @see #unregisterImsRegistrationCallback(RegistrationCallback) * @throws IllegalArgumentException if the subscription associated with this callback is not * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or * {@link CapabilityCallback} callback. * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor, * RegistrationManager.RegistrationCallback)} instead. * @hide */ @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new ImsException(e.getMessage(), e.errorCode); } } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * * {@inheritDoc} * */ @Override @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Removes an existing {@link RegistrationCallback}. * * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be removed. If this method is called for an * inactive subscription, it will result in a no-op. * * @param c The {@link RegistrationCallback} to be removed. * @see SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) * @deprecated Use {@link #unregisterImsRegistrationCallback( * RegistrationManager.RegistrationCallback)}. * @hide */ @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * Access by profile owners is deprecated and will be removed in a future release. * *{@inheritDoc} */ @Override @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Registers a {@link RegistrationCallback} with the system, which will provide IMS emergency * registration updates for the subscription specified in * {@link ImsManager#getImsMmTelManager(int)}. Use * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the * current emergency registration state. * Emergency registration callback is available when there is valid SIM card. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @param executor The executor the callback events should be run on. * @param c The {@link RegistrationCallback} to be added. * @see #unregisterImsEmergencyRegistrationCallback * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) public void registerImsEmergencyRegistrationCallback( @NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.registerImsEmergencyRegistrationCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Removes an existing {@link RegistrationCallback} for Emergency IMS registration. * * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be removed. If this method is called for an * inactive subscription, it will result in a no-op. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @param c The {@link RegistrationCallback} to be removed. * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsEmergencyRegistrationCallback(Executor, * RegistrationManager.RegistrationCallback) * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) public void unregisterImsEmergencyRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { Log.w("ImsMmTelManager", "Could not find Telephony Service."); return; } try { iTelephony.unregisterImsEmergencyRegistrationCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * {@inheritDoc} * @hide */ @Override @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsRegistrationState Consumer stateCallback) { if (stateCallback == null) { throw new IllegalArgumentException("Must include a non-null callback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> stateCallback.accept(result)); } finally { Binder.restoreCallingIdentity(identity); } } }); } catch (ServiceSpecificException | RemoteException e) { Log.w("ImsMmTelManager", "Error getting registration state: " + e); executor.execute(() -> stateCallback.accept(REGISTRATION_STATE_NOT_REGISTERED)); } } /** *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * Access by profile owners is deprecated and will be removed in a future release. * *{@inheritDoc} */ @Override @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer transportTypeCallback) { if (transportTypeCallback == null) { throw new IllegalArgumentException("Must include a non-null callback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.getImsMmTelRegistrationTransportType(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> transportTypeCallback.accept(result)); } finally { Binder.restoreCallingIdentity(identity); } } }); } catch (ServiceSpecificException | RemoteException e) { Log.w("ImsMmTelManager", "Error getting transport type: " + e); executor.execute(() -> transportTypeCallback.accept( AccessNetworkConstants.TRANSPORT_TYPE_INVALID)); } } /** * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service * availability updates for the subscription specified in * {@link ImsManager#getImsMmTelManager(int)}. * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * When the callback is registered, it will initiate the callback c to be called with the * current capabilities. * * @param executor The executor the callback events should be run on. * @param c The MmTel {@link CapabilityCallback} to be registered. * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) * @throws ImsException if the subscription associated with this callback is valid, but * the {@code ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor, @NonNull CapabilityCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } c.setExecutor(executor); ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Removes an existing MmTel {@link CapabilityCallback}. * * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be removed. If this method is called for an * inactive subscription, it will result in a no-op. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @param c The MmTel {@link CapabilityCallback} to be removed. * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { Log.w("ImsMmTelManager", "Could not find Telephony Service."); return; } try { iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to * enable MmTel IMS features, depending on the carrier configuration for the current * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will * be enabled as long as the carrier has provisioned these services for the specified * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on * carrier requirements. *

* Note: If the carrier configuration for advanced calling is not editable or hidden, this * method will always return the default value. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @return true if the user's setting for advanced calling is enabled, false otherwise. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isAdvancedCallingSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to * enable MmTel IMS features, depending on the carrier configuration for the current * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will * be enabled as long as the carrier has provisioned these services for the specified * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on * carrier requirements. * * Modifying this value may also trigger an IMS registration or deregistration, depending on * whether or not the new value is enabled or disabled. * * Note: If the carrier configuration for advanced calling is not editable or hidden, this * method will do nothing and will instead always use the default value. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see #isAdvancedCallingSettingEnabled() * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi public void setAdvancedCallingSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Query the IMS MmTel capability for a given registration technology. This does not * necessarily mean that we are registered and the capability is available, but rather the * subscription is capable of this service over IMS. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL * @see #isAvailable(int, int) * * @param imsRegTech The IMS registration technology. * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is capable for this subscription, false * otherwise. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isCapable(mSubId, capability, imsRegTech); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Query the availability of an IMS MmTel capability for a given registration technology. If * a capability is available, IMS is registered and the service is currently available over IMS. * * @see #isCapable(int, int) * * @param imsRegTech The IMS registration technology. * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is available for this subscription, false * otherwise. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isAvailable(mSubId, capability, imsRegTech); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Query whether or not the requested MmTel capability is supported by the carrier on the * specified network transport. *

* This is a configuration option and does not change. The only time this may change is if a * new IMS configuration is loaded when there is a * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription. * @param capability The capability that is being queried for support on the carrier network. * @param transportType The transport type of the capability to check support for. * @param executor The executor that the callback will be called with. * @param callback A consumer containing a Boolean result specifying whether or not the * capability is supported on this carrier network for the transport specified. * @throws ImsException if the subscription is no longer valid or the IMS service is not * available. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @AccessNetworkConstants.TransportType int transportType, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer callback) throws ImsException { if (callback == null) { throw new IllegalArgumentException("Must include a non-null Consumer."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> callback.accept(result == 1)); } finally { Binder.restoreCallingIdentity(identity); } } }, capability, transportType); } catch (ServiceSpecificException sse) { throw new ImsException(sse.getMessage(), sse.errorCode); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** * The user's setting for whether or not they have enabled the "Video Calling" setting. * *

* Note: If the carrier configuration for advanced calling is not editable or hidden, this * method will always return the default value. *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @return true if the user’s “Video Calling” setting is currently enabled. */ @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). public boolean isVtSettingEnabled() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isVtSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Change the user's setting for Video Telephony and enable the Video Telephony capability. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #isVtSettingEnabled() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVtSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. * *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isVoWiFiSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Sets the user's setting for whether or not Voice over WiFi is enabled. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= * @see #isVoWiFiSettingEnabled() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * This configuration is meaningful only on dual sim device. * If enabled, this will result in the device setting up IMS of all other * active subscriptions over the INTERNET APN of the primary default data subscription * when any of those subscriptions are roaming or out of service and if wifi is not available * for VoWifi. This feature will be disabled if * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. *

Following are the conditions in which system will try to register IMS over * cross sim *

*

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws ImsException if the IMS service associated with this subscription is not available or * the IMS service is not available. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabled() throws ImsException { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { return iTelephony.isCrossSimCallingEnabledByUser(mSubId); } catch (ServiceSpecificException sse) { throw new ImsException(sse.getMessage(), sse.errorCode); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } // Not reachable. Adding return to make compiler happy. return false; } /** * Sets the user's setting for whether or not Voice over Cross SIM is enabled. * If enabled, this will result in the device setting up IMS of all other * active subscriptions over the INTERNET APN of the primary default data subscription * when any of those subscriptions are roaming or out of service and if wifi is not available * for VoWifi. This feature will be disabled if * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. * *

Following are the conditions in which system will try to register IMS over * cross sim *

* @throws ImsException if the IMS service associated with this subscription is not available or * the IMS service is not available. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled, * false otherwise * @see #isCrossSimCallingEnabled() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException sse) { throw new ImsException(sse.getMessage(), sse.errorCode); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** * Returns the user's voice over WiFi roaming setting associated with the current subscription. * *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @return true if the user's setting for Voice over WiFi while roaming is enabled, false * if disabled. */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Change the user's setting for Voice over WiFi while roaming. * * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled, * false otherwise. * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #isVoWiFiRoamingSettingEnabled() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting. * Typically used during the Voice over WiFi registration process for some carriers. * * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false * otherwise. * @param mode the Voice over WiFi mode preference to set, which can be one of the following: * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #setVoWiFiSettingEnabled(boolean) * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Returns the user's voice over WiFi Roaming mode setting associated with the device. * *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @return The Voice over WiFi Mode preference set by the user, which can be one of the * following: * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public @WiFiCallingMode int getVoWiFiModeSetting() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.getVoWiFiModeSetting(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Set the user's preference for Voice over WiFi calling mode. * @param mode The user's preference for the technology to register for IMS over, can be one of * the following: * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #getVoWiFiModeSetting() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVoWiFiModeSetting(mSubId, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Set the user's preference for Voice over WiFi calling mode while the device is roaming on * another network. * * @return The user's preference for the technology to register for IMS over when roaming on * another network, can be one of the following: * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #setVoWiFiRoamingSettingEnabled(boolean) * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.getVoWiFiRoamingModeSetting(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Set the user's preference for Voice over WiFi mode while the device is roaming on another * network. * * @param mode The user's preference for the technology to register for IMS over when roaming on * another network, can be one of the following: * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see #getVoWiFiRoamingModeSetting() * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Sets the capability of RTT for IMS calls placed on this subscription. * * Note: This does not affect the value of * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting * for RTT. That value is enabled/disabled separately by the user through the Accessibility * settings. * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @param isEnabled if true RTT should be enabled during calls made on this subscription. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { iTelephony.setRttCapabilitySetting(mSubId, isEnabled); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * @return true if TTY over VoLTE is supported * *

This API requires one of the following: *

*

The profile owner is an app that owns a managed profile on the device; for more details * see Work profiles. * Access by profile owners is deprecated and will be removed in a future release. * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL */ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled() { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new RuntimeException("Could not find Telephony Service."); } try { return iTelephony.isTtyOverVolteEnabled(mSubId); } catch (ServiceSpecificException e) { if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { // Rethrow as runtime error to keep API compatible. throw new IllegalArgumentException(e.getMessage()); } else { throw new RuntimeException(e.getMessage()); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** * Get the status of the MmTel Feature registered on this subscription. * @param executor The executor that will be used to call the callback. * @param callback A callback containing an Integer describing the current state of the * MmTel feature, Which will be one of the following: * {@link ImsFeature#STATE_UNAVAILABLE}, * {@link ImsFeature#STATE_INITIALIZING}, * {@link ImsFeature#STATE_READY}. Will be called using the executor * specified when the service state has been retrieved from the IMS service. * @throws ImsException if the IMS service associated with this subscription is not available or * the IMS service is not available. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsFeature.ImsState Consumer callback) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } if (callback == null) { throw new IllegalArgumentException("Must include a non-null Consumer."); } ITelephony iTelephony = getITelephony(); if (iTelephony == null) { throw new ImsException("Could not find Telephony Service.", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> callback.accept(result)); } finally { Binder.restoreCallingIdentity(identity); } } }); } catch (ServiceSpecificException sse) { throw new ImsException(sse.getMessage(), sse.errorCode); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** * Register a new callback, which is used to notify the registrant of changes to * the state of the underlying IMS service that is attached to telephony to * implement IMS functionality. If the manager is created for * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, * this throws an {@link ImsException}. * *

Requires Permission: * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} * or that the calling app has carrier privileges * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * * @param executor the Executor that will be used to call the {@link ImsStateCallback}. * @param callback The callback instance being registered. * @throws ImsException in the case that the callback can not be registered. * See {@link ImsException#getCode} for more information on when this is called. */ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) public void registerImsStateCallback(@NonNull Executor executor, @NonNull ImsStateCallback callback) throws ImsException { Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); Objects.requireNonNull(executor, "Must include a non-null Executor."); callback.init(executor); ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied); if (telephony == null) { throw new ImsException("Telephony server is down", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { telephony.registerImsStateCallback( mSubId, ImsFeature.FEATURE_MMTEL, callback.getCallbackBinder(), getOpPackageName()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Unregisters a previously registered callback. * * @param callback The callback instance to be unregistered. */ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); ITelephony telephony = mBinderCache.removeRunnable(callback); try { if (telephony != null) { telephony.unregisterImsStateCallback(callback.getCallbackBinder()); } } catch (RemoteException ignore) { // ignore it } } private String getOpPackageName() { if (mContext != null) { return mContext.getOpPackageName(); } else { return null; } } private ITelephony getITelephony() { return mBinderCache.getBinder(); } private static ITelephony getITelephonyInterface() { ITelephony binder = ITelephony.Stub.asInterface( TelephonyFrameworkInitializer .getTelephonyServiceManager() .getTelephonyServiceRegisterer() .get()); return binder; } /** * Convert Wi-Fi calling mode to string. * * @param mode Wi-Fi calling mode. * @return The Wi-Fi calling mode in string format. * * @hide */ @NonNull public static String wifiCallingModeToString(@ImsMmTelManager.WiFiCallingMode int mode) { switch (mode) { case ImsMmTelManager.WIFI_MODE_UNKNOWN: return "UNKNOWN"; case ImsMmTelManager.WIFI_MODE_WIFI_ONLY: return "WIFI_ONLY"; case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED: return "CELLULAR_PREFERRED"; case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: return "WIFI_PREFERRED"; default: return "UNKNOWN(" + mode + ")"; } } }