• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.ims.rcs.uce.request;
18 
19 import android.net.Uri;
20 import android.telephony.ims.RcsContactTerminatedReason;
21 import android.telephony.ims.RcsContactUceCapability;
22 import android.telephony.ims.RcsUceAdapter;
23 import android.telephony.ims.RcsUceAdapter.ErrorCode;
24 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
25 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import com.android.ims.rcs.uce.UceController;
30 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
31 import com.android.ims.rcs.uce.util.NetworkSipCode;
32 import com.android.ims.rcs.uce.util.UceUtils;
33 
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.Optional;
42 import java.util.Set;
43 import java.util.stream.Collectors;
44 
45 /**
46  * The container of the result of the capabilities request.
47  */
48 public class CapabilityRequestResponse {
49 
50     private static final String LOG_TAG = UceUtils.getLogPrefix() + "CapabilityRequestResp";
51 
52     // The error code when the request encounters internal errors.
53     private @ErrorCode Optional<Integer> mRequestInternalError;
54 
55     // The command error code of the request. It is assigned by the callback "onCommandError"
56     private @CommandCode Optional<Integer> mCommandError;
57 
58     // The SIP code and reason of the network response.
59     private Optional<Integer> mNetworkRespSipCode;
60     private Optional<String> mReasonPhrase;
61 
62     // The SIP code and the phrase read from the reason header
63     private Optional<Integer> mReasonHeaderCause;
64     private Optional<String> mReasonHeaderText;
65 
66     // The reason why the this request was terminated and how long after it can be retried.
67     // This value is assigned by the callback "onTerminated"
68     private Optional<String> mTerminatedReason;
69     private Optional<Long> mRetryAfterMillis;
70 
71     // The list of the valid capabilities which is retrieved from the cache.
72     private List<RcsContactUceCapability> mCachedCapabilityList;
73 
74     // The list of the updated capabilities. This is assigned by the callback
75     // "onNotifyCapabilitiesUpdate"
76     private List<RcsContactUceCapability> mUpdatedCapabilityList;
77 
78     // The list of the terminated resource. This is assigned by the callback
79     // "onResourceTerminated"
80     private List<RcsContactUceCapability> mTerminatedResource;
81 
82     // The list of the remote contact's capability.
83     private Set<String> mRemoteCaps;
84 
85     // The collection to record whether the request contacts have received the capabilities updated.
86     private Map<Uri, Boolean> mContactCapsReceived;
87 
CapabilityRequestResponse()88     public CapabilityRequestResponse() {
89         mRequestInternalError = Optional.empty();
90         mCommandError = Optional.empty();
91         mNetworkRespSipCode = Optional.empty();
92         mReasonPhrase = Optional.empty();
93         mReasonHeaderCause = Optional.empty();
94         mReasonHeaderText = Optional.empty();
95         mTerminatedReason = Optional.empty();
96         mRetryAfterMillis = Optional.of(0L);
97         mTerminatedResource = new ArrayList<>();
98         mCachedCapabilityList = new ArrayList<>();
99         mUpdatedCapabilityList = new ArrayList<>();
100         mRemoteCaps = new HashSet<>();
101         mContactCapsReceived = new HashMap<>();
102     }
103 
104     /**
105      * Set the request contacts which is expected to receive the capabilities updated.
106      */
setRequestContacts(List<Uri> contactUris)107     public synchronized void setRequestContacts(List<Uri> contactUris) {
108         // Initialize the default value to FALSE. All the numbers have not received the
109         // capabilities updated.
110         contactUris.forEach(contact -> mContactCapsReceived.put(contact, Boolean.FALSE));
111         Log.d(LOG_TAG, "setRequestContacts: size=" + mContactCapsReceived.size());
112     }
113 
114     /**
115      * Get the contacts that have not received the capabilities updated yet.
116      */
getNotReceiveCapabilityUpdatedContact()117     public synchronized List<Uri> getNotReceiveCapabilityUpdatedContact() {
118         return mContactCapsReceived.entrySet()
119                 .stream()
120                 .filter(entry -> Objects.equals(entry.getValue(), Boolean.FALSE))
121                 .map(Map.Entry::getKey)
122                 .collect(Collectors.toList());
123     }
124 
125     /**
126      * Set the request contacts which is expected to receive the capabilities updated.
127      */
haveAllRequestCapsUpdatedBeenReceived()128     public synchronized boolean haveAllRequestCapsUpdatedBeenReceived() {
129         return !(mContactCapsReceived.containsValue(Boolean.FALSE));
130     }
131 
132     /**
133      * Set the error code when the request encounters internal unexpected errors.
134      * @param errorCode the error code of the internal request error.
135      */
setRequestInternalError(@rrorCode int errorCode)136     public synchronized void setRequestInternalError(@ErrorCode int errorCode) {
137         mRequestInternalError = Optional.of(errorCode);
138     }
139 
140     /**
141      * Get the request internal error code.
142      */
getRequestInternalError()143     public synchronized Optional<Integer> getRequestInternalError() {
144         return mRequestInternalError;
145     }
146 
147     /**
148      * Set the command error code which is sent from ImsService and set the capability error code.
149      */
setCommandError(@ommandCode int commandError)150     public synchronized void setCommandError(@CommandCode int commandError) {
151         mCommandError = Optional.of(commandError);
152     }
153 
154     /**
155      * Get the command error codeof this request.
156      */
getCommandError()157     public synchronized Optional<Integer> getCommandError() {
158         return mCommandError;
159     }
160 
161     /**
162      * Set the network response of this request which is sent by the network.
163      */
setNetworkResponseCode(int sipCode, String reason)164     public synchronized void setNetworkResponseCode(int sipCode, String reason) {
165         mNetworkRespSipCode = Optional.of(sipCode);
166         mReasonPhrase = Optional.ofNullable(reason);
167     }
168 
169     /**
170      * Set the network response of this request which is sent by the network.
171      */
setNetworkResponseCode(int sipCode, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText)172     public synchronized void setNetworkResponseCode(int sipCode, String reasonPhrase,
173             int reasonHeaderCause, String reasonHeaderText) {
174         mNetworkRespSipCode = Optional.of(sipCode);
175         mReasonPhrase = Optional.ofNullable(reasonPhrase);
176         mReasonHeaderCause = Optional.of(reasonHeaderCause);
177         mReasonHeaderText = Optional.ofNullable(reasonHeaderText);
178     }
179 
180     // Get the sip code of the network response.
getNetworkRespSipCode()181     public synchronized Optional<Integer> getNetworkRespSipCode() {
182         return mNetworkRespSipCode;
183     }
184 
185     // Get the reason of the network response.
getReasonPhrase()186     public synchronized Optional<String> getReasonPhrase() {
187         return mReasonPhrase;
188     }
189 
190     // Get the response sip code from the reason header.
getReasonHeaderCause()191     public synchronized Optional<Integer> getReasonHeaderCause() {
192         return mReasonHeaderCause;
193     }
194 
195     // Get the response phrae from the reason header.
getReasonHeaderText()196     public synchronized Optional<String> getReasonHeaderText() {
197         return mReasonHeaderText;
198     }
199 
getResponseSipCode()200     public Optional<Integer> getResponseSipCode() {
201         if (mReasonHeaderCause.isPresent()) {
202             return mReasonHeaderCause;
203         } else {
204             return mNetworkRespSipCode;
205         }
206     }
207 
getResponseReason()208     public Optional<String> getResponseReason() {
209         if (mReasonPhrase.isPresent()) {
210             return mReasonPhrase;
211         } else {
212             return mReasonHeaderText;
213         }
214     }
215 
216     /**
217      * Set the reason and retry-after info when the callback onTerminated is called.
218      * @param reason The reason why this request is terminated.
219      * @param retryAfterMillis How long to wait before retry this request.
220      */
setTerminated(String reason, long retryAfterMillis)221     public synchronized void setTerminated(String reason, long retryAfterMillis) {
222         mTerminatedReason = Optional.ofNullable(reason);
223         mRetryAfterMillis = Optional.of(retryAfterMillis);
224     }
225 
226     /**
227      * @return The reason of terminating the subscription request. empty string if it has not
228      * been given.
229      */
getTerminatedReason()230     public synchronized String getTerminatedReason() {
231         return mTerminatedReason.orElse("");
232     }
233 
234     /**
235      * @return Return the retryAfterMillis, 0L if the value is not present.
236      */
getRetryAfterMillis()237     public synchronized long getRetryAfterMillis() {
238         return mRetryAfterMillis.orElse(0L);
239     }
240 
241     /**
242      * Add the capabilities which are retrieved from the cache.
243      */
addCachedCapabilities(List<RcsContactUceCapability> capabilityList)244     public synchronized void addCachedCapabilities(List<RcsContactUceCapability> capabilityList) {
245         mCachedCapabilityList.addAll(capabilityList);
246 
247         // Update the flag to indicate that these contacts have received the capabilities updated.
248         updateCapsReceivedFlag(capabilityList);
249     }
250 
251     /**
252      * Update the flag to indicate that the given contacts have received the capabilities updated.
253      */
updateCapsReceivedFlag(List<RcsContactUceCapability> updatedCapList)254     private synchronized void updateCapsReceivedFlag(List<RcsContactUceCapability> updatedCapList) {
255         for (RcsContactUceCapability updatedCap : updatedCapList) {
256             Uri updatedUri = updatedCap.getContactUri();
257             if (updatedUri == null) continue;
258             String updatedUriStr = updatedUri.toString();
259 
260             for (Map.Entry<Uri, Boolean> contactCapEntry : mContactCapsReceived.entrySet()) {
261                 String number = UceUtils.getContactNumber(contactCapEntry.getKey());
262                 if (!TextUtils.isEmpty(number) && updatedUriStr.contains(number)) {
263                     // Set the flag that this contact has received the capability updated.
264                     contactCapEntry.setValue(true);
265                 }
266             }
267         }
268     }
269 
270     /**
271      * Clear the cached capabilities when the cached capabilities have been sent to client.
272      */
removeCachedContactCapabilities()273     public synchronized void removeCachedContactCapabilities() {
274         mCachedCapabilityList.clear();
275     }
276 
277     /**
278      * @return the cached capabilities.
279      */
getCachedContactCapability()280     public synchronized List<RcsContactUceCapability> getCachedContactCapability() {
281         return Collections.unmodifiableList(mCachedCapabilityList);
282     }
283 
284     /**
285      * Add the updated contact capabilities which sent from ImsService.
286      */
addUpdatedCapabilities(List<RcsContactUceCapability> capabilityList)287     public synchronized void addUpdatedCapabilities(List<RcsContactUceCapability> capabilityList) {
288         mUpdatedCapabilityList.addAll(capabilityList);
289 
290         // Update the flag to indicate that these contacts have received the capabilities updated.
291         updateCapsReceivedFlag(capabilityList);
292     }
293 
294     /**
295      * Remove the given capabilities from the UpdatedCapabilityList when these capabilities have
296      * updated to the requester.
297      */
removeUpdatedCapabilities(List<RcsContactUceCapability> capList)298     public synchronized void removeUpdatedCapabilities(List<RcsContactUceCapability> capList) {
299         mUpdatedCapabilityList.removeAll(capList);
300     }
301 
302     /**
303      * Get all the updated capabilities to trigger the capability receive callback.
304      */
getUpdatedContactCapability()305     public synchronized List<RcsContactUceCapability> getUpdatedContactCapability() {
306         return Collections.unmodifiableList(mUpdatedCapabilityList);
307     }
308 
309     /**
310      * Add the terminated resources which sent from ImsService.
311      */
addTerminatedResource(List<RcsContactTerminatedReason> resourceList)312     public synchronized void addTerminatedResource(List<RcsContactTerminatedReason> resourceList) {
313         // Convert the RcsContactTerminatedReason to RcsContactUceCapability
314         List<RcsContactUceCapability> capabilityList = resourceList.stream()
315                 .filter(Objects::nonNull)
316                 .map(reason -> PidfParserUtils.getTerminatedCapability(
317                         reason.getContactUri(), reason.getReason())).collect(Collectors.toList());
318 
319         // Save the terminated resource.
320         mTerminatedResource.addAll(capabilityList);
321 
322         // Update the flag to indicate that these contacts have received the capabilities updated.
323         updateCapsReceivedFlag(capabilityList);
324     }
325 
326     /*
327      * Remove the given capabilities from the mTerminatedResource when these capabilities have
328      * updated to the requester.
329      */
removeTerminatedResources(List<RcsContactUceCapability> resourceList)330     public synchronized void removeTerminatedResources(List<RcsContactUceCapability> resourceList) {
331         mTerminatedResource.removeAll(resourceList);
332     }
333 
334     /**
335      * Get the terminated resources which sent from ImsService.
336      */
getTerminatedResources()337     public synchronized List<RcsContactUceCapability> getTerminatedResources() {
338         return Collections.unmodifiableList(mTerminatedResource);
339     }
340 
341     /**
342      * Set the remote's capabilities which are sent from the network.
343      */
setRemoteCapabilities(Set<String> remoteCaps)344     public synchronized void setRemoteCapabilities(Set<String> remoteCaps) {
345         if (remoteCaps != null) {
346             remoteCaps.stream().filter(Objects::nonNull).forEach(capability ->
347                     mRemoteCaps.add(capability));
348         }
349     }
350 
351     /**
352      * Get the remote capability feature tags.
353      */
getRemoteCapability()354     public synchronized Set<String> getRemoteCapability() {
355         return Collections.unmodifiableSet(mRemoteCaps);
356     }
357 
358     /**
359      * Check if the network response is success.
360      * @return true if the network response code is OK or Accepted and the Reason header cause
361      * is either not present or OK.
362      */
isNetworkResponseOK()363     public synchronized boolean isNetworkResponseOK() {
364         final int sipCodeOk = NetworkSipCode.SIP_CODE_OK;
365         final int sipCodeAccepted = NetworkSipCode.SIP_CODE_ACCEPTED;
366         Optional<Integer> respSipCode = getNetworkRespSipCode();
367         if (respSipCode.filter(c -> (c == sipCodeOk || c == sipCodeAccepted)).isPresent()
368                 && (!getReasonHeaderCause().isPresent()
369                         || getReasonHeaderCause().filter(c -> c == sipCodeOk).isPresent())) {
370             return true;
371         }
372         return false;
373     }
374 
375     /**
376      * Check whether the request is forbidden or not.
377      * @return true if the Reason header sip code is 403(Forbidden) or the response sip code is 403.
378      */
isRequestForbidden()379     public synchronized boolean isRequestForbidden() {
380         final int sipCodeForbidden = NetworkSipCode.SIP_CODE_FORBIDDEN;
381         if (getReasonHeaderCause().isPresent()) {
382             return getReasonHeaderCause().filter(c -> c == sipCodeForbidden).isPresent();
383         } else {
384             return getNetworkRespSipCode().filter(c -> c == sipCodeForbidden).isPresent();
385         }
386     }
387 
388     /**
389      * Check the contacts of the request is not found.
390      * @return true if the sip code of the network response is one of NOT_FOUND(404),
391      * SIP_CODE_METHOD_NOT_ALLOWED(405) or DOES_NOT_EXIST_ANYWHERE(604)
392      */
isNotFound()393     public synchronized boolean isNotFound() {
394         Optional<Integer> respSipCode = Optional.empty();
395         if (getReasonHeaderCause().isPresent()) {
396             respSipCode = getReasonHeaderCause();
397         } else if (getNetworkRespSipCode().isPresent()) {
398             respSipCode = getNetworkRespSipCode();
399         }
400 
401         if (respSipCode.isPresent()) {
402             int sipCode = respSipCode.get();
403             if (sipCode == NetworkSipCode.SIP_CODE_NOT_FOUND ||
404             sipCode == NetworkSipCode.SIP_CODE_METHOD_NOT_ALLOWED ||
405             sipCode == NetworkSipCode.SIP_CODE_DOES_NOT_EXIST_ANYWHERE) {
406                 return true;
407             }
408         }
409         return false;
410     }
411 
412     /**
413      * This method convert from the command error code which are defined in the
414      * RcsCapabilityExchangeImplBase to the Capabilities error code which are defined in the
415      * RcsUceAdapter.
416      */
getCapabilityErrorFromCommandError(@ommandCode int cmdError)417     public static int getCapabilityErrorFromCommandError(@CommandCode int cmdError) {
418         int uceError;
419         switch (cmdError) {
420             case RcsCapabilityExchangeImplBase.COMMAND_CODE_SERVICE_UNKNOWN:
421             case RcsCapabilityExchangeImplBase.COMMAND_CODE_GENERIC_FAILURE:
422             case RcsCapabilityExchangeImplBase.COMMAND_CODE_INVALID_PARAM:
423             case RcsCapabilityExchangeImplBase.COMMAND_CODE_FETCH_ERROR:
424             case RcsCapabilityExchangeImplBase.COMMAND_CODE_NOT_SUPPORTED:
425             case RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE:
426                 uceError = RcsUceAdapter.ERROR_GENERIC_FAILURE;
427                 break;
428             case RcsCapabilityExchangeImplBase.COMMAND_CODE_NOT_FOUND:
429                 uceError = RcsUceAdapter.ERROR_NOT_FOUND;
430                 break;
431             case RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT:
432                 uceError = RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
433                 break;
434             case RcsCapabilityExchangeImplBase.COMMAND_CODE_INSUFFICIENT_MEMORY:
435                 uceError = RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
436                 break;
437             case RcsCapabilityExchangeImplBase.COMMAND_CODE_LOST_NETWORK_CONNECTION:
438                 uceError = RcsUceAdapter.ERROR_LOST_NETWORK;
439                 break;
440             case RcsCapabilityExchangeImplBase.COMMAND_CODE_SERVICE_UNAVAILABLE:
441                 uceError = RcsUceAdapter.ERROR_SERVER_UNAVAILABLE;
442                 break;
443             default:
444                 uceError = RcsUceAdapter.ERROR_GENERIC_FAILURE;
445                 break;
446         }
447         return uceError;
448     }
449 
450     /**
451      * Convert the SIP error code which sent by ImsService to the capability error code.
452      */
getCapabilityErrorFromSipCode(CapabilityRequestResponse response)453     public static int getCapabilityErrorFromSipCode(CapabilityRequestResponse response) {
454         int sipError;
455         String respReason;
456         // Check the sip code in the Reason header first if the Reason Header is present.
457         if (response.getReasonHeaderCause().isPresent()) {
458             sipError = response.getReasonHeaderCause().get();
459             respReason = response.getReasonHeaderText().orElse("");
460         } else {
461             sipError = response.getNetworkRespSipCode().orElse(-1);
462             respReason = response.getReasonPhrase().orElse("");
463         }
464         return NetworkSipCode.getCapabilityErrorFromSipCode(sipError, respReason,
465                 UceController.REQUEST_TYPE_CAPABILITY);
466     }
467 
468     @Override
toString()469     public synchronized String toString() {
470         StringBuilder builder = new StringBuilder();
471         return builder.append("RequestInternalError=").append(mRequestInternalError.orElse(-1))
472                 .append(", CommandErrorCode=").append(mCommandError.orElse(-1))
473                 .append(", NetworkResponseCode=").append(mNetworkRespSipCode.orElse(-1))
474                 .append(", NetworkResponseReason=").append(mReasonPhrase.orElse(""))
475                 .append(", ReasonHeaderCause=").append(mReasonHeaderCause.orElse(-1))
476                 .append(", ReasonHeaderText=").append(mReasonHeaderText.orElse(""))
477                 .append(", TerminatedReason=").append(mTerminatedReason.orElse(""))
478                 .append(", RetryAfterMillis=").append(mRetryAfterMillis.orElse(0L))
479                 .append(", Terminated resource size=" + mTerminatedResource.size())
480                 .append(", cached capability size=" + mCachedCapabilityList.size())
481                 .append(", Updated capability size=" + mUpdatedCapabilityList.size())
482                 .append(", RemoteCaps size=" + mRemoteCaps.size())
483                 .toString();
484     }
485 }
486