diff options
3 files changed, 602 insertions, 201 deletions
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl new file mode 100644 index 000000000000..a70470294794 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl @@ -0,0 +1,20 @@ +/* + * 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 RcsContactPresenceTuple; diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java new file mode 100644 index 000000000000..b0aaa92dd0ac --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -0,0 +1,360 @@ +/* + * 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.Nullable; +import android.annotation.StringDef; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a PIDF tuple element that is part of the presence element returned from the carrier + * network during a SUBSCRIBE request. See RFC3863 for more information. + * @hide + */ +public class RcsContactPresenceTuple implements Parcelable { + + /** The service id of the MMTEL */ + public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + + /** The service capabilities is available. */ + public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + + /** The service capabilities is unavailable. */ + public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = { + TUPLE_BASIC_STATUS_OPEN, + TUPLE_BASIC_STATUS_CLOSED + }) + public @interface BasicStatus {} + + /** + * An optional addition to the PIDF Presence Tuple containing service capabilities, which is + * defined in the servcaps element. See RFC5196, section 3.2.1. + */ + public static class ServiceCapabilities implements Parcelable { + + /** The service can simultaneously send and receive data. */ + public static final String DUPLEX_MODE_FULL = "full"; + + /** The service can alternate between sending and receiving data.*/ + public static final String DUPLEX_MODE_HALF = "half"; + + /** The service can only receive data. */ + public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + + /** The service can only send data. */ + public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "DUPLEX_MODE_", value = { + DUPLEX_MODE_FULL, + DUPLEX_MODE_HALF, + DUPLEX_MODE_RECEIVE_ONLY, + DUPLEX_MODE_SEND_ONLY + }) + public @interface DuplexMode {} + + /** + * Builder to help construct {@link ServiceCapabilities} instances. + */ + public static class Builder { + + private ServiceCapabilities mCapabilities; + + /** + * Create the ServiceCapabilities builder, which can be used to set service capabilities + * as well as custom capability extensions. + * @param isAudioCapable Whether the audio is capable or not. + * @param isVideoCapable Whether the video is capable or not. + */ + public Builder(boolean isAudioCapable, boolean isVideoCapable) { + mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable); + } + + /** + * Add the supported duplex mode. + * @param mode The supported duplex mode + */ + public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mSupportedDuplexModeList.add(mode); + return this; + } + + /** + * Add the unsupported duplex mode. + * @param mode The unsupported duplex mode + */ + public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mUnsupportedDuplexModeList.add(mode); + return this; + } + + /** + * @return the ServiceCapabilities instance. + */ + public ServiceCapabilities build() { + return mCapabilities; + } + } + + private final boolean mIsAudioCapable; + private final boolean mIsVideoCapable; + private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>(); + private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>(); + + /** + * Use {@link Builder} to build an instance of this interface. + * @param isAudioCapable Whether the audio is capable. + * @param isVideoCapable Whether the video is capable. + */ + ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) { + mIsAudioCapable = isAudioCapable; + mIsVideoCapable = isVideoCapable; + } + + private ServiceCapabilities(Parcel in) { + mIsAudioCapable = in.readBoolean(); + mIsVideoCapable = in.readBoolean(); + in.readStringList(mSupportedDuplexModeList); + in.readStringList(mUnsupportedDuplexModeList); + } + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeBoolean(mIsAudioCapable); + out.writeBoolean(mIsVideoCapable); + out.writeStringList(mSupportedDuplexModeList); + out.writeStringList(mUnsupportedDuplexModeList); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<ServiceCapabilities> CREATOR = + new Creator<ServiceCapabilities>() { + @Override + public ServiceCapabilities createFromParcel(Parcel in) { + return new ServiceCapabilities(in); + } + + @Override + public ServiceCapabilities[] newArray(int size) { + return new ServiceCapabilities[size]; + } + }; + + /** + * Query the audio capable. + * @return true if the audio is capable, false otherwise. + */ + public boolean isAudioCapable() { + return mIsAudioCapable; + } + + /** + * Query the video capable. + * @return true if the video is capable, false otherwise. + */ + public boolean isVideoCapable() { + return mIsVideoCapable; + } + + /** + * Get the supported duplex mode list. + * @return The list of supported duplex mode + */ + public @NonNull @DuplexMode List<String> getSupportedDuplexModes() { + return Collections.unmodifiableList(mSupportedDuplexModeList); + } + + /** + * Get the unsupported duplex mode list. + * @return The list of unsupported duplex mode + */ + public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() { + return Collections.unmodifiableList(mUnsupportedDuplexModeList); + } + } + + /** + * Builder to help construct {@link RcsContactPresenceTuple} instances. + */ + public static class Builder { + + private RcsContactPresenceTuple mPresenceTuple; + + /** + * Builds a RcsContactPresenceTuple instance. + * @param serviceId The OMA Presence service-id associated with this capability. See the + * OMA Presence SIMPLE specification v1.1, section 10.5.1. + * @param serviceVersion The OMA Presence version associated with the service capability. + * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. + */ + public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId, + @NonNull String serviceVersion) { + mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); + } + + /** + * The optional SIP Contact URI associated with the PIDF tuple element. + */ + public Builder addContactUri(@NonNull Uri contactUri) { + mPresenceTuple.mContactUri = contactUri; + return this; + } + + /** + * The optional timestamp indicating the data and time of the status change of this tuple. + * See RFC3863, section 4.1.7 for more information on the expected format. + */ + public Builder addTimeStamp(@NonNull String timestamp) { + mPresenceTuple.mTimestamp = timestamp; + return this; + } + + /** + * An optional parameter containing the description element of the service-description. See + * OMA Presence SIMPLE specification v1.1 + */ + public Builder addDescription(@NonNull String description) { + mPresenceTuple.mServiceDescription = description; + return this; + } + + /** + * An optional parameter containing the service capabilities of the presence tuple if they + * are present in the servcaps element. + */ + public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + mPresenceTuple.mServiceCapabilities = caps; + return this; + } + + /** + * @return the constructed instance. + */ + public RcsContactPresenceTuple build() { + return mPresenceTuple; + } + } + + private Uri mContactUri; + private String mTimestamp; + private @BasicStatus String mStatus; + + // The service information in the service-description element. + private String mServiceId; + private String mServiceVersion; + private String mServiceDescription; + + private ServiceCapabilities mServiceCapabilities; + + private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId, + @NonNull String serviceVersion) { + mStatus = status; + mServiceId = serviceId; + mServiceVersion = serviceVersion; + } + + private RcsContactPresenceTuple(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mTimestamp = in.readString(); + mStatus = in.readString(); + mServiceId = in.readString(); + mServiceVersion = in.readString(); + mServiceDescription = in.readString(); + mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader()); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mContactUri, flags); + out.writeString(mTimestamp); + out.writeString(mStatus); + out.writeString(mServiceId); + out.writeString(mServiceVersion); + out.writeString(mServiceDescription); + out.writeParcelable(mServiceCapabilities, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR = + new Creator<RcsContactPresenceTuple>() { + @Override + public RcsContactPresenceTuple createFromParcel(Parcel in) { + return new RcsContactPresenceTuple(in); + } + + @Override + public RcsContactPresenceTuple[] newArray(int size) { + return new RcsContactPresenceTuple[size]; + } + }; + + /** @return the status of the tuple element. */ + public @NonNull @BasicStatus String getStatus() { + return mStatus; + } + + /** @return the service-id element of the service-description */ + public @NonNull String getServiceId() { + return mServiceId; + } + + /** @return the version element of the service-description */ + public @NonNull String getServiceVersion() { + return mServiceVersion; + } + + /** @return the SIP URI contained in the contact element of the tuple if it exists. */ + public @Nullable Uri getContactUri() { + return mContactUri; + } + + /** @return the timestamp element contained in the tuple if it exists */ + public @Nullable String getTimestamp() { + return mTimestamp; + } + + /** @return the description element contained in the service-description if it exists */ + public @Nullable String getServiceDescription() { + return mServiceDescription; + } + + /** @return the {@link ServiceCapabilities} of the tuple if it exists. */ + public @Nullable ServiceCapabilities getServiceCapabilities() { + return mServiceCapabilities; + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index dc36edf5aad9..d12a6aef5186 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -16,7 +16,7 @@ package android.telephony.ims; -import android.annotation.LongDef; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; @@ -27,9 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * Contains the User Capability Exchange capabilities corresponding to a contact's URI. @@ -37,114 +35,80 @@ import java.util.Map; */ public final class RcsContactUceCapability implements Parcelable { - /** Supports 1-to-1 chat */ - public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0); - /** Supports group chat */ - public static final int CAPABILITY_CHAT_SESSION = (1 << 1); - /** Supports full store and forward group chat information. */ - public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2); + /** Contains presence information associated with the contact */ + public static final int CAPABILITY_MECHANISM_PRESENCE = 1; + + /** Contains OPTIONS information associated with the contact */ + public static final int CAPABILITY_MECHANISM_OPTIONS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_MECHANISM_", value = { + CAPABILITY_MECHANISM_PRESENCE, + CAPABILITY_MECHANISM_OPTIONS + }) + public @interface CapabilityMechanism {} + /** - * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward. + * The capabilities of this contact were requested recently enough to still be considered in + * the availability window. */ - public static final int CAPABILITY_FILE_TRANSFER = (1 << 3); - /** Supports File Transfer Thumbnail */ - public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4); - /** Supports File Transfer with Store and Forward */ - public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5); - /** Supports File Transfer via HTTP */ - public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6); - /** Supports file transfer via SMS */ - public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7); - /** Supports image sharing */ - public static final int CAPABILITY_IMAGE_SHARE = (1 << 8); - /** Supports video sharing during a circuit-switch call (IR.74)*/ - public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9); - /** Supports video share outside of voice call (IR.84) */ - public static final int CAPABILITY_VIDEO_SHARE = (1 << 10); - /** Supports social presence information */ - public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11); - /** Supports capability discovery via presence */ - public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12); - /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */ - public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13); - /** Supports IP video calling (IR.94) */ - public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14); - /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */ - public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15); - /** Supports Geolocation PUSH via SMS for fallback. */ - public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16); - /** Supports Geolocation pull. */ - public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17); - /** Supports Geolocation pull using file transfer support. */ - public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18); - /** Supports RCS voice calling */ - public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); - /** Supports RCS video calling */ - public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); - /** Supports RCS video calling, where video media can not be dropped. */ - public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); - /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/ - public static final int CAPABILITY_CALL_COMPOSER = (1 << 22); - /** Supports post call information that is included in the call if the call is missed.*/ - public static final int CAPABILITY_POST_CALL = (1 << 23); - /** Supports sharing a map where the user can draw, share markers, and share their position. */ - public static final int CAPABILITY_SHARED_MAP = (1 << 24); - /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/ - public static final int CAPABILITY_SHARED_SKETCH = (1 << 25); - /** Supports communication with Chatbots. */ - public static final int CAPABILITY_CHAT_BOT = (1 << 26); - /** Supports Chatbot roles. */ - public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27); - /** Supports the unidirectional plug-ins framework. */ - public static final int CAPABILITY_PLUG_IN = (1 << 28); - /** Supports standalone Chatbot communication. */ - public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29); - /** Supports MMTEL based call composer. */ - public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30); - - - - /** @hide*/ + public static final int SOURCE_TYPE_NETWORK = 0; + + /** + * The capabilities of this contact were retrieved from the cached information in the Enhanced + * Address Book. + */ + public static final int SOURCE_TYPE_CACHED = 1; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) - @LongDef(prefix = "CAPABILITY_", flag = true, value = { - CAPABILITY_CHAT_STANDALONE, - CAPABILITY_CHAT_SESSION, - CAPABILITY_CHAT_SESSION_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER, - CAPABILITY_FILE_TRANSFER_THUMBNAIL, - CAPABILITY_FILE_TRANSFER_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER_HTTP, - CAPABILITY_FILE_TRANSFER_SMS, - CAPABILITY_IMAGE_SHARE, - CAPABILITY_VIDEO_SHARE_DURING_CS_CALL, - CAPABILITY_VIDEO_SHARE, - CAPABILITY_SOCIAL_PRESENCE, - CAPABILITY_DISCOVERY_VIA_PRESENCE, - CAPABILITY_IP_VOICE_CALL, - CAPABILITY_IP_VIDEO_CALL, - CAPABILITY_GEOLOCATION_PUSH, - CAPABILITY_GEOLOCATION_PUSH_SMS, - CAPABILITY_GEOLOCATION_PULL, - CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, - CAPABILITY_RCS_VOICE_CALL, - CAPABILITY_RCS_VIDEO_CALL, - CAPABILITY_RCS_VIDEO_ONLY_CALL, - CAPABILITY_CALL_COMPOSER, - CAPABILITY_POST_CALL, - CAPABILITY_SHARED_MAP, - CAPABILITY_SHARED_SKETCH, - CAPABILITY_CHAT_BOT, - CAPABILITY_CHAT_BOT_ROLE, - CAPABILITY_PLUG_IN, - CAPABILITY_STANDALONE_CHAT_BOT, - CAPABILITY_MMTEL_CALL_COMPOSER + @IntDef(prefix = "SOURCE_TYPE_", value = { + SOURCE_TYPE_NETWORK, + SOURCE_TYPE_CACHED }) - public @interface CapabilityFlag {} + public @interface SourceType {} + + /** + * The requested contact was found to be offline when queried. This is only applicable to + * contact capabilities that were queried via OPTIONS requests and the network returned a + * 408/480 response. + */ + public static final int REQUEST_RESULT_NOT_ONLINE = 0; + + /** + * Capability information for the requested contact was not found. The contact should not be + * considered an RCS user. + */ + public static final int REQUEST_RESULT_NOT_FOUND = 1; /** - * Builder to help construct {@link RcsContactUceCapability} instances. + * Capability information for the requested contact was found successfully. */ - public static class Builder { + public static final int REQUEST_RESULT_FOUND = 2; + + /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_NOT_ONLINE, + REQUEST_RESULT_NOT_FOUND, + REQUEST_RESULT_FOUND, + REQUEST_RESULT_UNKNOWN + }) + public @interface RequestResult {} + + /** + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through SIP OPTIONS. + */ + public static class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -153,51 +117,38 @@ public final class RcsContactUceCapability implements Parcelable { * capability extensions. * @param contact The contact URI that the capabilities are attached to. */ - public Builder(@NonNull Uri contact) { - mCapabilities = new RcsContactUceCapability(contact); + public OptionsBuilder(@NonNull Uri contact) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS, + SOURCE_TYPE_NETWORK); } /** - * Add a UCE capability bit-field as well as the associated URI that the framework should - * use for those services. This is mainly used for capabilities that may use a URI separate - * from the contact's URI, for example the URI to use for VT calls. - * @param type The capability to map to a service URI that is different from the contact's - * URI. + * Set the result of the capabilities request. + * @param requestResult the request result + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) { - mCapabilities.mCapabilities |= type; - // Put each of these capabilities into the map separately. - for (long shift = 0; shift < Integer.SIZE; shift++) { - long cap = type & (1 << shift); - if (cap != 0) { - mCapabilities.mServiceMap.put(cap, serviceUri); - // remove that capability from the field. - type &= ~cap; - } - if (type == 0) { - // no need to keep going, end early. - break; - } - } + public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) { + mCapabilities.mRequestResult = requestResult; return this; } /** - * Add a UCE capability flag that this contact supports. - * @param type the capability that the contact supports. + * Add the feature tag into the capabilities instance. + * @param tag the supported feature tag + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type) { - mCapabilities.mCapabilities |= type; + public @NonNull OptionsBuilder addFeatureTag(String tag) { + mCapabilities.mFeatureTags.add(tag); return this; } /** - * Add a carrier specific service tag. - * @param extension A string containing a carrier specific service tag that is an extension - * of the {@link CapabilityFlag}s that are defined here. + * Add the list of feature tag into the capabilities instance. + * @param tags the list of the supported feature tags + * @return this OptionBuilder */ - public @NonNull Builder add(@NonNull String extension) { - mCapabilities.mExtensionTags.add(extension); + public @NonNull OptionsBuilder addFeatureTags(List<String> tags) { + mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -209,56 +160,88 @@ public final class RcsContactUceCapability implements Parcelable { } } - private final Uri mContactUri; - private long mCapabilities; - private List<String> mExtensionTags = new ArrayList<>(); - private Map<Long, Uri> mServiceMap = new HashMap<>(); - /** - * Use {@link Builder} to build an instance of this interface. - * @param contact The URI associated with this capability information. - * @hide + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through a presence server. */ - RcsContactUceCapability(@NonNull Uri contact) { - mContactUri = contact; - } + public static class PresenceBuilder { - private RcsContactUceCapability(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mCapabilities = in.readLong(); - in.readStringList(mExtensionTags); - // read mServiceMap as key,value pair - int mapSize = in.readInt(); - for (int i = 0; i < mapSize; i++) { - mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader())); + private final RcsContactUceCapability mCapabilities; + + /** + * Create the builder, which can be used to set UCE capabilities as well as custom + * capability extensions. + * @param contact The contact URI that the capabilities are attached to. + * @param sourceType The type where the capabilities of this contact were retrieved from. + * @param requestResult the request result + */ + public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType, + @RequestResult int requestResult) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE, + sourceType); + mCapabilities.mRequestResult = requestResult; } - } - public static final @NonNull Creator<RcsContactUceCapability> CREATOR = - new Creator<RcsContactUceCapability>() { - @Override - public RcsContactUceCapability createFromParcel(Parcel in) { - return new RcsContactUceCapability(in); + /** + * Add the {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuple The {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) { + mCapabilities.mPresenceTuples.add(tuple); + return this; } - @Override - public RcsContactUceCapability[] newArray(int size) { - return new RcsContactUceCapability[size]; + /** + * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) { + mCapabilities.mPresenceTuples.addAll(tuples); + return this; } - }; + + /** + * @return the RcsContactUceCapability instance. + */ + public @NonNull RcsContactUceCapability build() { + return mCapabilities; + } + } + + private final Uri mContactUri; + private @SourceType int mSourceType; + private @CapabilityMechanism int mCapabilityMechanism; + private @RequestResult int mRequestResult; + + private final List<String> mFeatureTags = new ArrayList<>(); + private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>(); + + private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism, + @SourceType int sourceType) { + mContactUri = contactUri; + mCapabilityMechanism = mechanism; + mSourceType = sourceType; + } + + private RcsContactUceCapability(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mCapabilityMechanism = in.readInt(); + mSourceType = in.readInt(); + mRequestResult = in.readInt(); + in.readStringList(mFeatureTags); + in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader()); + } @Override public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(mContactUri, 0); - out.writeLong(mCapabilities); - out.writeStringList(mExtensionTags); - // write mServiceMap as key,value pairs - int mapSize = mServiceMap.keySet().size(); - out.writeInt(mapSize); - for (long key : mServiceMap.keySet()) { - out.writeLong(key); - out.writeParcelable(mServiceMap.get(key), 0); - } + out.writeParcelable(mContactUri, flags); + out.writeInt(mCapabilityMechanism); + out.writeInt(mSourceType); + out.writeInt(mRequestResult); + out.writeStringList(mFeatureTags); + out.writeParcelableList(mPresenceTuples, flags); } @Override @@ -266,49 +249,87 @@ public final class RcsContactUceCapability implements Parcelable { return 0; } + public static final @NonNull Creator<RcsContactUceCapability> CREATOR = + new Creator<RcsContactUceCapability>() { + @Override + public RcsContactUceCapability createFromParcel(Parcel in) { + return new RcsContactUceCapability(in); + } + + @Override + public RcsContactUceCapability[] newArray(int size) { + return new RcsContactUceCapability[size]; + } + }; + /** - * Query for a capability - * @param type The capability flag to query. - * @return true if the capability flag specified is set, false otherwise. + * @return The mechanism used to get the capabilities. */ - public boolean isCapable(@CapabilityFlag long type) { - return (mCapabilities & type) > 0; + public @CapabilityMechanism int getCapabilityMechanism() { + return mCapabilityMechanism; } /** - * @return true if the extension service tag is set, false otherwise. + * @return The feature tags present in the OPTIONS response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_OPTIONS} */ - public boolean isCapable(@NonNull String extensionTag) { - return mExtensionTags.contains(extensionTag); + public @NonNull List<String> getOptionsFeatureTags() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(mFeatureTags); } /** - * @return An immutable list containing all of the extension tags that have been set as capable. - * @throws UnsupportedOperationException if this list is modified. + * @return The tuple elements associated with the presence element portion of the PIDF document + * contained in the NOTIFY response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<String> getCapableExtensionTags() { - return Collections.unmodifiableList(mExtensionTags); + public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(mPresenceTuples); } /** - * Retrieves the {@link Uri} associated with the capability being queried. - * <p> - * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless - * a different service {@link Uri} was associated with this capability using - * {@link Builder#add(long, Uri)}. + * Get the RcsContactPresenceTuple associated with the given service id. + * @param serviceId The service id to get the presence tuple. + * @return The RcsContactPresenceTuple which has the given service id. * - * @return a String containing the {@link Uri} associated with the service tag or - * {@code null} if this capability is not set as capable. - * @see #isCapable(long) + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable Uri getServiceUri(@CapabilityFlag long type) { - Uri result = mServiceMap.getOrDefault(type, null); - // If the capability is capable, but does not have a service URI associated, use the default - // contact URI. - if (result == null) { - return isCapable(type) ? getContactUri() : null; + public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return null; } - return result; + for (RcsContactPresenceTuple tuple : mPresenceTuples) { + if (tuple.getServiceId().equals(serviceId)) { + return tuple; + } + } + return null; + } + + /** + * @return the source of the data that was used to populate the capabilities of the requested + * contact. + */ + public @SourceType int getSourceType() { + return mSourceType; + } + + /** + * @return the result of querying the capabilities of the requested contact. + */ + public @RequestResult int getRequestResult() { + return mRequestResult; } /** |