• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_GENERIC_FAILURE;
20 
21 import android.annotation.Nullable;
22 import android.net.Uri;
23 import android.os.RemoteException;
24 import android.telephony.ims.RcsContactUceCapability;
25 import android.telephony.ims.RcsUceAdapter;
26 import android.telephony.ims.SipDetails;
27 import android.telephony.ims.aidl.IRcsUceControllerCallback;
28 
29 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
30 import com.android.ims.rcs.uce.eab.EabCapabilityResult;
31 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
32 import com.android.ims.rcs.uce.request.SubscriptionTerminatedHelper.TerminatedResult;
33 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
34 import com.android.ims.rcs.uce.UceStatsWriter;
35 import com.android.internal.annotations.VisibleForTesting;
36 
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Comparator;
40 import java.util.List;
41 import java.util.Optional;
42 import java.util.stream.Collectors;
43 
44 /**
45  * Responsible for the communication and interaction between SubscribeRequests and triggering
46  * the callback to notify the result of the capabilities request.
47  */
48 public class SubscribeRequestCoordinator extends UceRequestCoordinator {
49     /**
50      * The builder of the SubscribeRequestCoordinator.
51      */
52     public static final class Builder {
53         private SubscribeRequestCoordinator mRequestCoordinator;
54 
55         /**
56          * The builder of the SubscribeRequestCoordinator class.
57          */
Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c)58         public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c) {
59             mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c,
60                     UceStatsWriter.getInstance());
61         }
62         @VisibleForTesting
Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c, UceStatsWriter instance)63         public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c,
64                 UceStatsWriter instance) {
65             mRequestCoordinator = new SubscribeRequestCoordinator(subId, requests, c, instance);
66         }
67 
68         /**
69          * Set the callback to receive the request updated.
70          */
setCapabilitiesCallback(IRcsUceControllerCallback callback)71         public Builder setCapabilitiesCallback(IRcsUceControllerCallback callback) {
72             mRequestCoordinator.setCapabilitiesCallback(callback);
73             return this;
74         }
75 
76         /**
77          * Get the SubscribeRequestCoordinator instance.
78          */
build()79         public SubscribeRequestCoordinator build() {
80             return mRequestCoordinator;
81         }
82     }
83 
84     /**
85      * Different request updated events will create different {@link RequestResult}. Define the
86      * interface to get the {@link RequestResult} instance according to the given task ID and
87      * {@link CapabilityRequestResponse}.
88      */
89     @FunctionalInterface
90     private interface RequestResultCreator {
createRequestResult(long taskId, CapabilityRequestResponse response, RequestManagerCallback requestMgrCallback)91         RequestResult createRequestResult(long taskId, CapabilityRequestResponse response,
92                 RequestManagerCallback requestMgrCallback);
93     }
94 
95     // The RequestResult creator of the request error.
96     private static final RequestResultCreator sRequestErrorCreator = (taskId, response,
97             requestMgrCallback) -> {
98         int errorCode = response.getRequestInternalError().orElse(DEFAULT_ERROR_CODE);
99         long retryAfter = response.getRetryAfterMillis();
100         return RequestResult.createFailedResult(taskId, errorCode, retryAfter);
101     };
102 
103     // The RequestResult creator of the command error.
104     private static final RequestResultCreator sCommandErrorCreator = (taskId, response,
105             requestMgrCallback) -> {
106         int cmdError = response.getCommandError().orElse(COMMAND_CODE_GENERIC_FAILURE);
107         int errorCode = CapabilityRequestResponse.getCapabilityErrorFromCommandError(cmdError);
108         long retryAfter = response.getRetryAfterMillis();
109         return RequestResult.createFailedResult(taskId, errorCode, retryAfter);
110     };
111 
112     // The RequestResult creator of the network response error.
113     private static final RequestResultCreator sNetworkRespErrorCreator = (taskId, response,
114             requestMgrCallback) -> {
115         DeviceStateResult deviceState = requestMgrCallback.getDeviceState();
116         SipDetails details = response.getSipDetails().orElse(null);
117         if (deviceState.isRequestForbidden()) {
118             int errorCode = deviceState.getErrorCode().orElse(RcsUceAdapter.ERROR_FORBIDDEN);
119             long retryAfter = deviceState.getRequestRetryAfterMillis();
120             return RequestResult.createFailedResult(taskId, errorCode, retryAfter, details);
121         } else {
122             int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response);
123             long retryAfter = response.getRetryAfterMillis();
124             return RequestResult.createFailedResult(taskId, errorCode, retryAfter, details);
125         }
126     };
127 
128     // The RequestResult creator of the network response is not 200 OK, however, we can to treat
129     // it as a successful result and finish the request
130     private static final RequestResultCreator sNetworkRespSuccessfulCreator = (taskId, response,
131             requestMgrCallback) -> {
132         SipDetails detail = response.getSipDetails().orElse(null);
133         return RequestResult.createSuccessResult(taskId, detail);
134     };
135 
136     // The RequestResult creator of the request terminated.
137     private static final RequestResultCreator sTerminatedCreator = (taskId, response,
138             requestMgrCallback) -> {
139         // Check the given terminated reason to determine whether clients should retry or not.
140         TerminatedResult terminatedResult = SubscriptionTerminatedHelper.getAnalysisResult(
141                 response.getTerminatedReason(), response.getRetryAfterMillis(),
142                 response.haveAllRequestCapsUpdatedBeenReceived());
143         SipDetails details = response.getSipDetails().orElse(null);
144         if (terminatedResult.getErrorCode().isPresent()) {
145             // If the terminated error code is present, it means that the request is failed.
146             int errorCode = terminatedResult.getErrorCode().get();
147             long terminatedRetry = terminatedResult.getRetryAfterMillis();
148             return RequestResult.createFailedResult(taskId, errorCode, terminatedRetry);
149         } else if (!response.isNetworkResponseOK() || response.getRetryAfterMillis() > 0L) {
150             // If the network response is failed or the retryAfter is not 0, this request is failed.
151             long retryAfterMillis = response.getRetryAfterMillis();
152             int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response);
153             return RequestResult.createFailedResult(taskId, errorCode, retryAfterMillis, details);
154         } else {
155             return RequestResult.createSuccessResult(taskId, details);
156         }
157     };
158 
159     // The RequestResult creator for does not need to request from the network.
160     private static final RequestResultCreator sNotNeedRequestFromNetworkCreator =
161             (taskId, response, requestMgrCallback) -> RequestResult.createSuccessResult(taskId);
162 
163     // The RequestResult creator of the request timeout.
164     private static final RequestResultCreator sRequestTimeoutCreator =
165             (taskId, response, requestMgrCallback) -> RequestResult.createFailedResult(taskId,
166                     RcsUceAdapter.ERROR_REQUEST_TIMEOUT, 0L);
167 
168     // The callback to notify the result of the capabilities request.
169     private volatile IRcsUceControllerCallback mCapabilitiesCallback;
170 
171     private final UceStatsWriter mUceStatsWriter;
172 
SubscribeRequestCoordinator(int subId, Collection<UceRequest> requests, RequestManagerCallback requestMgrCallback, UceStatsWriter instance)173     private SubscribeRequestCoordinator(int subId, Collection<UceRequest> requests,
174             RequestManagerCallback requestMgrCallback, UceStatsWriter instance) {
175         super(subId, requests, requestMgrCallback);
176         mUceStatsWriter = instance;
177         logd("SubscribeRequestCoordinator: created");
178     }
179 
setCapabilitiesCallback(IRcsUceControllerCallback callback)180     private void setCapabilitiesCallback(IRcsUceControllerCallback callback) {
181         mCapabilitiesCallback = callback;
182     }
183 
184     @Override
onFinish()185     public void onFinish() {
186         logd("SubscribeRequestCoordinator: onFinish");
187         mCapabilitiesCallback = null;
188         super.onFinish();
189     }
190 
191     @Override
onRequestUpdated(long taskId, @UceRequestUpdate int event)192     public void onRequestUpdated(long taskId, @UceRequestUpdate int event) {
193         if (mIsFinished) return;
194         SubscribeRequest request = (SubscribeRequest) getUceRequest(taskId);
195         if (request == null) {
196             logw("onRequestUpdated: Cannot find SubscribeRequest taskId=" + taskId);
197             return;
198         }
199 
200         logd("onRequestUpdated(SubscribeRequest): taskId=" + taskId + ", event=" +
201                 REQUEST_EVENT_DESC.get(event));
202 
203         switch (event) {
204             case REQUEST_UPDATE_ERROR:
205                 handleRequestError(request);
206                 break;
207             case REQUEST_UPDATE_COMMAND_ERROR:
208                 handleCommandError(request);
209                 break;
210             case REQUEST_UPDATE_NETWORK_RESPONSE:
211                 handleNetworkResponse(request);
212                 break;
213             case REQUEST_UPDATE_CAPABILITY_UPDATE:
214                 handleCapabilitiesUpdated(request);
215                 break;
216             case REQUEST_UPDATE_RESOURCE_TERMINATED:
217                 handleResourceTerminated(request);
218                 break;
219             case REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE:
220                 handleCachedCapabilityUpdated(request);
221                 break;
222             case REQUEST_UPDATE_TERMINATED:
223                 handleTerminated(request);
224                 break;
225             case REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK:
226                 handleNoNeedRequestFromNetwork(request);
227                 break;
228             case REQUEST_UPDATE_TIMEOUT:
229                 handleRequestTimeout(request);
230                 break;
231             default:
232                 logw("onRequestUpdated(SubscribeRequest): invalid event " + event);
233                 break;
234         }
235 
236         // End this instance if all the UceRequests in the coordinator are finished.
237         checkAndFinishRequestCoordinator();
238     }
239 
240     /**
241      * Finish the SubscribeRequest because it has encountered error.
242      */
handleRequestError(SubscribeRequest request)243     private void handleRequestError(SubscribeRequest request) {
244         CapabilityRequestResponse response = request.getRequestResponse();
245         logd("handleRequestError: " + request.toString());
246 
247         // Finish this request.
248         request.onFinish();
249 
250         // Remove this request from the activated collection and notify RequestManager.
251         Long taskId = request.getTaskId();
252         RequestResult requestResult = sRequestErrorCreator.createRequestResult(taskId, response,
253                 mRequestManagerCallback);
254         moveRequestToFinishedCollection(taskId, requestResult);
255     }
256 
257     /**
258      * This method is called when the given SubscribeRequest received the onCommandError callback
259      * from the ImsService.
260      */
handleCommandError(SubscribeRequest request)261     private void handleCommandError(SubscribeRequest request) {
262         CapabilityRequestResponse response = request.getRequestResponse();
263         logd("handleCommandError: " + request.toString());
264 
265         // Finish this request.
266         request.onFinish();
267 
268         int commandErrorCode = response.getCommandError().orElse(0);
269         mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.SUBSCRIBE_EVENT,
270             false, commandErrorCode, 0);
271 
272         // Remove this request from the activated collection and notify RequestManager.
273         Long taskId = request.getTaskId();
274         RequestResult requestResult = sCommandErrorCreator.createRequestResult(taskId, response,
275                 mRequestManagerCallback);
276         moveRequestToFinishedCollection(taskId, requestResult);
277     }
278 
279     /**
280      * This method is called when the given SubscribeRequest received the onNetworkResponse
281      * callback from the ImsService.
282      */
handleNetworkResponse(SubscribeRequest request)283     private void handleNetworkResponse(SubscribeRequest request) {
284         CapabilityRequestResponse response = request.getRequestResponse();
285         logd("handleNetworkResponse: " + response.toString());
286 
287         int respCode = response.getNetworkRespSipCode().orElse(0);
288         mUceStatsWriter.setSubscribeResponse(mSubId, request.getTaskId(), respCode);
289 
290         // Refresh the device state with the request result.
291         response.getResponseSipCode().ifPresent(sipCode -> {
292             String reason = response.getResponseReason().orElse("");
293             mRequestManagerCallback.refreshDeviceState(sipCode, reason);
294         });
295 
296         // When the network response is unsuccessful, there is no subsequent callback for this
297         // request. Check the forbidden state and finish this request. Otherwise, keep waiting for
298         // the subsequent callback of this request.
299         if (!response.isNetworkResponseOK()) {
300             // Handle the network response not OK cases and get the request result to finish this
301             // request.
302             RequestResult requestResult = handleNetworkResponseFailed(request);
303 
304             // Trigger capabilities updated callback if there is any.
305             List<RcsContactUceCapability> updatedCapList = response.getUpdatedContactCapability();
306             if (!updatedCapList.isEmpty()) {
307                 if (response.isNotFound()) {
308                     mRequestManagerCallback.saveCapabilities(updatedCapList);
309                 }
310                 triggerCapabilitiesReceivedCallback(updatedCapList);
311                 response.removeUpdatedCapabilities(updatedCapList);
312             }
313 
314             // Finish this request.
315             request.onFinish();
316 
317             // Remove this request from the activated collection and notify RequestManager.
318             moveRequestToFinishedCollection(request.getTaskId(), requestResult);
319         }
320     }
321 
handleNetworkResponseFailed(SubscribeRequest request)322     private RequestResult handleNetworkResponseFailed(SubscribeRequest request) {
323         final long taskId = request.getTaskId();
324         final CapabilityRequestResponse response = request.getRequestResponse();
325         final List<Uri> requestUris = response.getNotReceiveCapabilityUpdatedContact();
326         RequestResult requestResult = null;
327 
328         if (response.isNotFound()) {
329             // In the network response with the not found case, we won't receive the capabilities
330             // updated callback from the ImsService afterward. Therefore, we create the capabilities
331             // with the result REQUEST_RESULT_NOT_FOUND by ourself and will trigger the
332             // capabilities received callback to the clients later.
333             List<RcsContactUceCapability> capabilityList = requestUris.stream().map(uri ->
334                     PidfParserUtils.getNotFoundContactCapabilities(uri))
335                     .collect(Collectors.toList());
336             response.addUpdatedCapabilities(capabilityList);
337 
338             // We treat the NOT FOUND is a successful result.
339             requestResult = sNetworkRespSuccessfulCreator.createRequestResult(taskId, response,
340                     mRequestManagerCallback);
341         } else {
342             // The request result is unsuccessful and it's not the NOT FOUND error. we need to get
343             // the capabilities from the cache.
344             List<RcsContactUceCapability> capabilitiesList =
345                     getCapabilitiesFromCacheIncludingExpired(requestUris);
346             response.addUpdatedCapabilities(capabilitiesList);
347 
348             // Add to the throttling list for the inconclusive result of the contacts.
349             mRequestManagerCallback.addToThrottlingList(requestUris,
350                     response.getResponseSipCode().orElse(
351                             com.android.ims.rcs.uce.util.NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT));
352 
353             requestResult = sNetworkRespErrorCreator.createRequestResult(taskId, response,
354                     mRequestManagerCallback);
355         }
356         return requestResult;
357     }
358 
359     /**
360      * Get the contact capabilities from the cache even if the capabilities have expired. If the
361      * capabilities doesn't exist, create the non-RCS capabilities instead.
362      * @param uris the uris to get the capabilities from cache.
363      * @return The contact capabilities for the given uris.
364      */
getCapabilitiesFromCacheIncludingExpired(List<Uri> uris)365     private List<RcsContactUceCapability> getCapabilitiesFromCacheIncludingExpired(List<Uri> uris) {
366         List<RcsContactUceCapability> resultList = new ArrayList<>();
367         List<RcsContactUceCapability> notFoundFromCacheList = new ArrayList<>();
368 
369         // Get the capabilities from the cache.
370         List<EabCapabilityResult> eabResultList =
371                 mRequestManagerCallback.getCapabilitiesFromCacheIncludingExpired(uris);
372 
373         eabResultList.forEach(eabResult -> {
374             if (eabResult.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL ||
375                 eabResult.getStatus() == EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE) {
376                 // The capabilities are found, add to the result list
377                 resultList.add(eabResult.getContactCapabilities());
378             } else {
379                 // Cannot get the capabilities from cache, create the non-RCS capabilities instead.
380                 notFoundFromCacheList.add(PidfParserUtils.getNotFoundContactCapabilities(
381                         eabResult.getContact()));
382             }
383         });
384 
385         if (!notFoundFromCacheList.isEmpty()) {
386             resultList.addAll(notFoundFromCacheList);
387         }
388 
389         logd("getCapabilitiesFromCacheIncludingExpired: requesting uris size=" + uris.size() +
390                 ", capabilities not found from cache size=" + notFoundFromCacheList.size());
391         return resultList;
392     }
393 
394     /**
395      * This method is called when the given SubscribeRequest received the onNotifyCapabilitiesUpdate
396      * callback from the ImsService.
397      */
handleCapabilitiesUpdated(SubscribeRequest request)398     private void handleCapabilitiesUpdated(SubscribeRequest request) {
399         CapabilityRequestResponse response = request.getRequestResponse();
400         Long taskId = request.getTaskId();
401         List<RcsContactUceCapability> updatedCapList = response.getUpdatedContactCapability();
402         logd("handleCapabilitiesUpdated: taskId=" + taskId + ", size=" + updatedCapList.size());
403 
404         if (updatedCapList.isEmpty()) {
405             return;
406         }
407 
408         mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, updatedCapList);
409         // Save the updated capabilities to the cache.
410         mRequestManagerCallback.saveCapabilities(updatedCapList);
411 
412         // Trigger the capabilities updated callback and remove the given capabilities that have
413         // executed the callback onCapabilitiesReceived.
414         triggerCapabilitiesReceivedCallback(updatedCapList);
415         response.removeUpdatedCapabilities(updatedCapList);
416     }
417 
418     /**
419      * This method is called when the given SubscribeRequest received the onResourceTerminated
420      * callback from the ImsService.
421      */
handleResourceTerminated(SubscribeRequest request)422     private void handleResourceTerminated(SubscribeRequest request) {
423         CapabilityRequestResponse response = request.getRequestResponse();
424         Long taskId = request.getTaskId();
425         List<RcsContactUceCapability> terminatedResources = response.getTerminatedResources();
426         logd("handleResourceTerminated: taskId=" + taskId + ", size=" + terminatedResources.size());
427 
428         if (terminatedResources.isEmpty()) {
429             return;
430         }
431 
432         mUceStatsWriter.setPresenceNotifyEvent(mSubId, taskId, terminatedResources);
433 
434         // Save the terminated capabilities to the cache.
435         mRequestManagerCallback.saveCapabilities(terminatedResources);
436 
437         // Trigger the capabilities updated callback and remove the given capabilities from the
438         // resource terminated list.
439         triggerCapabilitiesReceivedCallback(terminatedResources);
440         response.removeTerminatedResources(terminatedResources);
441     }
442 
443     /**
444      * This method is called when the given SubscribeRequest retrieve the cached capabilities.
445      */
handleCachedCapabilityUpdated(SubscribeRequest request)446     private void handleCachedCapabilityUpdated(SubscribeRequest request) {
447         CapabilityRequestResponse response = request.getRequestResponse();
448         Long taskId = request.getTaskId();
449         List<RcsContactUceCapability> cachedCapList = response.getCachedContactCapability();
450         logd("handleCachedCapabilityUpdated: taskId=" + taskId + ", size=" + cachedCapList.size());
451 
452         if (cachedCapList.isEmpty()) {
453             return;
454         }
455 
456         // Trigger the capabilities updated callback.
457         triggerCapabilitiesReceivedCallback(cachedCapList);
458         response.removeCachedContactCapabilities();
459     }
460 
461     /**
462      * This method is called when the given SubscribeRequest received the onTerminated callback
463      * from the ImsService.
464      */
handleTerminated(SubscribeRequest request)465     private void handleTerminated(SubscribeRequest request) {
466         CapabilityRequestResponse response = request.getRequestResponse();
467         logd("handleTerminated: " + response.toString());
468 
469         // Finish this request.
470         request.onFinish();
471 
472         // Remove this request from the activated collection and notify RequestManager.
473         Long taskId = request.getTaskId();
474         mUceStatsWriter.setSubscribeTerminated(mSubId, taskId, response.getTerminatedReason());
475         RequestResult requestResult = sTerminatedCreator.createRequestResult(taskId, response,
476                 mRequestManagerCallback);
477         moveRequestToFinishedCollection(taskId, requestResult);
478     }
479 
480     /**
481      * This method is called when all the capabilities can be retrieved from the cached and it does
482      * not need to request capabilities from the network.
483      */
handleNoNeedRequestFromNetwork(SubscribeRequest request)484     private void handleNoNeedRequestFromNetwork(SubscribeRequest request) {
485         CapabilityRequestResponse response = request.getRequestResponse();
486         logd("handleNoNeedRequestFromNetwork: " + response.toString());
487 
488         // Finish this request.
489         request.onFinish();
490 
491         // Remove this request from the activated collection and notify RequestManager.
492         long taskId = request.getTaskId();
493         RequestResult requestResult = sNotNeedRequestFromNetworkCreator.createRequestResult(taskId,
494                 response, mRequestManagerCallback);
495         moveRequestToFinishedCollection(taskId, requestResult);
496     }
497 
498     /**
499      * This method is called when the framework did not receive the capabilities request result.
500      */
handleRequestTimeout(SubscribeRequest request)501     private void handleRequestTimeout(SubscribeRequest request) {
502         CapabilityRequestResponse response = request.getRequestResponse();
503         List<Uri> requestUris = response.getNotReceiveCapabilityUpdatedContact();
504         logd("handleRequestTimeout: " + response);
505         logd("handleRequestTimeout: not received updated uri size=" + requestUris.size());
506 
507         // Add to the throttling list for the inconclusive result of the contacts.
508         mRequestManagerCallback.addToThrottlingList(requestUris,
509                 com.android.ims.rcs.uce.util.NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT);
510 
511         // Get the capabilities from the cache instead and add to the response.
512         List<RcsContactUceCapability> capabilitiesList =
513                 getCapabilitiesFromCacheIncludingExpired(requestUris);
514         response.addUpdatedCapabilities(capabilitiesList);
515 
516         // Trigger capabilities updated callback if there is any.
517         List<RcsContactUceCapability> updatedCapList = response.getUpdatedContactCapability();
518         if (!updatedCapList.isEmpty()) {
519             triggerCapabilitiesReceivedCallback(updatedCapList);
520             response.removeUpdatedCapabilities(updatedCapList);
521         }
522 
523         // Remove this request from the activated collection and notify RequestManager.
524         long taskId = request.getTaskId();
525         RequestResult requestResult = sRequestTimeoutCreator.createRequestResult(taskId,
526                 response, mRequestManagerCallback);
527 
528         // Finish this request
529         request.onFinish();
530 
531         // Remove this request from the activated collection and notify RequestManager.
532         moveRequestToFinishedCollection(taskId, requestResult);
533     }
534 
checkAndFinishRequestCoordinator()535     private void checkAndFinishRequestCoordinator() {
536         synchronized (mCollectionLock) {
537             // Return because there are requests running.
538             if (!mActivatedRequests.isEmpty()) {
539                 return;
540             }
541 
542             // All the requests has finished, find the request which has the max retryAfter time.
543             // If the result is empty, it means all the request are success.
544             Optional<RequestResult> optRequestResult =
545                     mFinishedRequests.values().stream()
546                         .filter(result -> !result.isRequestSuccess())
547                         .max(Comparator.comparingLong(result ->
548                                 result.getRetryMillis().orElse(-1L)));
549 
550             Optional<RequestResult> optDebugInfoResult = mFinishedRequests.values().stream()
551                     .filter(result -> !result.getSipDetails().isEmpty())
552                     .findFirst();
553 
554             SipDetails details = null;
555             if (optDebugInfoResult.isPresent()) {
556                 RequestResult result = optDebugInfoResult.get();
557                 details = result.getSipDetails().orElse(null);
558             }
559             // Trigger the callback
560             if (optRequestResult.isPresent()) {
561                 RequestResult result = optRequestResult.get();
562                 int errorCode = result.getErrorCode().orElse(DEFAULT_ERROR_CODE);
563                 long retryAfter = result.getRetryMillis().orElse(0L);
564                 triggerErrorCallback(errorCode, retryAfter, details);
565             } else {
566                 triggerCompletedCallback(details);
567             }
568 
569             // Notify UceRequestManager to remove this instance from the collection.
570             mRequestManagerCallback.notifyRequestCoordinatorFinished(mCoordinatorId);
571 
572             logd("checkAndFinishRequestCoordinator(SubscribeRequest) done, id=" + mCoordinatorId);
573         }
574     }
575 
576     /**
577      * Trigger the capabilities updated callback.
578      */
triggerCapabilitiesReceivedCallback(List<RcsContactUceCapability> capList)579     private void triggerCapabilitiesReceivedCallback(List<RcsContactUceCapability> capList) {
580         try {
581             logd("triggerCapabilitiesCallback: size=" + capList.size());
582             mCapabilitiesCallback.onCapabilitiesReceived(capList);
583         } catch (RemoteException e) {
584             logw("triggerCapabilitiesCallback exception: " + e);
585         } finally {
586             logd("triggerCapabilitiesCallback: done");
587         }
588     }
589 
590     /**
591      * Trigger the onComplete callback to notify the request is completed.
592      */
triggerCompletedCallback(@ullable SipDetails details)593     private void triggerCompletedCallback(@Nullable SipDetails details) {
594         try {
595             logd("triggerCompletedCallback");
596             mCapabilitiesCallback.onComplete(details);
597         } catch (RemoteException e) {
598             logw("triggerCompletedCallback exception: " + e);
599         } finally {
600             logd("triggerCompletedCallback: done");
601         }
602     }
603 
604     /**
605      * Trigger the onError callback to notify the request is failed.
606      */
triggerErrorCallback(int errorCode, long retryAfterMillis, @Nullable SipDetails details)607     private void triggerErrorCallback(int errorCode, long retryAfterMillis,
608             @Nullable SipDetails details) {
609         try {
610             logd("triggerErrorCallback: errorCode=" + errorCode + ", retry=" + retryAfterMillis);
611             mCapabilitiesCallback.onError(errorCode, retryAfterMillis, details);
612         } catch (RemoteException e) {
613             logw("triggerErrorCallback exception: " + e);
614         } finally {
615             logd("triggerErrorCallback: done");
616         }
617     }
618 
619     @VisibleForTesting
getActivatedRequest()620     public Collection<UceRequest> getActivatedRequest() {
621         return mActivatedRequests.values();
622     }
623 
624     @VisibleForTesting
getFinishedRequest()625     public Collection<RequestResult> getFinishedRequest() {
626         return mFinishedRequests.values();
627     }
628 }
629