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          * @return the RcsContactUceCapability instance.
225          */
build()226         public @NonNull RcsContactUceCapability build() {
227             return mCapabilities;
228         }
229     }
230 
231     private final Uri mContactUri;
232     private @SourceType int mSourceType;
233     private @CapabilityMechanism int mCapabilityMechanism;
234     private @RequestResult int mRequestResult;
235 
236     private final Set<String> mFeatureTags = new HashSet<>();
237     private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
238 
RcsContactUceCapability(@onNull Uri contactUri, @CapabilityMechanism int mechanism, @SourceType int sourceType)239     private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
240             @SourceType int sourceType) {
241         mContactUri = contactUri;
242         mCapabilityMechanism = mechanism;
243         mSourceType = sourceType;
244     }
245 
RcsContactUceCapability(Parcel in)246     private RcsContactUceCapability(Parcel in) {
247         mContactUri = in.readParcelable(Uri.class.getClassLoader());
248         mCapabilityMechanism = in.readInt();
249         mSourceType = in.readInt();
250         mRequestResult = in.readInt();
251         List<String> featureTagList = new ArrayList<>();
252         in.readStringList(featureTagList);
253         mFeatureTags.addAll(featureTagList);
254         in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
255     }
256 
257     @Override
writeToParcel(@onNull Parcel out, int flags)258     public void writeToParcel(@NonNull Parcel out, int flags) {
259         out.writeParcelable(mContactUri, flags);
260         out.writeInt(mCapabilityMechanism);
261         out.writeInt(mSourceType);
262         out.writeInt(mRequestResult);
263         out.writeStringList(new ArrayList<>(mFeatureTags));
264         out.writeParcelableList(mPresenceTuples, flags);
265     }
266 
267     @Override
describeContents()268     public int describeContents() {
269         return 0;
270     }
271 
272     public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
273             new Creator<RcsContactUceCapability>() {
274                 @Override
275                 public RcsContactUceCapability createFromParcel(Parcel in) {
276                     return new RcsContactUceCapability(in);
277                 }
278 
279                 @Override
280                 public RcsContactUceCapability[] newArray(int size) {
281                     return new RcsContactUceCapability[size];
282                 }
283             };
284 
285     /**
286      * @return The mechanism used to get the capabilities.
287      */
getCapabilityMechanism()288     public @CapabilityMechanism int getCapabilityMechanism() {
289         return mCapabilityMechanism;
290     }
291 
292     /**
293      * @return The feature tags present in the OPTIONS response from the network.
294      * <p>
295      * Note: this is only populated if {@link #getCapabilityMechanism} is
296      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
297      */
getFeatureTags()298     public @NonNull Set<String> getFeatureTags() {
299         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
300             return Collections.emptySet();
301         }
302         return Collections.unmodifiableSet(mFeatureTags);
303     }
304 
305     /**
306      * @return The tuple elements associated with the presence element portion of the PIDF document
307      * contained in the NOTIFY response from the network.
308      * <p>
309      * Note: this is only populated if {@link #getCapabilityMechanism} is
310      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
311      */
getCapabilityTuples()312     public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
313         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
314             return Collections.emptyList();
315         }
316         return Collections.unmodifiableList(mPresenceTuples);
317     }
318 
319     /**
320      * Get the RcsContactPresenceTuple associated with the given service id.
321      * @param serviceId The service id to get the presence tuple.
322      * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
323      * service id does not exist in the list of presence tuples returned from the network.
324      *
325      * <p>
326      * Note: this is only populated if {@link #getCapabilityMechanism} is
327      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
328      */
getCapabilityTuple(@onNull String serviceId)329     public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
330         if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
331             return null;
332         }
333         for (RcsContactPresenceTuple tuple : mPresenceTuples) {
334             if (tuple.getServiceId() != null && tuple.getServiceId().equals(serviceId)) {
335                 return tuple;
336             }
337         }
338         return null;
339     }
340 
341     /**
342      * @return the source of the data that was used to populate the capabilities of the requested
343      * contact.
344      */
getSourceType()345     public @SourceType int getSourceType() {
346         return mSourceType;
347     }
348 
349     /**
350      * @return the result of querying the capabilities of the requested contact.
351      */
getRequestResult()352     public @RequestResult int getRequestResult() {
353         return mRequestResult;
354     }
355 
356     /**
357      * Retrieve the contact URI requested by the applications.
358      * @return the URI representing the contact associated with the capabilities.
359      */
getContactUri()360     public @NonNull Uri getContactUri() {
361         return mContactUri;
362     }
363 
364     @Override
toString()365     public String toString() {
366         StringBuilder builder = new StringBuilder("RcsContactUceCapability");
367         if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
368             builder.append("(presence) {");
369         } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
370             builder.append("(options) {");
371         } else {
372             builder.append("(?) {");
373         }
374         if (Build.IS_ENG) {
375             builder.append("uri=");
376             builder.append(mContactUri);
377         } else {
378             builder.append("uri (isNull)=");
379             builder.append(mContactUri != null ? "XXX" : "null");
380         }
381         builder.append(", sourceType=");
382         builder.append(mSourceType);
383         builder.append(", requestResult=");
384         builder.append(mRequestResult);
385 
386         if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
387             builder.append(", presenceTuples={");
388             builder.append(mPresenceTuples);
389             builder.append("}");
390         } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
391             builder.append(", featureTags={");
392             builder.append(mFeatureTags);
393             builder.append("}");
394         }
395 
396         return builder.toString();
397     }
398 }
399