/* * Copyright (c) 2020 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.stub; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.net.Uri; import android.telephony.ims.ImsException; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.SipDetails; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; /** * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform * using the vendor ImsService. *

* See RCC.07 for more details on UCE as well as how UCE should be implemented. * @hide */ @SystemApi public class RcsCapabilityExchangeImplBase { private static final String LOG_TAG = "RcsCapExchangeImplBase"; /** * Service is unknown. */ public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; /** * The command failed with an unknown error. */ public static final int COMMAND_CODE_GENERIC_FAILURE = 1; /** * Invalid parameter(s). */ public static final int COMMAND_CODE_INVALID_PARAM = 2; /** * Fetch error. */ public static final int COMMAND_CODE_FETCH_ERROR = 3; /** * Request timed out. */ public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; /** * Failure due to insufficient memory available. */ public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; /** * Network connection is lost. */ public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; /** * Requested feature/resource is not supported. */ public static final int COMMAND_CODE_NOT_SUPPORTED = 7; /** * Contact or resource is not found. */ public static final int COMMAND_CODE_NOT_FOUND = 8; /** * Service is not available. */ public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; /** * Command resulted in no change in state, ignoring. */ public static final int COMMAND_CODE_NO_CHANGE = 10; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "COMMAND_CODE_", value = { COMMAND_CODE_SERVICE_UNKNOWN, COMMAND_CODE_GENERIC_FAILURE, COMMAND_CODE_INVALID_PARAM, COMMAND_CODE_FETCH_ERROR, COMMAND_CODE_REQUEST_TIMEOUT, COMMAND_CODE_INSUFFICIENT_MEMORY, COMMAND_CODE_LOST_NETWORK_CONNECTION, COMMAND_CODE_NOT_SUPPORTED, COMMAND_CODE_NOT_FOUND, COMMAND_CODE_SERVICE_UNAVAILABLE, COMMAND_CODE_NO_CHANGE }) public @interface CommandCode {} /** * Interface used by the framework to receive the response of the publish request. */ public interface PublishResponseCallback { /** * Notify the framework that the command associated with the * {@link #publishCapabilities(String, PublishResponseCallback)} has failed. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. */ void onCommandError(@CommandCode int code) throws ImsException; /** * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * * If this network response also contains a “Reason” header, then the * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. * * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If there is a reason header * included in the response, that should take precedence over the reason provided in the * status line. If the network provided no reason with the sip code, the string should be * empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. * * @deprecated Replaced sip information with the newly added * {@link #onNetworkResponse(SipDetails)}. */ @Deprecated void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; /** * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)} that also * includes a reason provided in the “reason” header. See RFC3326 for more * information. * * @param sipCode The SIP response code sent from the network. * @param reasonPhrase The optional reason response from the network. If the * network provided no reason with the sip code, the string should be empty. * @param reasonHeaderCause The “cause” parameter of the “reason” header * included in the SIP message. * @param reasonHeaderText The “text” parameter of the “reason” header * included in the SIP message. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. * * @deprecated Replaced sip information with the newly added * {@link #onNetworkResponse(SipDetails)}. */ @Deprecated void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException; /** * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * * @param details The SIP information received in response to a publish operation. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. */ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException { if (TextUtils.isEmpty(details.getReasonHeaderText())) { onNetworkResponse(details.getResponseCode(), details.getResponsePhrase()); } else { onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(), details.getReasonHeaderCause(), details.getReasonHeaderText()); } } } /** * Interface used by the framework to respond to OPTIONS requests. */ public interface OptionsResponseCallback { /** * Notify the framework that the command associated with this callback has failed. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} * has not received the {@link ImsFeature#onFeatureReady()} callback. This may also happen * in rare cases when the Telephony stack has crashed. */ void onCommandError(@CommandCode int code) throws ImsException; /** * Send the response of a SIP OPTIONS capability exchange to the framework. * @param sipCode The SIP response code that was sent by the network in response * to the request sent by {@link #sendOptionsCapabilityRequest}. * @param reason The optional SIP response reason sent by the network. * If none was sent, this should be an empty string. * @param theirCaps the contact's UCE capabilities associated with the * capability request. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not * currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare * cases when the Telephony stack has crashed. */ void onNetworkResponse(int sipCode, @NonNull String reason, @NonNull List theirCaps) throws ImsException; } /** * Interface used by the framework to receive the response of the subscribe request. */ public interface SubscribeResponseCallback { /** * Notify the framework that the command associated with this callback has failed. *

* Must only be called when there was an error generating a SUBSCRIBE request due to an * IMS stack error. This is a terminating event, so no other callback event will be * expected after this callback. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. */ void onCommandError(@CommandCode int code) throws ImsException; /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. *

* If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the * subsequent NOTIFY responses to the subscription. * * If this network response also contains a “Reason” header, then the * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. * * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If the network * provided no reason with the sip code, the string should be empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. * This may also happen in rare cases when the Telephony stack has crashed. * * @deprecated Replaced sip information with the newly added * {@link #onNetworkResponse(SipDetails)}. */ @Deprecated void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also * includes a reason provided in the “reason” header. See RFC3326 for more * information. * * @param sipCode The SIP response code sent from the network, * @param reasonPhrase The optional reason response from the network. If the * network provided no reason with the sip code, the string should be empty. * @param reasonHeaderCause The “cause” parameter of the “reason” header * included in the SIP message. * @param reasonHeaderText The “text” parameter of the “reason” header * included in the SIP message. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. * * @deprecated Replaced sip information with the newly added * {@link #onNetworkResponse(SipDetails)}. */ @Deprecated void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException; /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. *

* If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the * subsequent NOTIFY responses to the subscription. * * @param details The SIP information related to this request. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. * This may also happen in rare cases when the Telephony stack has crashed. */ default void onNetworkResponse(@NonNull SipDetails details) throws ImsException { if (TextUtils.isEmpty(details.getReasonHeaderText())) { onNetworkResponse(details.getResponseCode(), details.getResponsePhrase()); } else { onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(), details.getReasonHeaderCause(), details.getReasonHeaderText()); } }; /** * Notify the framework of the latest XML PIDF documents included in the network response * for the requested contacts' capabilities requested by the Framework using * {@link RcsUceAdapter#requestCapabilities(List, Executor, * RcsUceAdapter.CapabilitiesCallback)}. *

* The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a * "application/pidf+xml" object and start with a root element. For NOTIFY * responses that contain RLMI information and potentially multiple PIDF XMLs, each * PIDF XML should be separated and added as a separate item in the List. This should be * called every time a new NOTIFY event is received with new capability information. * * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed * for. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. */ void onNotifyCapabilitiesUpdate(@NonNull List pidfXmls) throws ImsException; /** * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response * for the ongoing SUBSCRIBE dialog has been terminated. *

* This will be used to notify the framework that a contact URI that the IMS stack has * subscribed to on the Resource List Server has been terminated as well as the reason why. * Usually this means that there will not be any capability information for the contact URI * that they subscribed for. See RFC 4662 for more information. * * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the * list is the contact URI and its terminated reason. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. */ void onResourceTerminated( @NonNull List> uriTerminatedReason) throws ImsException; /** * The subscription associated with a previous * {@link RcsUceAdapter#requestCapabilities(List, Executor, * RcsUceAdapter.CapabilitiesCallback)} * operation has been terminated. This will mostly be due to the network sending a final * NOTIFY response due to the subscription expiring, but this may also happen due to a * network error. * * @param reason The reason for the request being unable to process. * @param retryAfterMilliseconds The time in milliseconds the requesting application should * wait before retrying, if non-zero. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in * rare cases when the Telephony stack has crashed. */ void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } /** * Create a new RcsCapabilityExchangeImplBase instance. */ public RcsCapabilityExchangeImplBase() { } /** * The user capabilities of one or multiple contacts have been requested by the framework. *

* The implementer must follow up this call with an * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. * The response from the network to the SUBSCRIBE request must be sent back to the framework * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. * As NOTIFY requests come in from the network, the requested contact’s capabilities should be * sent back to the framework using * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List}) and * {@link SubscribeResponseCallback#onResourceTerminated(List>)} * should be called with the presence information for the contacts specified. *

* Once the subscription is terminated, * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the * framework to finish listening for NOTIFY responses. * * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the * UCE capabilities for. * @param cb The callback of the subscribe request. */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") public void subscribeForCapabilities(@NonNull Collection uris, @NonNull SubscribeResponseCallback cb) { // Stub - to be implemented by service Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation."); try { cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); } catch (ImsException e) { // Do not do anything, this is a stub implementation. } } /** * The capabilities of this device have been updated and should be published to the network. *

* If this operation succeeds, network response updates should be sent to the framework using * {@link PublishResponseCallback#onNetworkResponse(int, String)}. * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent * to the carrier’s presence server. * @param cb The callback of the publish request */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) { // Stub - to be implemented by service Log.w(LOG_TAG, "publishCapabilities called with no implementation."); try { cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); } catch (ImsException e) { // Do not do anything, this is a stub implementation. } } /** * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism * in order to receive the capabilities of the remote user in response. *

* The implementer must use {@link OptionsResponseCallback} to send the response of * this query from the network back to the framework. * @param contactUri The URI of the remote user that we wish to get the capabilities of. * @param myCapabilities The capabilities of this device to send to the remote user. * @param callback The callback of this request which is sent from the remote user. */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, @NonNull Set myCapabilities, @NonNull OptionsResponseCallback callback) { // Stub - to be implemented by service Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation."); try { callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED); } catch (ImsException e) { // Do not do anything, this is a stub implementation. } } }