diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 28feab27a794b..42d7707c97d93 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -22,7 +22,12 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.telephony.BinderCacheManager; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.ims.aidl.IImsRcsController; + +import com.android.internal.telephony.ITelephony; /** * Provides access to information about Telephony IMS services on the device. @@ -30,8 +35,6 @@ import android.telephony.SubscriptionManager; @SystemService(Context.TELEPHONY_IMS_SERVICE) public class ImsManager { - private Context mContext; - /** *
Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
* network due to the network returning a "forbidden" response. This may be due to a
@@ -87,6 +90,14 @@ public class ImsManager {
public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
"android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ // Cache Telephony Binder interfaces, one cache per process.
+ private static final BinderCacheManager
- * Used for RCS single registration cases, where an IMS application needs to forward SIP
- * traffic through the device's IMS service.
- * @param subscriptionId The ID of the subscription that this SipDelegateManager will use.
+ * Allows an IMS application to forward SIP traffic through the device's IMS service,
+ * which is used for cellular carriers that require the device to share a single IMS
+ * registration for both MMTEL and RCS features.
+ * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will
+ * be bound to.
* @throws IllegalArgumentException if the subscription is invalid.
- * @return a SipDelegateManager instance for the specified subscription ID.
+ * @return a {@link SipDelegateManager} instance for the specified subscription ID.
* @hide
*/
@SystemApi
@@ -144,6 +157,22 @@ public class ImsManager {
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new SipDelegateManager(mContext, subscriptionId);
+ return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+ }
+
+ private static IImsRcsController getIImsRcsControllerInterface() {
+ return IImsRcsController.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyImsServiceRegisterer()
+ .get());
+ }
+
+ private static ITelephony getITelephonyInterface() {
+ return ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
}
}
diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
new file mode 100644
index 0000000000000..beec4a680d78b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Callback interface provided to the SipTransport implementation to notify a remote application of
+ * the following:
+ *
+ * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It
+ * is still expected that the SipDelegateConnection responds to the request.
+ */
+ public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1;
+
+ /**
+ * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP
+ * messages will fail until IMS registration occurs.
+ */
+ public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2;
+
+ /**
+ * This feature tag is being deregistered because the PDN that the IMS registration is on is
+ *changing.
+ * All open SIP dialogs need to be closed before the PDN change can proceed.
+ */
+ public static final int DEREGISTERING_REASON_PDN_CHANGE = 3;
+
+ /**
+ * This feature tag is being deregistered due to a provisioning change. This can be triggered by
+ * many things, such as a provisioning change triggered by the carrier network, a radio access
+ * technology change by the modem causing a different set of feature tags to be provisioned, or
+ * a user triggered hange, such as data being enabled/disabled.
+ *
+ * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed
+ * before the IMS registration modification can proceed.
+ */
+ public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4;
+
+ /**
+ * This feature tag is deregistering because the SipDelegate associated with this feature tag
+ * needs to change its supported feature set.
+ *
+ * All open SIP Dialogs associated with this feature tag must be closed before this operation
+ * can proceed.
+ */
+ public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5;
+
+ /**
+ * This feature tag is deregistering because the SipDelegate is in the process of being
+ * destroyed.
+ *
+ * All open SIP Dialogs associated with this feature tag must be closed before this operation
+ * can proceed.
+ */
+ public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DEREGISTERED_REASON_", value = {
+ DEREGISTERED_REASON_UNKNOWN,
+ DEREGISTERED_REASON_NOT_PROVISIONED,
+ DEREGISTERED_REASON_NOT_REGISTERED
+ })
+ public @interface DeregisteredReason {}
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DEREGISTERING_REASON_", value = {
+ DEREGISTERING_REASON_PDN_CHANGE,
+ DEREGISTERING_REASON_PROVISIONING_CHANGE,
+ DEREGISTERING_REASON_FEATURE_TAGS_CHANGING,
+ DEREGISTERING_REASON_DESTROY_PENDING
+ })
+ public @interface DeregisteringReason {}
+
+ private final ArrayList
+ * Any incoming SIP messages associated with a feature tag included in this list will still be
+ * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the
+ * SipDelegate, but outgoing out-of-dialog SIP messages with a feature tag that is included in
+ * this list will fail.
+ *
+ * The SipDelegate will stay in this state for a limited period of time while it waits for the
+ * RCS application to perform a specific action. More details on the actions that can cause this
+ * state as well as the expected response are included in the reason codes and can be found in
+ * {@link DeregisteringReason}.
+ * @return A Set of feature tags that the SipDelegate has associated with that are included in
+ * the network IMS registration but are in the process of deregistering.
+ */
+ public @NonNull Set
+ * See {@link DeregisteredReason} codes for more information related to the reasons why this may
+ * occur.
+ *
+ * Due to network race conditions, there may still be onditions where an incoming out-of-dialog
+ * SIP message is delivered for a feature tag that is considered deregistered. Due to this
+ * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be
+ * allowed as long as they are in response to a dialog started by a remote party. Any outgoing
+ * out-of-dialog SIP messages associated with feature tags included in this list will fail to be
+ * sent.
+ * @return A list of feature tags that the SipDelegate has associated with that not included in
+ * the network IMS registration.
+ */
+ public @NonNull Set
+ * The remote application will not be able to proceed sending SIP messages until after this
+ * configuration is sent the first time, so this configuration should be sent as soon as the
+ * {@link SipDelegate} has access to these configuration parameters.
+ *
+ * Incoming SIP messages should not be routed to the remote application until AFTER this
+ * configuration change is sent to ensure that the remote application can respond correctly.
+ * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+ * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+ * change event to reduce conditions where the remote application is using a stale IMS
+ * configuration.
+ */
+ void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
+
+ /**
+ * Call to notify the remote application that the {@link SipDelegate} has modified the IMS
+ * registration state of the RCS feature tags that were requested as part of the initial
+ * {@link DelegateRequest}.
+ *
+ * See {@link DelegateRegistrationState} for more information about how IMS Registration state
+ * should be communicated the associated SipDelegateConnection in cases such as
+ * IMS deregistration, handover, PDN change, provisioning changes, etc…
+ *
+ * Note: Even after the status of the feature tags are updated here to deregistered, the
+ * SipDelegate must still be able to handle these messages and call
+ * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the
+ * message was not sent.
+ *
+ * @param registrationState The current network IMS registration state for all feature tags
+ * associated with this SipDelegate.
+ */
+ void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState);
+}
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.aidl b/telephony/java/android/telephony/ims/FeatureTagState.aidl
new file mode 100644
index 0000000000000..bce55742461c7
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable FeatureTagState;
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java
new file mode 100644
index 0000000000000..060be6f2510d2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/FeatureTagState.java
@@ -0,0 +1,131 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a
+ * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability
+ * state as set by the ImsService managing the related IMS registration.
+ *
+ * This class is only used to report more information about a IMS feature tag that is not fully
+ * available at this time.
+ *
+ * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and
+ * {@link DelegateConnectionStateCallback} for more information about how this class is used to
+ * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not
+ * currently available.
+ * @hide
+ */
+public final class FeatureTagState implements Parcelable {
+
+ private final String mFeatureTag;
+ private final int mState;
+
+ /**
+ * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
+ * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
+ * DelegateRegistrationState, List)} and
+ * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this
+ * is used.
+ *
+ * @param featureTag The IMS feature tag that is deregistered, in the process of
+ * deregistering, or denied.
+ * @param state The {@link DelegateRegistrationState.DeregisteredReason},
+ * {@link DelegateRegistrationState.DeregisteringReason}, or
+ * {@link SipDelegateManager.DeniedReason} associated with this feature tag.
+ */
+ public FeatureTagState(@NonNull String featureTag, int state) {
+ mFeatureTag = featureTag;
+ mState = state;
+ }
+
+ /**
+ * Used for constructing instances during un-parcelling.
+ */
+ private FeatureTagState(Parcel source) {
+ mFeatureTag = source.readString();
+ mState = source.readInt();
+ }
+
+ /**
+ * @return The IMS feature tag string that is in the process of deregistering,
+ * deregistered, or denied.
+ */
+ public @NonNull String getFeatureTag() {
+ return mFeatureTag;
+ }
+
+ /**
+ * @return The reason for why the feature tag is currently in the process of deregistering,
+ * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and
+ * {@link DelegateConnectionStateCallback} for more information.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mFeatureTag);
+ dest.writeInt(mState);
+ }
+
+ public static final Creator
+ * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying
+ * the associated {@link DelegateMessageCallback} when the message was either sent successfully or
+ * failed to be sent.
+ *
+ * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming
+ * SIP messages have been received successfully via
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error
+ * receiving the message using {@link #notifyMessageReceived(String)} and
+ * {@link #notifyMessageReceiveError(String, int)}.
+ *
+ * @see SipDelegateManager#createSipDelegate
+ * @hide
+ */
+public interface SipDelegateConnection {
+
+ /**
+ * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The
+ * {@link SipMessage} will either be acknowledged with
+ * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message
+ * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error
+ * sending the message.
+ * @param sipMessage The SipMessage to be sent.
+ * @param configVersion The SipDelegateImsConfiguration version used to construct the
+ * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
+ * information on this parameter and why it is used.
+ */
+ void sendMessage(@NonNull SipMessage sipMessage, int configVersion);
+
+ /**
+ * Notify the {@link SipDelegate} that a SIP message received from
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully
+ * and is being processed.
+ * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+ * branch parameter.
+ */
+ void notifyMessageReceived(@NonNull String viaTransactionId);
+
+ /**
+ * Notify the SIP delegate that the SIP message has been received from
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error
+ * processing it.
+ * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+ * branch parameter.
+ * @param reason The reason why the error occurred.
+ */
+ void notifyMessageReceiveError(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
new file mode 100644
index 0000000000000..44ae1b1300461
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable SipDelegateImsConfiguration;
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
new file mode 100644
index 0000000000000..8abd0ee948655
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -0,0 +1,499 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ *
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration}
+ * instance.
+ *
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new {@link SipDelegateImsConfiguration} instance should not be an incremental
+ * update.
+ * @hide
+ */
+public class SipDelegateImsConfiguration implements Parcelable {
+
+ /**
+ * IPV4 Address type.
+ *
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+ */
+ public static final String IPTYPE_IPV4 = "IPV4";
+
+ /**
+ * IPV6 Address type.
+ *
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+ */
+ public static final String IPTYPE_IPV6 = "IPV6";
+
+ /**
+ * The SIP transport uses UDP.
+ *
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+ */
+ public static final String SIP_TRANSPORT_UDP = "UDP";
+
+ /**
+ * The SIP transport uses TCP.
+ *
+ * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+ */
+ public static final String SIP_TRANSPORT_TCP = "TCP";
+
+ /**
+ * Flag specifying if SIP compact form is enabled
+ */
+ public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL =
+ "sip_config_is_compact_form_enabled_bool";
+
+ /**
+ * Flag specifying if SIP keepalives are enabled
+ */
+ public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL =
+ "sip_config_is_keepalive_enabled_bool";
+
+ /**
+ * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp
+ * payload size, then TCP must be used
+ */
+ public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT =
+ "sip_config_udp_max_payload_size_int";
+
+ /**
+ * Transport protocol used for SIP signaling.
+ * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP }
+ */
+ public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING =
+ "sip_config_protocol_type_string";
+
+ /**
+ * IMS public user identifier string
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING =
+ "sip_config_ue_public_user_id_string";
+
+ /**
+ * IMS private user identifier string
+ */
+ public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING =
+ "sip_config_ue_private_user_id_string";
+
+ /**
+ * IMS home domain string
+ */
+ public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+
+ /**
+ * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the
+ * Contact header with a value of the device IMEI in the form "urn:gsma:imei:
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING =
+ "sip_config_ue_public_ipaddress_with_nat_string";
+
+ /**
+ * UE's public SIP port when UE is behind a NAT.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT =
+ "sip_config_ue_public_port_with_nat_int";
+
+ /**
+ * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808
+ */
+ public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL =
+ "sip_config_is_gruu_enabled_bool";
+
+ /**
+ * UE's Globally routable user-agent uri if this feature is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING =
+ "sip_config_ue_public_gruu_string";
+
+ /**
+ * Flag specifying if SIP over IPSec is enabled.
+ */
+ public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL =
+ "sip_config_is_ipsec_enabled_bool";
+ /**
+ * UE's SIP port used to send traffic when IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT =
+ "sip_config_ue_ipsec_client_port_int";
+
+ /**
+ * UE's SIP port used to receive traffic when IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT =
+ "sip_config_ue_ipsec_server_port_int";
+
+ /**
+ * UE's SIP port used for the previous IPsec security association if IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT =
+ "sip_config_ue_ipsec_old_client_port_int";
+
+ /**
+ * Port number used by the SIP server to send SIP traffic when IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT =
+ "sip_config_server_ipsec_client_port_int";
+
+ /**
+ * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT =
+ "sip_config_server_ipsec_server_port_int";
+
+ /**
+ * Port number used by the SIP server to send SIP traffic on the previous IPSec security
+ * association when IPSec is enabled.
+ *
+ * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+ */
+ public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT =
+ "sip_config_server_ipsec_old_client_port_int";
+ /**
+ * SIP Authentication header string
+ */
+ public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING =
+ "sip_config_auhentication_header_string";
+
+ /**
+ * SIP Authentication nonce string
+ */
+ public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING =
+ "sip_config_authentication_nonce_string";
+
+ /**
+ * SIP service route header string
+ */
+ public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING =
+ "sip_config_service_route_header_string";
+
+ /**
+ * SIP security verify header string
+ */
+ public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING =
+ "sip_config_security_verify_header_string";
+
+ /**
+ * SIP Path header string
+ */
+ public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING =
+ "sip_config_path_header_string";
+
+ /**
+ * SIP User part string in contact header
+ */
+ public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
+ "sip_config_uri_user_part_string";
+
+ /**
+ * SIP P-access-network-info header string
+ */
+ public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING =
+ "sip_config_p_access_network_info_header_string";
+
+ /**
+ * SIP P-last-access-network-info header string
+ */
+ public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
+ "sip_config_p_last_access_network_info_header_string";
+
+ /**
+ * SIP P-associated-uri header string
+ */
+ public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
+ "sip_config_p_associated_uri_header_string";
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = {
+ KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING,
+ KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING,
+ KEY_SIP_CONFIG_HOME_DOMAIN_STRING,
+ KEY_SIP_CONFIG_IMEI_STRING,
+ KEY_SIP_CONFIG_IPTYPE_STRING,
+ KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING,
+ KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING,
+ KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING,
+ KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING,
+ KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING,
+ KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
+ KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
+ KEY_SIP_CONFIG_PATH_HEADER_STRING,
+ KEY_SIP_CONFIG_URI_USER_PART_STRING,
+ KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StringConfigKey {}
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = {
+ KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+ KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT,
+ KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT,
+ KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT,
+ KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IntConfigKey {}
+
+ /**@hide*/
+ @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = {
+ KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL,
+ KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BooleanConfigKey {}
+
+ /**
+ * Builder class to be used when constructing a new SipDelegateImsConfiguration.
+ */
+ public static class Builder {
+ private final long mVersion;
+ private final PersistableBundle mBundle;
+
+ /**
+ * Creates an empty implementation of SipDelegateImsConfiguration.
+ * @param version The version associated with the SipDelegateImsConfiguration being built.
+ * See {@link #getVersion} for more information.
+ */
+ public Builder(int version) {
+ mVersion = version;
+ mBundle = new PersistableBundle();
+ }
+ /**
+ * Clones an existing implementation of SipDelegateImsConfiguration to handle situations
+ * where only a small number of parameters have changed from the previous configuration.
+ *
+ * Automatically increments the version of this configuration by 1. See {@link #getVersion}
+ * for more information.
+ */
+ public Builder(@NonNull SipDelegateImsConfiguration config) {
+ mVersion = config.getVersion() + 1;
+ mBundle = config.copyBundle();
+ }
+ /**
+ * Put a string value into this configuration bundle for the given key.
+ */
+ public Builder putString(@StringConfigKey String key, String value) {
+ mBundle.putString(key, value);
+ return this;
+ }
+
+ /**
+ * Replace the existing default value with a new value for a given key.
+ */
+ public Builder putInt(@IntConfigKey String key, int value) {
+ mBundle.putInt(key, value);
+ return this;
+ }
+
+ /**
+ * Replace the existing default value with a new value for a given key.
+ */
+ public Builder putBoolean(@BooleanConfigKey String key, boolean value) {
+ mBundle.putBoolean(key, value);
+ return this;
+ }
+
+ /**
+ * @return a new SipDelegateImsConfiguration from this Builder.
+ */
+ public SipDelegateImsConfiguration build() {
+ return new SipDelegateImsConfiguration(mVersion, mBundle);
+ }
+ }
+
+ private final long mVersion;
+ private final PersistableBundle mBundle;
+
+ private SipDelegateImsConfiguration(long version, PersistableBundle bundle) {
+ mVersion = version;
+ mBundle = bundle;
+ }
+
+ private SipDelegateImsConfiguration(Parcel source) {
+ mVersion = source.readLong();
+ mBundle = source.readPersistableBundle();
+ }
+
+ /**
+ * @return the string value associated with a given key or {@code null} if it doesn't exist.
+ */
+ public @StringConfigKey String getString(String key) {
+ return mBundle.getString(key);
+ }
+
+ /**
+ * @return the Integer value associated with a given key or {@code null} if the value doesn't
+ * exist.
+ */
+ public @IntConfigKey Integer getInt(String key) {
+ if (!mBundle.containsKey(key)) {
+ return null;
+ }
+ return mBundle.getInt(key);
+ }
+
+ /**
+ * @return the Integer value associated with a given key or {@code null} if the value doesn't
+ * exist.
+ */
+ public @BooleanConfigKey Boolean getBoolen(String key) {
+ if (!mBundle.containsKey(key)) {
+ return null;
+ }
+ return mBundle.getBoolean(key);
+ }
+
+ /**
+ * @return a shallow copy of the full configuration.
+ */
+ public PersistableBundle copyBundle() {
+ return new PersistableBundle(mBundle);
+ }
+
+ /**
+ * An integer representing the version number of this SipDelegateImsConfiguration.
+ * {@link SipMessage}s that are created using this configuration will also have a this
+ * version number associated with them, which will allow the IMS service to validate that the
+ * {@link SipMessage} was using the latest configuration during creation and not a stale
+ * configuration due to race conditions between the configuration being updated and the RCS
+ * application not receiving the updated configuration before generating a new message.
+ *
+ * @return the version number associated with this {@link SipDelegateImsConfiguration}.
+ */
+ public long getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mVersion);
+ dest.writePersistableBundle(mBundle);
+ }
+
+ public static final Creator
+ * The caller should retry a message that failed with this response.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
+
+ /**
+ * The remote service associated with this connection has died and the message was not
+ * properly sent/received.
+ *
+ * This is considered a permanent error and the system will automatically begin the teardown and
+ * destruction of the SipDelegate. No further messages should be sent on this transport.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
+
+ /**
+ * The message has not been sent/received because the delegate is in the process of closing and
+ * has become unavailable. No further messages should be sent/received on this delegate.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
+
+ /**
+ * The SIP message has an invalid start line and the message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
+
+ /**
+ * One or more of the header fields in the header section of the outgoing SIP message is invalid
+ * and the SIP message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
+
+ /**
+ * The body content of the SIP message is invalid and the message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
+
+ /**
+ * The feature tag associated with the outgoing message does not match any known feature tags
+ * and this message can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
+
+ /**
+ * The feature tag associated with the outgoing message is not enabled for the associated
+ * SipDelegateConnection and can not be sent.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
+
+ /**
+ * The link to the network has been lost and the outgoing message has failed to send.
+ *
+ * This message should be retried when connectivity to the network is re-established. See
+ * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
+
+ /**
+ * The outgoing SIP message has not been sent due to the SipDelegate not being registered for
+ * IMS at this time.
+ *
+ * This is considered a temporary failure, the message should not be retried until an IMS
+ * registration change callback is received via
+ * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
+
+ /**
+ * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration}
+ * version associated with the outgoing {@link SipMessage} is now stale and has failed
+ * validation checks.
+ *
+ * The @link SipMessage} should be recreated using the newest
+ * {@link SipDelegateImsConfiguration} and sent again.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
+
+ /**
+ * The outgoing SIP message has not been sent because the internal state of the associated
+ * {@link SipDelegate} is changing and has temporarily brought the transport down.
+ *
+ * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
+ * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
+ * no longer reported.
+ * @hide
+ */
+ public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = {
+ MESSAGE_FAILURE_REASON_UNKNOWN,
+ MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+ MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+ MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+ MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+ MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+ MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+ MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+ MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+ MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+ MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+ MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION
+ })
+ public @interface MessageFailureReason {}
+
+
+ /**
+ * Access to use this feature tag has been denied for an unknown reason.
+ * @hide
+ */
+ public static final int DENIED_REASON_UNKNOWN = 0;
+
+ /**
+ * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
+ * another SipDelegateConnection and can not be associated with this delegate. The feature tag
+ * will stay in this state until the feature tag is release by the other application.
+ * @hide
+ */
+ public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
+
+ /**
+ * Access to use this feature tag has been denied because this application does not have the
+ * permissions required to access this feature tag.
+ * @hide
+ */
+ public static final int DENIED_REASON_NOT_ALLOWED = 2;
+
+ /**
+ * Access to use this feature tag has been denied because single registration is not allowed by
+ * the carrier at this time. The application should fall back to dual registration if
+ * applicable.
+ * @hide
+ */
+ public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
+
+ /**
+ * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
+ * denied.
+ * @hide
+ */
+ public static final int DENIED_REASON_INVALID = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "DENIED_REASON_", value = {
+ DENIED_REASON_UNKNOWN,
+ DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+ DENIED_REASON_NOT_ALLOWED,
+ DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED,
+ DENIED_REASON_INVALID
+ })
+ public @interface DeniedReason {}
+
+ /**
+ * The SipDelegate has closed due to an unknown reason.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
+
+ /**
+ * The SipDelegate has closed because the IMS service has died unexpectedly.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
+
+ /**
+ * The SipDelegate has closed because the IMS application has requested that the connection be
+ * destroyed.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
+
+ /**
+ * The SipDelegate has closed because the IMS service does not support the creation of
+ * SipDelegates.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3;
+
+ /**
+ * The SipDelegate has been closed due to the user disabling RCS.
+ * @hide
+ */
+ public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = {
+ SIP_DELEGATE_DESTROY_REASON_UNKNOWN,
+ SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD,
+ SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+ SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED,
+ SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS
+ })
+ public @interface SipDelegateDestroyReason {}
+
private final Context mContext;
private final int mSubId;
+ private final BinderCacheManager
+ * This API requires that the caller is running as part of a long-running process and will
+ * always be available to handle incoming messages. One mechanism that can be used for this is
+ * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps
+ * a persistent binding to when the app is the default SMS application.
+ * @param request The parameters that are associated with the SipDelegate creation request that
+ * will be used to create the SipDelegate connection.
+ * @param executor The executor that will be used to call the callbacks associated with this
+ * SipDelegate.
+ * @param dc The callback that will be used to notify the listener of the creation/destruction
+ * of the remote SipDelegate as well as changes to the state of the remote SipDelegate
+ * connection.
+ * @param mc The callback that will be used to notify the listener of new incoming SIP messages
+ * as well as the status of messages that were sent by the associated
+ * SipDelegateConnection.
+ * @throws ImsException Thrown if there was a problem communicating with the ImsService
+ * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
+ @NonNull DelegateConnectionStateCallback dc,
+ @NonNull DelegateConnectionMessageCallback mc) throws ImsException {
+ if (request == null || executor == null || dc == null || mc == null) {
+ throw new IllegalArgumentException("Invalid arguments passed into createSipDelegate");
+ }
+ try {
+ SipDelegateConnectionAidlWrapper wrapper =
+ new SipDelegateConnectionAidlWrapper(executor, dc, mc);
+ IImsRcsController controller = mBinderCache.listenOnBinder(wrapper,
+ wrapper::binderDied);
+ if (controller == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(),
+ wrapper.getMessageCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(),
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Destroy a previously created {@link SipDelegateConnection} that was created using
+ * {@link #createSipDelegate}.
+ *
+ * This will also clean up all related callbacks in the associated ImsService.
+ * @param delegateConnection The SipDelegateConnection to destroy.
+ * @param reason The reason for why this SipDelegateConnection was destroyed.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
+ @SipDelegateDestroyReason int reason) {
+
+ if (delegateConnection == null) {
+ throw new IllegalArgumentException("invalid argument passed into destroySipDelegate");
+ }
+ if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) {
+ SipDelegateConnectionAidlWrapper w =
+ (SipDelegateConnectionAidlWrapper) delegateConnection;
+ try {
+ IImsRcsController c = mBinderCache.removeRunnable(w);
+ c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason);
+ } catch (RemoteException e) {
+ // Connection to telephony died, but this will signal destruction of SipDelegate
+ // eventually anyway, so return normally.
+ try {
+ w.getStateCallbackBinder().onDestroyed(
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ } catch (RemoteException ignore) {
+ // Local to process.
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+ + " into this method");
+ }
}
}
diff --git a/telephony/java/android/telephony/ims/SipMessage.aidl b/telephony/java/android/telephony/ims/SipMessage.aidl
new file mode 100644
index 0000000000000..5140f8ac357fb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable SipMessage;
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
new file mode 100644
index 0000000000000..c3b1be2d7fc84
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -0,0 +1,155 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
+ * messages are structured and used.
+ *
+ * The SIP message is represented in a partially encoded form in order to allow for easier
+ * verification and should not be used as a generic SIP message container.
+ * @hide
+ */
+public final class SipMessage implements Parcelable {
+ // Should not be set to true for production!
+ private static final boolean IS_DEBUGGING = Build.IS_ENG;
+
+ private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS",
+ "BYE", "CANCEL", "REGISTER"};
+
+ private final String mStartLine;
+ private final String mHeaderSection;
+ private final byte[] mContent;
+
+ /**
+ * Represents a partially encoded SIP message.
+ *
+ * @param startLine The start line of the message, containing either the request-line or
+ * status-line.
+ * @param headerSection A String containing the full unencoded SIP message header.
+ * @param content UTF-8 encoded SIP message body.
+ */
+ public SipMessage(@NonNull String startLine, @NonNull String headerSection,
+ @NonNull byte[] content) {
+ if (startLine == null || headerSection == null || content == null) {
+ throw new IllegalArgumentException("One or more null parameters entered");
+ }
+ mStartLine = startLine;
+ mHeaderSection = headerSection;
+ mContent = content;
+ }
+
+ /**
+ * Private constructor used only for unparcelling.
+ */
+ private SipMessage(Parcel source) {
+ mStartLine = source.readString();
+ mHeaderSection = source.readString();
+ mContent = new byte[source.readInt()];
+ source.readByteArray(mContent);
+ }
+ /**
+ * @return The start line of the SIP message, which contains either the request-line or
+ * status-line.
+ */
+ public @NonNull String getStartLine() {
+ return mStartLine;
+ }
+
+ /**
+ * @return The full, unencoded header section of the SIP message.
+ */
+ public @NonNull String getHeaderSection() {
+ return mHeaderSection;
+ }
+
+ /**
+ * @return only the UTF-8 encoded SIP message body.
+ */
+ public @NonNull byte[] getContent() {
+ return mContent;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mStartLine);
+ dest.writeString(mHeaderSection);
+ dest.writeInt(mContent.length);
+ dest.writeByteArray(mContent);
+ }
+
+ public static final Creator
+ * When the remote {@link SipDelegate} is created, this instance tracks the
+ * {@link ISipDelegate} associated with it and implements the
+ * {@link SipDelegateConnection} sent back to the local callback.
+ * @hide
+ */
+public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection,
+ IBinder.DeathRecipient {
+ private static final String LOG_TAG = "SipDelegateCAW";
+
+ private final ISipDelegateConnectionStateCallback.Stub mStateBinder =
+ new ISipDelegateConnectionStateCallback.Stub() {
+ @Override
+ public void onCreated(ISipDelegate c) {
+ associateSipDelegate(c);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState,
+ List
+ * After {@link SipDelegateManager#createSipDelegate} is used to request a new
+ * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the
+ * {@link SipDelegateConnection} instance that must be used to communicate with the remote
+ * {@link SipDelegate}.
+ *
+ * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current
+ * status of the feature tags that have been requested. The application may receive multiple
+ * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated
+ * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with
+ * those feature tags will be handled.
+ *
+ * In order to start sending SIP messages, the SIP configuration parameters will need to be
+ * received, so the messaging application should make no assumptions about these parameters and wait
+ * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is
+ * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
+ * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
+ * feature tags were denied, no IMS configuration will be sent.
+ *
+ * The {@link SipDelegateConnection} will stay associated with this RCS application until either the
+ * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the
+ * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)} will be called.
+ * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in
+ * rare cases, such as if telephony itself or IMS service dies unexpectedly. See
+ * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the
+ * cases that will trigger the {@link SipDelegateConnection} to be destroyed.
+ *
+ * @hide
+ */
+public interface DelegateConnectionStateCallback {
+
+ /**
+ * A {@link SipDelegateConnection} has been successfully created for the
+ * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}.
+ */
+ void onCreated(@NonNull SipDelegateConnection c);
+
+ /**
+ * The status of the RCS feature tags that were requested as part of the initial
+ * {@link DelegateRequest}.
+ *
+ * There are four states that each RCS feature tag can be in: registered, deregistering,
+ * deregistered, and denied.
+ *
+ * When a feature tag is considered registered, SIP messages associated with that feature tag
+ * may be sent and received freely.
+ *
+ * When a feature tag is deregistering, the network IMS registration still contains the feature
+ * tag, however the IMS service and associated {@link SipDelegate} is in the progress of
+ * modifying the IMS registration to remove this feature tag and requires the application to
+ * perform an action before the IMS registration can change. The specific action required for
+ * the SipDelegate to continue modifying the IMS registration can be found in the definition of
+ * each {@link DelegateRegistrationState.DeregisteringReason}.
+ *
+ * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that
+ * feature tag will be rejected, however due to network race conditions, the RCS application
+ * should still be able to handle new out-of-dialog SIP requests from the network. This may not
+ * be possible, however, if the IMS registration itself was lost. See the
+ * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP
+ * messages are handled in each of these cases.
+ *
+ * If a feature tag is denied, no incoming messages will be routed to the associated
+ * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this
+ * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason}
+ * reasons for more information about the conditions when this will happen.
+ *
+ * The set of feature tags contained in the registered, deregistering, deregistered, and denied
+ * lists will always equal the set of feature tags requested in the initial
+ * {@link DelegateRequest}.
+ *
+ * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa
+ * may happen quite often, however transitions to/from denied are rare and only occur if the
+ * user has changed the role of your application to add/remove support for one or more requested
+ * feature tags or carrier provisioning has enabled or disabled single registration entirely.
+ * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of
+ * these cases as well as what may cause them to change.
+ *
+ * @param registrationState The new IMS registration state of each of the feature tags
+ * associated with the {@link SipDelegate}.
+ * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature
+ * tag associated with this {@link SipDelegateConnection} that has no access to
+ * send/receive SIP messages as well as a reason for why the feature tag is denied. For more
+ * information on the reason why the feature tag was denied access, see the
+ * {@link SipDelegateManager.DeniedReason} reasons.
+ */
+ void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState,
+ @NonNull Set
+ * There should never be assumptions made about the configuration of the underling IMS stack and
+ * the IMS application should wait for this indication before sending out any outgoing SIP
+ * messages.
+ *
+ * Configuration may change due to IMS registration changes as well as
+ * other optional events on the carrier network. If IMS stack is already registered at the time
+ * of callback registration, then this method shall be invoked with the current configuration.
+ * Otherwise, there may be a delay in this method being called if initial IMS registration has
+ * not compleed yet.
+ *
+ * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ */
+ void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig);
+
+ /**
+ * The previously created {@link SipDelegateConnection} instance delivered via
+ * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer
+ * be used for any SIP message handling.
+ *
+ * @param reason The reason for the failure.
+ */
+ void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 12abdd1d7e11a..a6f5c45445f55 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,6 +17,8 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.RemoteException;
@@ -125,6 +127,57 @@ public class ImsRegistrationImplBase {
mCallbacks.unregister(c);
}
+ /**
+ * Called by the framework to request that the ImsService perform the network registration
+ * of all SIP delegates associated with this ImsService.
+ *
+ * If the SIP delegate feature tag configuration has changed, then this method will be
+ * called in order to let the ImsService know that it can pick up these changes in the IMS
+ * registration.
+ * @hide
+ */
+ public void updateSipDelegateRegistration() {
+ // Stub implementation, ImsService should implement this
+ }
+
+
+ /**
+ * Called by the framework to request that the ImsService perform the network deregistration of
+ * all SIP delegates associated with this ImsService.
+ *
+ * This is typically called in situations where the user has changed the configuration of the
+ * device (for example, the default messaging application) and the framework is reconfiguring
+ * the tags associated with each IMS application.
+ *
+ * This should not affect the registration of features managed by the ImsService itself, such as
+ * feature tags related to MMTEL registration.
+ * @hide
+ */
+ public void triggerSipDelegateDeregistration() {
+ // Stub implementation, ImsService should implement this
+ }
+
+ /**
+ * Called by the framework to notify the ImsService that a SIP delegate connection has received
+ * a SIP message containing a permanent failure response (such as a 403) or an indication that a
+ * SIP response timer has timed out in response to an outgoing SIP message. This method will be
+ * called when this condition occurs to trigger the ImsService to tear down the full IMS
+ * registration and re-register again.
+ *
+ * @param sipCode The SIP error code that represents a permanent failure that was received in
+ * response to a request generated by the IMS application. See RFC3261 7.2 for the general
+ * classes of responses available here, however the codes that generate this condition may
+ * be carrier specific.
+ * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
+ * reason associated with the error.
+ * @hide
+ */
+ public void triggerNetworkReregistration(@IntRange(from = 100, to = 699) int sipCode,
+ @Nullable String sipReason) {
+ // Stub implementation, ImsService should implement this
+ }
+
+
/**
* Notify the framework that the device is connected to the IMS network.
*
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
new file mode 100644
index 0000000000000..3ec97095eb008
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -0,0 +1,91 @@
+/*
+ * 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.NonNull;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged
+ * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of
+ * incoming SIP messages delivered to the application over the existing IMS registration, allowing
+ * for a single IMS registration for multiple IMS applications.
+ *
+ * Once the SIP delegate is created for that application,
+ * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that
+ * the application is finished setting up SipDelegates and the existing IMS registration may be
+ * modified to include the features managed by these SipDelegates.
+ *
+ * This SipDelegate will need to notify the remote application of the registration of these features
+ * as well as the associated {@link SipDelegateImsConfiguration} before the application can start
+ * sending/receiving SIP messages via the transport. See
+ * {@link android.telephony.ims.DelegateStateCallback} for more information.
+ * @hide
+ */
+public interface SipDelegate {
+
+ /**
+ * The framework calls this method when a remote RCS application wishes to send a new outgoing
+ * SIP message.
+ *
+ * Once sent, this SIP delegate should notify the remote application of the success or
+ * failure using {@link DelegateMessageCallback#onMessageSent(String)} or
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
+ * @param message The SIP message to be sent over the operator’s network.
+ * @param configVersion The SipDelegateImsConfiguration version used to construct the
+ * SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the
+ * version specified here does not match the most recently constructed
+ * {@link SipDelegateImsConfiguration}, this message should fail validation checks and
+ * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
+ */
+ void sendMessage(@NonNull SipMessage message, int configVersion);
+
+ /**
+ * The framework is requesting that routing resources associated with the SIP dialog using the
+ * provided Call-ID to be cleaned up.
+ *
+ * Typically a SIP Dialog close event will be signalled by that dialog receiving a BYE or 200 OK
+ * message, however, in some cases, the framework will request that the ImsService close the
+ * dialog due to the open dialog holding up an event such as applying a provisioning change or
+ * handing over to another transport type.
+ * @param callId The call-ID header value associated with the ongoing SIP Dialog that the
+ * framework is requesting be closed.
+ */
+ void closeDialog(@NonNull String callId);
+
+ /**
+ * The remote application has received the SIP message and is processing it.
+ * @param viaTransactionId The Transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ */
+ void notifyMessageReceived(@NonNull String viaTransactionId);
+
+ /**
+ * The remote application has either not received the SIP message or there was an error
+ * processing it.
+ * @param viaTransactionId The Transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ * @param reason The reason why the message was not correctly received.
+ */
+ void notifyMessageReceiveError(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index b2b2914b3739a..b48f6317e4135 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -18,27 +18,75 @@ package android.telephony.ims.stub;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.IBinder;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.aidl.SipDelegateAidlWrapper;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
- * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
- * IMS applications in order to support IMS single registration.
+ * The ImsService implements this class to manage the creation and destruction of
+ * {@link SipDelegate}s.
+ *
+ * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS
+ * applications as a delegate to the associated carrier's IMS Network in order to support using a
+ * single IMS registration for all MMTEL and RCS signalling traffic.
* @hide
*/
@SystemApi
public class SipTransportImplBase {
+ private static final String LOG_TAG = "SipTransportIB";
+
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mBinderExecutor.execute(() -> binderDiedInternal());
+ }
+ };
+
+ private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() {
+ @Override
+ public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc,
+ ISipDelegateMessageCallback mc) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void destroySipDelegate(ISipDelegate delegate, int reason) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
private final Executor mBinderExecutor;
- private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
-
- };
+ private final ArrayList
+ * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with
+ * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
+ *
+ * This method will be called on the Executor specified in
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+ *
+ * @param request A SIP delegate request containing the parameters that the remote RCS
+ * application wishes to use.
+ * @param dc A callback back to the remote application to be used to communicate state callbacks
+ * for the SipDelegate.
+ * @param mc A callback back to the remote application to be used to send SIP messages to the
+ * remote application and acknowledge the sending of outgoing SIP messages.
+ * @hide
+ */
+ public void createSipDelegate(@NonNull DelegateRequest request,
+ @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
+ throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+ }
+
+ /**
+ * Destroys the SipDelegate associated with a remote IMS application.
+ *
+ * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be
+ * called to notify listeners of its destruction to release associated resources.
+ *
+ * This method will be called on the Executor specified in
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+ * @param delegate The delegate to be destroyed.
+ * @param reason The reason the remote connection to this {@link SipDelegate} is being
+ * destroyed.
+ * @hide
+ */
+ public void destroySipDelegate(@NonNull SipDelegate delegate,
+ @SipDelegateManager.SipDelegateDestroyReason int reason) {
+ throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+ }
+
+ private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb,
+ ISipDelegateMessageCallback mc) {
+ SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
+ mDelegates.add(wrapper);
+ createSipDelegate(r, wrapper, wrapper);
+ }
+
+ private void destroySipDelegateInternal(ISipDelegate d, int reason) {
+ SipDelegateAidlWrapper result = null;
+ for (SipDelegateAidlWrapper w : mDelegates) {
+ if (Objects.equals(d, w.getDelegateBinder())) {
+ result = w;
+ break;
+ }
+ }
+
+ if (result != null) {
+ mDelegates.remove(result);
+ destroySipDelegate(result.getDelegate(), reason);
+ } else {
+ Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to "
+ + d);
+ }
+ }
+
+ private void binderDiedInternal() {
+ for (SipDelegateAidlWrapper w : mDelegates) {
+ destroySipDelegate(w.getDelegate(),
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ }
+ mDelegates.clear();
+ }
+
/**
* @return The IInterface used by the framework.
* @hide
+ *
+ * @hide
+ */
+public interface DelegateMessageCallback {
+
+ /**
+ * Send a new incoming SIP message to the remote application for processing.
+ */
+ void onMessageReceived(@NonNull SipMessage message);
+
+ /**
+ * Notify the remote application that a previous request to send a SIP message using
+ * {@link SipDelegate#sendMessage} has succeeded.
+ *
+ * @param viaTransactionId The transaction ID found in the via header field of the
+ * previously sent {@link SipMessage}.
+ */
+ void onMessageSent(@NonNull String viaTransactionId);
+
+ /**
+ * Notify the remote application that a previous request to send a SIP message using
+ * {@link SipDelegate#sendMessage} has failed.
+ *
+ * @param viaTransactionId The Transaction ID found in the via header field of the previously
+ * sent {@link SipMessage}.
+ * @param reason The reason for the failure.
+ */
+ void onMessageSendFailure(@NonNull String viaTransactionId,
+ @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
new file mode 100644
index 0000000000000..756ea920d06a4
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable DelegateRegistrationState;
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
new file mode 100644
index 0000000000000..4facfa77de212
--- /dev/null
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -0,0 +1,327 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the
+ * ImsService.
+ * @hide
+ */
+public final class DelegateRegistrationState implements Parcelable {
+
+ /**
+ * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP
+ * messages associated with feature tags that are not registered will fail.
+ */
+ public static final int DEREGISTERED_REASON_UNKNOWN = 0;
+
+ /**
+ * This feature tag has been deregistered because it is not provisioned to be used on this radio
+ * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags
+ * that are not registered will fail.
+ *
+ *
+ * @hide
+ */
+public interface DelegateStateCallback {
+
+ /**
+ * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
+ * called by the framework to notify the framework and remote application that the
+ * {@link SipDelegate} has been successfully created.
+ *
+ * @param delegate The SipDelegate created to service the DelegateRequest.
+ * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags
+ * associated with this {@link SipDelegate} that have no access to send/receive SIP messages
+ * as well as a reason for why the feature tag is denied. For more information on the reason
+ * why the feature tag was denied access, see the
+ * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
+ * to this {@link SipDelegate} not supporting a feature or this ImsService already
+ * implementing this feature elsewhere. If all features of this {@link SipDelegate} are
+ * denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will
+ * later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the
+ * delegate up.
+ */
+ void onCreated(@NonNull SipDelegate delegate, @Nullable List