/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; import android.os.Build; 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.HashSet; import java.util.List; import java.util.Set; /** * Contains the User Capability Exchange capabilities corresponding to a contact's URI. * @hide */ @SystemApi public final class RcsContactUceCapability implements Parcelable { /** 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 {} /** * The capabilities of this contact were requested recently enough to still be considered in * the availability window. */ 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) @IntDef(prefix = "SOURCE_TYPE_", value = { SOURCE_TYPE_NETWORK, SOURCE_TYPE_CACHED }) public @interface SourceType {} /** * 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 = 0; /** * 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 = 1; /** * 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 = 2; /** * Capability information for the requested contact was found successfully. */ public static final int REQUEST_RESULT_FOUND = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "REQUEST_RESULT_", value = { REQUEST_RESULT_UNKNOWN, REQUEST_RESULT_NOT_ONLINE, REQUEST_RESULT_NOT_FOUND, REQUEST_RESULT_FOUND }) public @interface RequestResult {} /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. */ public static final class OptionsBuilder { 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. */ public OptionsBuilder(@NonNull Uri contact) { mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS, SOURCE_TYPE_NETWORK); } /** * 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. * @hide */ public OptionsBuilder(@NonNull Uri contact, @SourceType int sourceType) { mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS, sourceType); } /** * Set the result of the capabilities request. * @param requestResult the request result * @return this OptionBuilder */ public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) { mCapabilities.mRequestResult = requestResult; return this; } /** * Add the feature tag into the capabilities instance. * @param tag the supported feature tag * @return this OptionBuilder */ public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) { mCapabilities.mFeatureTags.add(tag); return this; } /** * Add the list of feature tag into the capabilities instance. * @param tags the list of the supported feature tags * @return this OptionBuilder */ public @NonNull OptionsBuilder addFeatureTags(@NonNull Set tags) { mCapabilities.mFeatureTags.addAll(tags); return this; } /** * @return the constructed instance. */ public @NonNull RcsContactUceCapability build() { return mCapabilities; } } /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ public static final class PresenceBuilder { 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; } /** * Add the {@link RcsContactPresenceTuple} into the capabilities instance. * @param tuple The {@link RcsContactPresenceTuple} to be added into. * @return this PresenceBuilder */ public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) { mCapabilities.mPresenceTuples.add(tuple); return this; } /** * 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( @NonNull List tuples) { mCapabilities.mPresenceTuples.addAll(tuples); return this; } /** * Set the entity URI related to the contact whose capabilities were requested. * @param entityUri the 'pres' URL of the PRESENTITY publishing presence document. */ public @NonNull PresenceBuilder setEntityUri(@NonNull Uri entityUri) { mCapabilities.mEntityUri = entityUri; 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 Uri mEntityUri; private final Set mFeatureTags = new HashSet<>(); private final List 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(), android.net.Uri.class); mCapabilityMechanism = in.readInt(); mSourceType = in.readInt(); mRequestResult = in.readInt(); mEntityUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); List featureTagList = new ArrayList<>(); in.readStringList(featureTagList); mFeatureTags.addAll(featureTagList); in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.class); } @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(mContactUri, flags); out.writeInt(mCapabilityMechanism); out.writeInt(mSourceType); out.writeInt(mRequestResult); out.writeParcelable(mEntityUri, flags); out.writeStringList(new ArrayList<>(mFeatureTags)); out.writeParcelableList(mPresenceTuples, flags); } @Override public int describeContents() { return 0; } public static final @NonNull Creator CREATOR = new Creator() { @Override public RcsContactUceCapability createFromParcel(Parcel in) { return new RcsContactUceCapability(in); } @Override public RcsContactUceCapability[] newArray(int size) { return new RcsContactUceCapability[size]; } }; /** * @return The mechanism used to get the capabilities. */ public @CapabilityMechanism int getCapabilityMechanism() { return mCapabilityMechanism; } /** * @return The feature tags present in the OPTIONS response from the network. *

* Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} */ public @NonNull Set getFeatureTags() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { return Collections.emptySet(); } return Collections.unmodifiableSet(mFeatureTags); } /** * @return The tuple elements associated with the presence element portion of the PIDF document * contained in the NOTIFY response from the network. *

* Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ public @NonNull List getCapabilityTuples() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return Collections.emptyList(); } return Collections.unmodifiableList(mPresenceTuples); } /** * 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 or {@code null} if the * service id does not exist in the list of presence tuples returned from the network. * *

* Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return null; } for (RcsContactPresenceTuple tuple : mPresenceTuples) { if (tuple.getServiceId() != null && 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; } /** * Retrieve the contact URI requested by the applications. * @return the URI representing the contact associated with the capabilities. */ public @NonNull Uri getContactUri() { return mContactUri; } /** * Retrieve the entity URI of the contact whose presence information is being requested for. * @return the URI representing the 'pres' URL of the PRESENTITY publishing presence document * or {@code null} if the entity uri does not exist in the presence document. */ public @Nullable Uri getEntityUri() { return mEntityUri; } @Override public String toString() { StringBuilder builder = new StringBuilder("RcsContactUceCapability"); if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { builder.append("(presence) {"); } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { builder.append("(options) {"); } else { builder.append("(?) {"); } if (Build.IS_ENG) { builder.append("uri="); builder.append(mContactUri); } else { builder.append("uri (isNull)="); builder.append(mContactUri != null ? "XXX" : "null"); } builder.append(", sourceType="); builder.append(mSourceType); builder.append(", requestResult="); builder.append(mRequestResult); if (Build.IS_ENG) { builder.append("entity uri="); builder.append(mEntityUri != null ? mEntityUri : "null"); } else { builder.append("entity uri (isNull)="); builder.append(mEntityUri != null ? "XXX" : "null"); } if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { builder.append(", presenceTuples={"); builder.append(mPresenceTuples); builder.append("}"); } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { builder.append(", featureTags={"); builder.append(mFeatureTags); builder.append("}"); } return builder.toString(); } }