1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony.ims;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.net.Uri;
24 import android.os.Build;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
38  * @hide
39  */
40 @SystemApi
41 public final class RcsContactUceCapability implements Parcelable {
42 
43     /** Contains presence information associated with the contact */
44     public static final int CAPABILITY_MECHANISM_PRESENCE = 1;
45 
46     /** Contains OPTIONS information associated with the contact */
47     public static final int CAPABILITY_MECHANISM_OPTIONS = 2;
48 
49     /** @hide */
50     @Retention(RetentionPolicy.SOURCE)
51     @IntDef(prefix = "CAPABILITY_MECHANISM_", value = {
52         CAPABILITY_MECHANISM_PRESENCE,
53         CAPABILITY_MECHANISM_OPTIONS
54     })
55     public @interface CapabilityMechanism {}
56 
57     /**
58      * The capabilities of this contact were requested recently enough to still be considered in
59      * the availability window.
60      */
61     public static final int SOURCE_TYPE_NETWORK = 0;
62 
63     /**
64      * The capabilities of this contact were retrieved from the cached information in the Enhanced
65      * Address Book.
66      */
67     public static final int SOURCE_TYPE_CACHED = 1;
68 
69     /** @hide */
70     @Retention(RetentionPolicy.SOURCE)
71     @IntDef(prefix = "SOURCE_TYPE_", value = {
72         SOURCE_TYPE_NETWORK,
73         SOURCE_TYPE_CACHED
74     })
75     public @interface SourceType {}
76 
77     /**
78      * Capability information for the requested contact has expired and can not be refreshed due to
79      * a temporary network error. This is a temporary error and the capabilities of the contact
80      * should be queried again at a later time.
81      */
82     public static final int REQUEST_RESULT_UNKNOWN = 0;
83 
84     /**
85      * The requested contact was found to be offline when queried. This is only applicable to
86      * contact capabilities that were queried via OPTIONS requests and the network returned a
87      * 408/480 response.
88      */
89     public static final int REQUEST_RESULT_NOT_ONLINE = 1;
90 
91     /**
92      * Capability information for the requested contact was not found. The contact should not be
93      * considered an RCS user.
94      */
95     public static final int REQUEST_RESULT_NOT_FOUND = 2;
96 
97     /**
98      * Capability information for the requested contact was found successfully.
99      */
100     public static final int REQUEST_RESULT_FOUND = 3;
101 
102     /** @hide */
103     @Retention(RetentionPolicy.SOURCE)
104     @IntDef(prefix = "REQUEST_RESULT_", value = {
105         REQUEST_RESULT_UNKNOWN,
106         REQUEST_RESULT_NOT_ONLINE,
107         REQUEST_RESULT_NOT_FOUND,
108         REQUEST_RESULT_FOUND
109     })
110     public @interface RequestResult {}
111 
112     /**
113      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
114      * queried through SIP OPTIONS.
115      */
116     public static final class OptionsBuilder {
117 
118         private final RcsContactUceCapability mCapabilities;
119 
120         /**
121          * Create the Builder, which can be used to set UCE capabilities as well as custom
122          * capability extensions.
123          * @param contact The contact URI that the capabilities are attached to.
124          */
OptionsBuilder(@onNull Uri contact)125         public OptionsBuilder(@NonNull Uri contact) {
126             mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
127                     SOURCE_TYPE_NETWORK);
128         }
129 
130         /**
131          * Create the Builder, which can be used to set UCE capabilities as well as custom
132          * capability extensions.
133          * @param contact The contact URI that the capabilities are attached to.
134          * @param sourceType The type where the capabilities of this contact were retrieved from.
135          * @hide
136          */
OptionsBuilder(@onNull Uri contact, @SourceType int sourceType)137         public OptionsBuilder(@NonNull Uri contact, @SourceType int sourceType) {
138             mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
139                     sourceType);
140         }
141 
142         /**
143          * Set the result of the capabilities request.
144          * @param requestResult the request result
145          * @return this OptionBuilder
146          */
setRequestResult(@equestResult int requestResult)147         public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) {
148             mCapabilities.mRequestResult = requestResult;
149             return this;
150         }
151 
152         /**
153          * Add the feature tag into the capabilities instance.
154          * @param tag the supported feature tag
155          * @return this OptionBuilder
156          */
addFeatureTag(@onNull String tag)157         public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) {
158             mCapabilities.mFeatureTags.add(tag);
159             return this;
160         }
161 
162         /**
163          * Add the list of feature tag into the capabilities instance.
164          * @param tags the list of the supported feature tags
165          * @return this OptionBuilder
166          */
addFeatureTags(@onNull Set<String> tags)167         public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) {
168             mCapabilities.mFeatureTags.addAll(tags);
169             return this;
170         }
171 
172         /**
173          * @return the constructed instance.
174          */
build()175         public @NonNull RcsContactUceCapability build() {
176             return mCapabilities;
177         }
178     }
179 
180     /**
181      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
182      * queried through a presence server.
183      */
184     public static final class PresenceBuilder {
185 
186         private final RcsContactUceCapability mCapabilities;
187 
188         /**
189          * Create the builder, which can be used to set UCE capabilities as well as custom
190          * capability extensions.
191          * @param contact The contact URI that the capabilities are attached to.
192          * @param sourceType The type where the capabilities of this contact were retrieved from.
193          * @param requestResult the request result
194          */
PresenceBuilder(@onNull Uri contact, @SourceType int sourceType, @RequestResult int requestResult)195         public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType,
196                 @RequestResult int requestResult) {
197             mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE,
198                 sourceType);
199             mCapabilities.mRequestResult = requestResult;
200         }
201 
202         /**
203          * Add the {@link RcsContactPresenceTuple} into the capabilities instance.
204          * @param tuple The {@link RcsContactPresenceTuple} to be added into.
205          * @return this PresenceBuilder
206          */
addCapabilityTuple(@onNull RcsContactPresenceTuple tuple)207         public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) {
208             mCapabilities.mPresenceTuples.add(tuple);
209             return this;
210         }
211 
212         /**
213          * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance.
214          * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
215          * @return this PresenceBuilder
216          */
addCapabilityTuples( @onNull List<RcsContactPresenceTuple> tuples)217         public @NonNull PresenceBuilder addCapabilityTuples(
218                 @NonNull List<RcsContactPresenceTuple> tuples) {
219             mCapabilities.mPresenceTuples.addAll(tuples);
220             return this;
221         }
222 
223         /**
224          * Set the entity URI related to the contact whose capabilities were requested.
225          * @param entityUri the 'pres' URL of the PRESENTITY publishing presence document.
226          */
setEntityUri(@onNull Uri entityUri)227         public @NonNull PresenceBuilder setEntityUri(@NonNull Uri entityUri) {
228             mCapabilities.mEntityUri = entityUri;
229             return this;
230         }
231 
232         /**
233          * @return the RcsContactUceCapability instance.
234          */
build()235         public @NonNull RcsContactUceCapability build() {
236             return mCapabilities;
237         }
238     }
239 
240     private final Uri mContactUri;
241     private @SourceType int mSourceType;
242     private @CapabilityMechanism int mCapabilityMechanism;
243     private @RequestResult int mRequestResult;
244     private Uri mEntityUri;
245 
246     private final Set<String> mFeatureTags = new HashSet<>();
247     private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
248 
RcsContactUceCapability(@onNull Uri contactUri, @CapabilityMechanism int mechanism, @SourceType int sourceType)249     private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
250             @SourceType int sourceType) {
251         mContactUri = contactUri;
252         mCapabilityMechanism = mechanism;
253         mSourceType = sourceType;
254     }
255 
RcsContactUceCapability(Parcel in)256     private RcsContactUceCapability(Parcel in) {
257         mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
258         mCapabilityMechanism = in.readInt();
259         mSourceType = in.readInt();
260         mRequestResult = in.readInt();
261         mEntityUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
262         List<String> featureTagList = new ArrayList<>();
263         in.readStringList(featureTagList);
264         mFeatureTags.addAll(featureTagList);
265         in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.class);
266     }
267 
268     @Override
writeToParcel(@onNull Parcel out, int flags)269     public void writeToParcel(@NonNull Parcel out, int flags) {
270         out.writeParcelable(mContactUri, flags);
271         out.writeInt(mCapabilityMechanism);
272         out.writeInt(mSourceType);
273         out.writeInt(mRequestResult);
274         out.writeParcelable(mEntityUri, flags);
275         out.writeStringList(new ArrayList<>(mFeatureTags));
276         out.writeParcelableList(mPresenceTuples, flags);
277     }
278 
279     @Override
describeContents()280     public int describeContents() {
281         return 0;
282     }
283 
284     public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
285             new Creator<RcsContactUceCapability>() {
286                 @Override
287                 public RcsContactUceCapability createFromParcel(Parcel in) {
288                     return new RcsContactUceCapability(in);
289                 }
290 
291                 @Override
292                 public RcsContactUceCapability[] newArray(int size) {
293                     return new RcsContactUceCapability[size];
294                 }
295             };
296 
297     /**
298      * @return The mechanism used to get the capabilities.
299      */
getCapabilityMechanism()300     public @CapabilityMechanism int getCapabilityMechanism() {
301         return mCapabilityMechanism;
302     }
303 
304     /**
305      * @return The feature tags present in the OPTIONS response from the network.
306      * <p>
307      * Note: this is only populated if {@link #getCapabilityMechanism} is
308      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
309      */
getFeatureTags()310     public @NonNull Set<String> getFeatureTags() {
311         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
312             return Collections.emptySet();
313         }
314         return Collections.unmodifiableSet(mFeatureTags);
315     }
316 
317     /**
318      * @return The tuple elements associated with the presence element portion of the PIDF document
319      * contained in the NOTIFY response from the network.
320      * <p>
321      * Note: this is only populated if {@link #getCapabilityMechanism} is
322      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
323      */
getCapabilityTuples()324     public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
325         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
326             return Collections.emptyList();
327         }
328         return Collections.unmodifiableList(mPresenceTuples);
329     }
330 
331     /**
332      * Get the RcsContactPresenceTuple associated with the given service id.
333      * @param serviceId The service id to get the presence tuple.
334      * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
335      * service id does not exist in the list of presence tuples returned from the network.
336      *
337      * <p>
338      * Note: this is only populated if {@link #getCapabilityMechanism} is
339      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
340      */
getCapabilityTuple(@onNull String serviceId)341     public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
342         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
343             return null;
344         }
345         for (RcsContactPresenceTuple tuple : mPresenceTuples) {
346             if (tuple.getServiceId() != null && tuple.getServiceId().equals(serviceId)) {
347                 return tuple;
348             }
349         }
350         return null;
351     }
352 
353     /**
354      * @return the source of the data that was used to populate the capabilities of the requested
355      * contact.
356      */
getSourceType()357     public @SourceType int getSourceType() {
358         return mSourceType;
359     }
360 
361     /**
362      * @return the result of querying the capabilities of the requested contact.
363      */
getRequestResult()364     public @RequestResult int getRequestResult() {
365         return mRequestResult;
366     }
367 
368     /**
369      * Retrieve the contact URI requested by the applications.
370      * @return the URI representing the contact associated with the capabilities.
371      */
getContactUri()372     public @NonNull Uri getContactUri() {
373         return mContactUri;
374     }
375 
376     /**
377      * Retrieve the entity URI of the contact whose presence information is being requested for.
378      * @return the URI representing the 'pres' URL of the PRESENTITY publishing presence document
379      * or {@code null} if the entity uri does not exist in the presence document.
380      */
getEntityUri()381     public @Nullable Uri getEntityUri() {
382         return mEntityUri;
383     }
384 
385     @Override
toString()386     public String toString() {
387         StringBuilder builder = new StringBuilder("RcsContactUceCapability");
388         if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
389             builder.append("(presence) {");
390         } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
391             builder.append("(options) {");
392         } else {
393             builder.append("(?) {");
394         }
395         if (Build.IS_ENG) {
396             builder.append("uri=");
397             builder.append(mContactUri);
398         } else {
399             builder.append("uri (isNull)=");
400             builder.append(mContactUri != null ? "XXX" : "null");
401         }
402         builder.append(", sourceType=");
403         builder.append(mSourceType);
404         builder.append(", requestResult=");
405         builder.append(mRequestResult);
406         if (Build.IS_ENG) {
407             builder.append("entity uri=");
408             builder.append(mEntityUri != null ? mEntityUri : "null");
409         } else {
410             builder.append("entity uri (isNull)=");
411             builder.append(mEntityUri != null ? "XXX" : "null");
412         }
413 
414         if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
415             builder.append(", presenceTuples={");
416             builder.append(mPresenceTuples);
417             builder.append("}");
418         } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
419             builder.append(", featureTags={");
420             builder.append(mFeatureTags);
421             builder.append("}");
422         }
423 
424         return builder.toString();
425     }
426 }
427