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