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