script-astra/Android/Sdk/sources/android-35/android/telephony/ims/ImsMmTelManager.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

1752 lines
78 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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<ITelephony> mBinderCache;
// Cache Telephony Binder interfaces, one cache per process.
private static final BinderCacheManager<ITelephony> 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()
*
* <p>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<ITelephony> binderCache) {
this(null, subId, binderCache);
}
/**
* Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
* @hide
*/
@VisibleForTesting
public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> 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);
}
}
/**
*
* <p>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();
}
}
/**
*
* <p>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.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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<Integer> 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));
}
}
/**
* <p>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<Integer> 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.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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 users 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.
* <p>
* Note: If the carrier configuration for advanced calling is not editable or hidden, this
* method will always return the default value.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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 users 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.
* <p>
* 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<Boolean> 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.
*
* <p>
* Note: If the carrier configuration for advanced calling is not editable or hidden, this
* method will always return the default value.
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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 users “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.
*
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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.
* <p>Following are the conditions in which system will try to register IMS over
* cross sim
* <ul>
* <li>Wifi is not available, one SIM is roaming and the default data
* SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
* default data subscription </li>
* <li>Wifi is not available, one SIM is out of service and the default data
* SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
* APN of the default data subscription </li>
* </ul>
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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.
*
* <p>Following are the conditions in which system will try to register IMS over
* cross sim
* <ul>
* <li>Wifi is not available, one SIM is roaming and the default data
* SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
* default data subscription </li>
* <li>Wifi is not available, one SIM is out of service and the default data
* SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
* APN of the default data subscription </li>
* </ul>
* @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.
*
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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.
*
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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
*
* <p>This API requires one of the following:
* <ul>
* <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
* <li>If the caller is the device or profile owner, the caller holds the
* {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
* <li>The caller has carrier privileges (see
* {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
* active subscription.</li>
* </ul>
* <p>The profile owner is an app that owns a managed profile on the device; for more details
* see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
* 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<Integer> 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}.
*
* <p>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 + ")";
}
}
}