/* * 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.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; /** * Manages RCS User Capability Exchange for the subscription specified. * * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class. */ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) public class RcsUceAdapter { private static final String TAG = "RcsUceAdapter"; /** * This carrier supports User Capability Exchange as, defined by the framework using * SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this * RcsFeature should not publish capabilities or service capability requests. * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead. * @hide */ @Deprecated public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; /** * This carrier supports User Capability Exchange as, defined by the framework using a * presence server. If set, the RcsFeature should support capability exchange. If not set, this * RcsFeature should not publish capabilities or service capability requests. * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead. * @hide */ @Deprecated @SystemApi public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; /** * @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead. * @hide */ @Deprecated @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CAPABILITY_TYPE_", value = { CAPABILITY_TYPE_OPTIONS_UCE, CAPABILITY_TYPE_PRESENCE_UCE }) public @interface RcsImsCapabilityFlag {} /** * An unknown error has caused the request to fail. * @hide */ @SystemApi public static final int ERROR_GENERIC_FAILURE = 1; /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ @SystemApi public static final int ERROR_NOT_ENABLED = 2; /** * The data network that the device is connected to does not support UCE currently (e.g. it is * 1x only currently). * @hide */ @SystemApi public static final int ERROR_NOT_AVAILABLE = 3; /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ @SystemApi public static final int ERROR_NOT_REGISTERED = 4; /** * The network has responded to this request with a SIP 403 error and reason "not authorized for * presence" for this subscriber. * @hide */ @SystemApi public static final int ERROR_NOT_AUTHORIZED = 5; /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ @SystemApi public static final int ERROR_FORBIDDEN = 6; /** * The contact URI requested is not provisioned for voice or it is not known as an IMS * subscriber to the carrier network. * @hide */ @SystemApi public static final int ERROR_NOT_FOUND = 7; /** * The capabilities request contained too many URIs for the carrier network to handle. Retry * with a lower number of contact numbers. The number varies per carrier. * @hide */ @SystemApi // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; /** * The network did not respond to the capabilities request before the request timed out. * @hide */ @SystemApi public static final int ERROR_REQUEST_TIMEOUT = 9; /** * The request failed due to the service having insufficient memory. * @hide */ @SystemApi public static final int ERROR_INSUFFICIENT_MEMORY = 10; /** * The network was lost while trying to complete the request. * @hide */ @SystemApi public static final int ERROR_LOST_NETWORK = 11; /** * The network is temporarily unavailable or busy. Retries should only be done after the retry * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ @SystemApi public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { ERROR_GENERIC_FAILURE, ERROR_NOT_ENABLED, ERROR_NOT_AVAILABLE, ERROR_NOT_REGISTERED, ERROR_NOT_AUTHORIZED, ERROR_FORBIDDEN, ERROR_NOT_FOUND, ERROR_REQUEST_TOO_LARGE, ERROR_REQUEST_TIMEOUT, ERROR_INSUFFICIENT_MEMORY, ERROR_LOST_NETWORK, ERROR_SERVER_UNAVAILABLE }) public @interface ErrorCode {} /** * A capability update has been requested but the reason is unknown. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; /** * A capability update has been requested due to the Entity Tag (ETag) expiring. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; /** * A capability update has been requested due to moving to LTE with VoPS disabled. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; /** * A capability update has been requested due to moving to LTE with VoPS enabled. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; /** * A capability update has been requested due to moving to eHRPD. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; /** * A capability update has been requested due to moving to HSPA+. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; /** * A capability update has been requested due to moving to 3G. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; /** * A capability update has been requested due to moving to 2G. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; /** * A capability update has been requested due to moving to WLAN * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; /** * A capability update has been requested due to moving to IWLAN * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; /** * A capability update has been requested due to moving to 5G NR with VoPS disabled. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; /** * A capability update has been requested due to moving to 5G NR with VoPS enabled. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; /** * A capability update has been requested due to IMS being registered over INTERNET PDN. * @hide */ @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { CAPABILITY_UPDATE_TRIGGER_UNKNOWN, CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN }) public @interface StackPublishTriggerType {} /** * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for * UCE. * @hide */ @SystemApi public static final int PUBLISH_STATE_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. * @hide */ @SystemApi public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not provisioned for voice. * @hide */ @SystemApi public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not RCS or UCE provisioned. * @hide */ @SystemApi public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; /** * The last publish resulted in a "408 Request Timeout" response. * @hide */ @SystemApi public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; /** * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable" * or SIP 423 - "Interval too short". *

* Device shall retry with exponential back-off. * @hide */ @SystemApi public static final int PUBLISH_STATE_OTHER_ERROR = 6; /** * The device is currently trying to publish its capabilities to the network. * @hide */ @SystemApi public static final int PUBLISH_STATE_PUBLISHING = 7; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "PUBLISH_STATE_", value = { PUBLISH_STATE_OK, PUBLISH_STATE_NOT_PUBLISHED, PUBLISH_STATE_VOICE_PROVISION_ERROR, PUBLISH_STATE_RCS_PROVISION_ERROR, PUBLISH_STATE_REQUEST_TIMEOUT, PUBLISH_STATE_OTHER_ERROR, PUBLISH_STATE_PUBLISHING }) public @interface PublishState {} /** * An application can use {@link #addOnPublishStateChangedListener} to register a * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to * the network changes. * @hide */ @SystemApi public interface OnPublishStateChangedListener { /** * Notifies the callback when the publish state has changed. * @param publishState The latest update to the publish state. * * @deprecated Replaced by {@link #onPublishStateChange}, deprecated for * sip information. */ @Deprecated void onPublishStateChange(@PublishState int publishState); /** * Notifies the callback when the publish state has changed or the publish operation is * done. * @param attributes The latest information related to the publish. */ default void onPublishStateChange(@NonNull PublishAttributes attributes) { onPublishStateChange(attributes.getPublishState()); }; } /** * An application can use {@link #addOnPublishStateChangedListener} to register a * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to * the network changes. * @hide */ public static class PublishStateCallbackAdapter { private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { private final OnPublishStateChangedListener mPublishStateChangeListener; private final Executor mExecutor; PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) { mExecutor = executor; mPublishStateChangeListener = listener; } @Override public void onPublishUpdated(@NonNull PublishAttributes attributes) { if (mPublishStateChangeListener == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { mExecutor.execute(() -> mPublishStateChangeListener.onPublishStateChange(attributes)); } finally { restoreCallingIdentity(callingIdentity); } } } private final PublishStateBinder mBinder; public PublishStateCallbackAdapter(@NonNull Executor executor, @NonNull OnPublishStateChangedListener listener) { mBinder = new PublishStateBinder(executor, listener); } /**@hide*/ public final IRcsUcePublishStateCallback getBinder() { return mBinder; } } /** * A callback for the response to a UCE request. The method * {@link CapabilitiesCallback#onCapabilitiesReceived} will be called zero or more times as the * capabilities are fetched from multiple sources, both cached on the device and on the network. *

* This request will take a varying amount of time depending on if the contacts requested are * cached or if it requires a network query. The timeout time of these requests can vary * depending on the network, however in poor cases it could take up to a minute for a request * to timeout. In that time, only a subset of capabilities may have been retrieved. *

* After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has * been called, the reference to this callback will be discarded on the service side. * @see #requestCapabilities(Collection, Executor, CapabilitiesCallback) * @hide */ @SystemApi public interface CapabilitiesCallback { /** * The pending capability request has completed successfully for one or more of the * requested contacts. * This may be called one or more times before the request is fully completed, as * capabilities may need to be fetched from multiple sources both on device and on the * network. Once the capabilities of all the requested contacts have been received, * {@link #onComplete()} will be called. If there was an error during the capability * exchange process, {@link #onError(int, long)} will be called instead. * @param contactCapabilities List of capabilities associated with each contact requested. */ void onCapabilitiesReceived(@NonNull List contactCapabilities); /** * Called when the pending request has completed successfully due to all requested contacts * information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be * called one or more times and will contain the contacts in the request that the device has * received capabilities for. * * @see #onComplete(SipDetails) onComplete(SipDetails) provides more information related to * the underlying SIP transaction used to perform the capabilities exchange. Either this * method or the alternate method should be implemented to determine when the request has * completed successfully. */ default void onComplete() {} /** * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. * * @see #onError(int, long, SipDetails) onError(int, long, SipDetails) provides more * information related to the underlying SIP transaction that resulted in an error. Either * this method or the alternative method should be implemented to determine when the * request has completed with an error. */ default void onError(@ErrorCode int errorCode, long retryIntervalMillis) {} /** * Called when the pending request has completed successfully due to all requested contacts * information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be * called one or more times and will contain the contacts in the request that the device has * received capabilities for. * * This method contains more information about the underlying SIP transaction if it exists. * If this information is not needed, {@link #onComplete()} can be implemented * instead. * * @param details The SIP information related to this request if the device supports * supplying this information. This parameter will be {@code null} if this * information is not available. */ default void onComplete(@Nullable SipDetails details) { onComplete(); }; /** * The pending request has resulted in an error and may need to be retried, depending on the * error code. * * This method contains more information about the underlying SIP transaction if it exists. * If this information is not needed, {@link #onError(int, long)} can be implemented * instead. * @param errorCode The reason for the framework being unable to process the request. * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. * @param details The SIP information related to this request if the device supports * supplying this information. This parameter will be {@code null} if this * information is not available. */ default void onError(@ErrorCode int errorCode, long retryIntervalMillis, @Nullable SipDetails details) { onError(errorCode, retryIntervalMillis); }; } private final Context mContext; private final int mSubId; private final Map mPublishStateCallbacks; /** * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate * this manager class. * @hide */ RcsUceAdapter(Context context, int subId) { mContext = context; mSubId = subId; mPublishStateCallbacks = new HashMap<>(); } /** * Request the RCS capabilities for one or more contacts using RCS User Capability Exchange. *

* This API will first check a local cache for the requested numbers and return the cached * RCS capabilities of each number if the cache exists and is not stale. If the cache for a * number is stale or there is no cached information about the requested number, the device will * then perform a query to the carrier's network to request the RCS capabilities of the * requested numbers. *

* Depending on the number of requests being sent, this API may throttled internally as the * operations are queued to be executed by the carrier's network. *

* Be sure to check the availability of this feature using * {@link ImsRcsManager#isAvailable(int, int)} and ensuring * {@link * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or * {@link * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is * enabled or else this operation will fail with {@link #ERROR_NOT_AVAILABLE} or * {@link #ERROR_NOT_ENABLED}. * * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. * @param c A one-time callback for when the request for capabilities completes or there is an * error processing the request. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull Collection contactNumbers, @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } if (contactNumbers == null) { throw new IllegalArgumentException("Must include non-null contact number list."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "requestCapabilities: IImsRcsController is null"); throw new ImsException("Can not find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { @Override public void onCapabilitiesReceived(List contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } } @Override public void onComplete(@Nullable SipDetails details) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onComplete(details)); } finally { restoreCallingIdentity(callingIdentity); } } @Override public void onError(int errorCode, long retryAfterMilliseconds, @Nullable SipDetails details) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details)); } finally { restoreCallingIdentity(callingIdentity); } } }; try { imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Request the RCS capabilities for a phone number using User Capability Exchange. *

* Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches * the result received from the network for a certain amount of time and uses that cached result * for subsequent requests for RCS capabilities of the same phone number, this API will always * request the RCS capabilities of a contact from the carrier's network. *

* Depending on the number of requests, this API may throttled internally as the operations are * queued to be executed by the carrier's network. *

* Be sure to check the availability of this feature using * {@link ImsRcsManager#isAvailable(int, int)} and ensuring * {@link * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or * {@link * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is * enabled or else this operation will fail with * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * * @param contactNumber The contact of the capabilities is being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. * @param c A one-time callback for when the request for capabilities completes or there is * an error processing the request. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull Uri contactNumber, @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } if (contactNumber == null) { throw new IllegalArgumentException("Must include non-null contact number."); } if (c == null) { throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "requestAvailability: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { @Override public void onCapabilitiesReceived(List contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } } @Override public void onComplete(@Nullable SipDetails details) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onComplete(details)); } finally { restoreCallingIdentity(callingIdentity); } } @Override public void onError(int errorCode, long retryAfterMilliseconds, @Nullable SipDetails details) { final long callingIdentity = Binder.clearCallingIdentity(); try { executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details)); } finally { restoreCallingIdentity(callingIdentity); } } }; try { imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag(), contactNumber, internalCallback); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Gets the last publish result from the UCE service if the device is using an RCS presence * server. * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, * this method will return {@link #PUBLISH_STATE_OK} as well. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @PublishState int getUcePublishState() throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "getUcePublishState: IImsRcsController is null"); throw new ImsException("Can not find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { return imsRcsController.getUcePublishState(mSubId); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. *

* Use {@link android.telephony.SubscriptionManager.OnSubscriptionsChangedListener} to listen * to subscription * changed events and call * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up. *

* The registered {@link OnPublishStateChangedListener} will also receive a callback when it is * registered with the current publish state. * * @param executor The executor the listener callback events should be run on. * @param listener The {@link OnPublishStateChangedListener} to be added. * @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. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor, @NonNull OnPublishStateChangedListener listener) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } if (listener == null) { throw new IllegalArgumentException( "Must include a non-null OnPublishStateChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); try { imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Removes an existing {@link OnPublishStateChangedListener}. *

* 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 listener The callback to be unregistered. * @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. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener( @NonNull OnPublishStateChangedListener listener) throws ImsException { if (listener == null) { throw new IllegalArgumentException( "Must include a non-null OnPublishStateChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } PublishStateCallbackAdapter callback = removePublishStateCallback(listener); if (callback == null) { return; } try { imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder()); } catch (android.os.ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * The setting for whether or not the user has opted in to the automatic refresh of the RCS * capabilities associated with the contacts in the user's contact address book. By default, * this setting is disabled and must be enabled after the user has seen the opt-in dialog shown * by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}. *

* If this feature is enabled, the device will periodically share the phone numbers of all of * the contacts in the user's address book with the carrier to refresh the RCS capabilities * cache associated with those contacts as the local cache becomes stale. *

* This setting will only enable this feature if * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is * also enabled. *

* Note: This setting does not affect whether or not the device publishes its service * capabilities if the subscription supports presence publication. * * @return true if the user has opted in for automatic refresh of the RCS capabilities of their * contacts, false otherwise. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. */ @RequiresPermission(Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null"); throw new ImsException("Can not find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this. return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag()); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Change the user’s setting for whether or not the user has opted in to the automatic * refresh of the RCS capabilities associated with the contacts in the user's contact address * book. By default, this setting is disabled and must be enabled using this method after the * user has seen the opt-in dialog shown by * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}. *

* If an application wishes to request that the user enable this feature, they must launch an * Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, * which will ask the user if they wish to enable this feature. This setting must only be * enabled after the user has opted-in to this feature. *

* This must not affect the * {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or * {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API, * as those APIs are still required for per-contact RCS capability queries of phone numbers * required for operations such as placing a Video Telephony call or starting an RCS chat * session. *

* This setting will only enable this feature if * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is * also enabled. *

* Note: This setting does not affect whether or not the device publishes its service * capabilities if the subscription supports presence publication. * * @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities * of their contacts, or false if they have chosen to opt-out. By default this * setting is disabled. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @throws UnsupportedOperationException If the device does not have * {@link PackageManager#FEATURE_TELEPHONY_IMS}. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean isEnabled) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null"); throw new ImsException("Can not find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { imsRcsController.setUceSettingEnabled(mSubId, isEnabled); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** * Add the {@link OnPublishStateChangedListener} to collection for tracking. * @param executor The executor that will be used when the publish state is changed and the * {@link OnPublishStateChangedListener} is called. * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed. * @return The {@link PublishStateCallbackAdapter} to wrapper the * {@link OnPublishStateChangedListener} */ private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor, @NonNull OnPublishStateChangedListener listener) { PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener); synchronized (mPublishStateCallbacks) { mPublishStateCallbacks.put(listener, adapter); } return adapter; } /** * Remove the existing {@link OnPublishStateChangedListener}. * @param listener The {@link OnPublishStateChangedListener} to remove from the collection. * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the * {@link OnPublishStateChangedListener}. */ private PublishStateCallbackAdapter removePublishStateCallback( @NonNull OnPublishStateChangedListener listener) { synchronized (mPublishStateCallbacks) { return mPublishStateCallbacks.remove(listener); } } private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() .getTelephonyImsServiceRegisterer() .get(); return IImsRcsController.Stub.asInterface(binder); } }