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.content.Context; 20 import android.net.Uri; 21 import android.os.Handler; 22 import android.os.Looper; 23 import android.os.Message; 24 import android.os.RemoteException; 25 import android.telephony.TelephonyManager; 26 import android.telephony.ims.RcsContactUceCapability; 27 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; 28 import android.telephony.ims.RcsUceAdapter; 29 import android.telephony.ims.aidl.IOptionsRequestCallback; 30 import android.telephony.ims.aidl.IRcsUceControllerCallback; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import com.android.i18n.phonenumbers.NumberParseException; 35 import com.android.i18n.phonenumbers.PhoneNumberUtil; 36 import com.android.i18n.phonenumbers.Phonenumber; 37 import com.android.ims.rcs.uce.UceController; 38 import com.android.ims.rcs.uce.UceController.UceControllerCallback; 39 import com.android.ims.rcs.uce.UceDeviceState; 40 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; 41 import com.android.ims.rcs.uce.eab.EabCapabilityResult; 42 import com.android.ims.rcs.uce.options.OptionsController; 43 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; 44 import com.android.ims.rcs.uce.request.UceRequest.UceRequestType; 45 import com.android.ims.rcs.uce.request.UceRequestCoordinator.UceRequestUpdate; 46 import com.android.ims.rcs.uce.util.UceUtils; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.os.SomeArgs; 49 50 import java.lang.ref.WeakReference; 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.Objects; 57 import java.util.Optional; 58 import java.util.stream.Collectors; 59 60 /** 61 * Managers the capabilities requests and the availability requests from UceController. 62 */ 63 public class UceRequestManager { 64 65 private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceRequestManager"; 66 67 /** 68 * When enabled, skip the request queue for requests that have numbers with valid cached 69 * capabilities and return that cached info directly. 70 * Note: This also has a CTS test associated with it, so this can not be disabled without 71 * disabling the corresponding RcsUceAdapterTest#testCacheQuerySuccessWhenNetworkBlocked test. 72 */ 73 private static final boolean FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS = true; 74 75 /** 76 * Testing interface used to mock UceUtils in testing. 77 */ 78 @VisibleForTesting 79 public interface UceUtilsProxy { 80 /** 81 * The interface for {@link UceUtils#isPresenceCapExchangeEnabled(Context, int)} used for 82 * testing. 83 */ isPresenceCapExchangeEnabled(Context context, int subId)84 boolean isPresenceCapExchangeEnabled(Context context, int subId); 85 86 /** 87 * The interface for {@link UceUtils#isPresenceSupported(Context, int)} used for testing. 88 */ isPresenceSupported(Context context, int subId)89 boolean isPresenceSupported(Context context, int subId); 90 91 /** 92 * The interface for {@link UceUtils#isSipOptionsSupported(Context, int)} used for testing. 93 */ isSipOptionsSupported(Context context, int subId)94 boolean isSipOptionsSupported(Context context, int subId); 95 96 /** 97 * @return true when the Presence group subscribe is enabled. 98 */ isPresenceGroupSubscribeEnabled(Context context, int subId)99 boolean isPresenceGroupSubscribeEnabled(Context context, int subId); 100 101 /** 102 * Retrieve the maximum number of contacts that can be included in a request. 103 */ getRclMaxNumberEntries(int subId)104 int getRclMaxNumberEntries(int subId); 105 106 /** 107 * @return true if the given phone number is blocked by the network. 108 */ isNumberBlocked(Context context, String phoneNumber)109 boolean isNumberBlocked(Context context, String phoneNumber); 110 } 111 112 private static UceUtilsProxy sUceUtilsProxy = new UceUtilsProxy() { 113 @Override 114 public boolean isPresenceCapExchangeEnabled(Context context, int subId) { 115 return UceUtils.isPresenceCapExchangeEnabled(context, subId); 116 } 117 118 @Override 119 public boolean isPresenceSupported(Context context, int subId) { 120 return UceUtils.isPresenceSupported(context, subId); 121 } 122 123 @Override 124 public boolean isSipOptionsSupported(Context context, int subId) { 125 return UceUtils.isSipOptionsSupported(context, subId); 126 } 127 128 @Override 129 public boolean isPresenceGroupSubscribeEnabled(Context context, int subId) { 130 return UceUtils.isPresenceGroupSubscribeEnabled(context, subId); 131 } 132 133 @Override 134 public int getRclMaxNumberEntries(int subId) { 135 return UceUtils.getRclMaxNumberEntries(subId); 136 } 137 138 @Override 139 public boolean isNumberBlocked(Context context, String phoneNumber) { 140 return UceUtils.isNumberBlocked(context, phoneNumber); 141 } 142 }; 143 144 @VisibleForTesting setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy)145 public void setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy) { 146 sUceUtilsProxy = uceUtilsProxy; 147 } 148 149 /** 150 * The callback interface to receive the request and the result from the UceRequest. 151 */ 152 public interface RequestManagerCallback { 153 /** 154 * Notify sending the UceRequest 155 */ notifySendingRequest(long coordinator, long taskId, long delayTimeMs)156 void notifySendingRequest(long coordinator, long taskId, long delayTimeMs); 157 158 /** 159 * Retrieve the contact capabilities from the cache. 160 */ getCapabilitiesFromCache(List<Uri> uriList)161 List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList); 162 163 /** 164 * Retrieve the contact capabilities from the cache including the expired capabilities. 165 */ getCapabilitiesFromCacheIncludingExpired(List<Uri> uriList)166 List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uriList); 167 168 /** 169 * Retrieve the contact availability from the cache. 170 */ getAvailabilityFromCache(Uri uri)171 EabCapabilityResult getAvailabilityFromCache(Uri uri); 172 173 /** 174 * Retrieve the contact availability from the cache including the expired capabilities. 175 */ getAvailabilityFromCacheIncludingExpired(Uri uri)176 EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri); 177 178 /** 179 * Store the given contact capabilities to the cache. 180 */ saveCapabilities(List<RcsContactUceCapability> contactCapabilities)181 void saveCapabilities(List<RcsContactUceCapability> contactCapabilities); 182 183 /** 184 * Retrieve the device's capabilities. 185 */ getDeviceCapabilities(@apabilityMechanism int capMechanism)186 RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int capMechanism); 187 188 /** 189 * Get the device state to check whether the device is disallowed by the network or not. 190 */ getDeviceState()191 DeviceStateResult getDeviceState(); 192 193 /** 194 * Refresh the device state. It is called when receive the UCE request response. 195 */ refreshDeviceState(int sipCode, String reason)196 void refreshDeviceState(int sipCode, String reason); 197 198 /** 199 * Notify that the UceRequest associated with the given taskId encounters error. 200 */ notifyRequestError(long requestCoordinatorId, long taskId)201 void notifyRequestError(long requestCoordinatorId, long taskId); 202 203 /** 204 * Notify that the UceRequest received the onCommandError callback from the ImsService. 205 */ notifyCommandError(long requestCoordinatorId, long taskId)206 void notifyCommandError(long requestCoordinatorId, long taskId); 207 208 /** 209 * Notify that the UceRequest received the onNetworkResponse callback from the ImsService. 210 */ notifyNetworkResponse(long requestCoordinatorId, long taskId)211 void notifyNetworkResponse(long requestCoordinatorId, long taskId); 212 213 /** 214 * Notify that the UceRequest received the onTerminated callback from the ImsService. 215 */ notifyTerminated(long requestCoordinatorId, long taskId)216 void notifyTerminated(long requestCoordinatorId, long taskId); 217 218 /** 219 * Notify that some contacts are not RCS anymore. It will updated the cached capabilities 220 * and trigger the callback IRcsUceControllerCallback#onCapabilitiesReceived 221 */ notifyResourceTerminated(long requestCoordinatorId, long taskId)222 void notifyResourceTerminated(long requestCoordinatorId, long taskId); 223 224 /** 225 * Notify that the capabilities updates. It will update the cached and trigger the callback 226 * IRcsUceControllerCallback#onCapabilitiesReceived 227 */ notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId)228 void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId); 229 230 /** 231 * Notify that some of the request capabilities can be retrieved from the cached. 232 */ notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId)233 void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId); 234 235 /** 236 * Notify that all the requested capabilities can be retrieved from the cache. It does not 237 * need to request capabilities from the network. 238 */ notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId)239 void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId); 240 241 /** 242 * Notify that the remote options request is done. This is sent by RemoteOptionsRequest and 243 * it will notify the RemoteOptionsCoordinator to handle it. 244 */ notifyRemoteRequestDone(long requestCoordinatorId, long taskId)245 void notifyRemoteRequestDone(long requestCoordinatorId, long taskId); 246 247 /** 248 * Set the timer for the request timeout. It will cancel the request when the time is up. 249 */ setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs)250 void setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs); 251 252 /** 253 * Remove the timeout timer of the capabilities request. 254 */ removeRequestTimeoutTimer(long taskId)255 void removeRequestTimeoutTimer(long taskId); 256 257 /** 258 * Notify that the UceRequest has finished. This is sent by UceRequestCoordinator. 259 */ notifyUceRequestFinished(long requestCoordinatorId, long taskId)260 void notifyUceRequestFinished(long requestCoordinatorId, long taskId); 261 262 /** 263 * Notify that the RequestCoordinator has finished. This is sent by UceRequestCoordinator 264 * to remove the coordinator from the UceRequestRepository. 265 */ notifyRequestCoordinatorFinished(long requestCoordinatorId)266 void notifyRequestCoordinatorFinished(long requestCoordinatorId); 267 268 /** 269 * Check whether the given uris are in the throttling list. 270 * @param uriList the uris to check if it is in the throttling list 271 * @return the uris in the throttling list 272 */ getInThrottlingListUris(List<Uri> uriList)273 List<Uri> getInThrottlingListUris(List<Uri> uriList); 274 275 /** 276 * Add the given uris to the throttling list because the capabilities request result 277 * is inconclusive. 278 */ addToThrottlingList(List<Uri> uriList, int sipCode)279 void addToThrottlingList(List<Uri> uriList, int sipCode); 280 } 281 282 private RequestManagerCallback mRequestMgrCallback = new RequestManagerCallback() { 283 @Override 284 public void notifySendingRequest(long coordinatorId, long taskId, long delayTimeMs) { 285 mHandler.sendRequestMessage(coordinatorId, taskId, delayTimeMs); 286 } 287 288 @Override 289 public List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList) { 290 return mControllerCallback.getCapabilitiesFromCache(uriList); 291 } 292 293 @Override 294 public List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uris) { 295 return mControllerCallback.getCapabilitiesFromCacheIncludingExpired(uris); 296 } 297 298 @Override 299 public EabCapabilityResult getAvailabilityFromCache(Uri uri) { 300 return mControllerCallback.getAvailabilityFromCache(uri); 301 } 302 303 @Override 304 public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri) { 305 return mControllerCallback.getAvailabilityFromCacheIncludingExpired(uri); 306 } 307 308 @Override 309 public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) { 310 mControllerCallback.saveCapabilities(contactCapabilities); 311 } 312 313 @Override 314 public RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism) { 315 return mControllerCallback.getDeviceCapabilities(mechanism); 316 } 317 318 @Override 319 public DeviceStateResult getDeviceState() { 320 return mControllerCallback.getDeviceState(); 321 } 322 323 @Override 324 public void refreshDeviceState(int sipCode, String reason) { 325 mControllerCallback.refreshDeviceState(sipCode, reason, 326 UceController.REQUEST_TYPE_CAPABILITY); 327 } 328 329 @Override 330 public void notifyRequestError(long requestCoordinatorId, long taskId) { 331 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 332 UceRequestCoordinator.REQUEST_UPDATE_ERROR); 333 } 334 335 @Override 336 public void notifyCommandError(long requestCoordinatorId, long taskId) { 337 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 338 UceRequestCoordinator.REQUEST_UPDATE_COMMAND_ERROR); 339 } 340 341 @Override 342 public void notifyNetworkResponse(long requestCoordinatorId, long taskId) { 343 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 344 UceRequestCoordinator.REQUEST_UPDATE_NETWORK_RESPONSE); 345 } 346 @Override 347 public void notifyTerminated(long requestCoordinatorId, long taskId) { 348 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 349 UceRequestCoordinator.REQUEST_UPDATE_TERMINATED); 350 } 351 @Override 352 public void notifyResourceTerminated(long requestCoordinatorId, long taskId) { 353 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 354 UceRequestCoordinator.REQUEST_UPDATE_RESOURCE_TERMINATED); 355 } 356 @Override 357 public void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId) { 358 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 359 UceRequestCoordinator.REQUEST_UPDATE_CAPABILITY_UPDATE); 360 } 361 362 @Override 363 public void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId) { 364 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 365 UceRequestCoordinator.REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE); 366 } 367 368 @Override 369 public void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId) { 370 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 371 UceRequestCoordinator.REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK); 372 } 373 374 @Override 375 public void notifyRemoteRequestDone(long requestCoordinatorId, long taskId) { 376 mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId, 377 UceRequestCoordinator.REQUEST_UPDATE_REMOTE_REQUEST_DONE); 378 } 379 380 @Override 381 public void setRequestTimeoutTimer(long coordinatorId, long taskId, long timeoutAfterMs) { 382 mHandler.sendRequestTimeoutTimerMessage(coordinatorId, taskId, timeoutAfterMs); 383 } 384 385 @Override 386 public void removeRequestTimeoutTimer(long taskId) { 387 mHandler.removeRequestTimeoutTimer(taskId); 388 } 389 390 @Override 391 public void notifyUceRequestFinished(long requestCoordinatorId, long taskId) { 392 mHandler.sendRequestFinishedMessage(requestCoordinatorId, taskId); 393 } 394 395 @Override 396 public void notifyRequestCoordinatorFinished(long requestCoordinatorId) { 397 mHandler.sendRequestCoordinatorFinishedMessage(requestCoordinatorId); 398 } 399 400 @Override 401 public List<Uri> getInThrottlingListUris(List<Uri> uriList) { 402 return mThrottlingList.getInThrottlingListUris(uriList); 403 } 404 405 @Override 406 public void addToThrottlingList(List<Uri> uriList, int sipCode) { 407 mThrottlingList.addToThrottlingList(uriList, sipCode); 408 } 409 }; 410 411 private final int mSubId; 412 private final Context mContext; 413 private final UceRequestHandler mHandler; 414 private final UceRequestRepository mRequestRepository; 415 private final ContactThrottlingList mThrottlingList; 416 private volatile boolean mIsDestroyed; 417 418 private OptionsController mOptionsCtrl; 419 private SubscribeController mSubscribeCtrl; 420 private UceControllerCallback mControllerCallback; 421 UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c)422 public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c) { 423 mSubId = subId; 424 mContext = context; 425 mControllerCallback = c; 426 mHandler = new UceRequestHandler(this, looper); 427 mThrottlingList = new ContactThrottlingList(mSubId); 428 mRequestRepository = new UceRequestRepository(subId, mRequestMgrCallback); 429 logi("create"); 430 } 431 432 @VisibleForTesting UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c, UceRequestRepository requestRepository)433 public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c, 434 UceRequestRepository requestRepository) { 435 mSubId = subId; 436 mContext = context; 437 mControllerCallback = c; 438 mHandler = new UceRequestHandler(this, looper); 439 mRequestRepository = requestRepository; 440 mThrottlingList = new ContactThrottlingList(mSubId); 441 } 442 443 /** 444 * Set the OptionsController for requestiong capabilities by OPTIONS mechanism. 445 */ setOptionsController(OptionsController controller)446 public void setOptionsController(OptionsController controller) { 447 mOptionsCtrl = controller; 448 } 449 450 /** 451 * Set the SubscribeController for requesting capabilities by Subscribe mechanism. 452 */ setSubscribeController(SubscribeController controller)453 public void setSubscribeController(SubscribeController controller) { 454 mSubscribeCtrl = controller; 455 } 456 457 /** 458 * Notify that the request manager instance is destroyed. 459 */ onDestroy()460 public void onDestroy() { 461 logi("onDestroy"); 462 mIsDestroyed = true; 463 mHandler.onDestroy(); 464 mThrottlingList.reset(); 465 mRequestRepository.onDestroy(); 466 } 467 468 /** 469 * Clear the throttling list. 470 */ resetThrottlingList()471 public void resetThrottlingList() { 472 mThrottlingList.reset(); 473 } 474 475 /** 476 * Send a new capability request. It is called by UceController. 477 */ sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)478 public void sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache, 479 IRcsUceControllerCallback callback) throws RemoteException { 480 if (mIsDestroyed) { 481 callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 482 return; 483 } 484 sendRequestInternal(UceRequest.REQUEST_TYPE_CAPABILITY, uriList, skipFromCache, callback); 485 } 486 487 /** 488 * Send a new availability request. It is called by UceController. 489 */ sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)490 public void sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback) 491 throws RemoteException { 492 if (mIsDestroyed) { 493 callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 494 return; 495 } 496 sendRequestInternal(UceRequest.REQUEST_TYPE_AVAILABILITY, 497 Collections.singletonList(uri), false /* skipFromCache */, callback); 498 } 499 sendRequestInternal(@ceRequestType int type, List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)500 private void sendRequestInternal(@UceRequestType int type, List<Uri> uriList, 501 boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException { 502 UceRequestCoordinator requestCoordinator = null; 503 List<Uri> nonCachedUris = uriList; 504 if (FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS && !skipFromCache) { 505 nonCachedUris = sendCachedCapInfoToRequester(type, uriList, callback); 506 if (uriList.size() != nonCachedUris.size()) { 507 logd("sendRequestInternal: shortcut queue for caps - request reduced from " 508 + uriList.size() + " entries to " + nonCachedUris.size() + " entries"); 509 } else { 510 logd("sendRequestInternal: shortcut queue for caps - no cached caps."); 511 } 512 if (nonCachedUris.isEmpty()) { 513 logd("sendRequestInternal: shortcut complete, sending success result"); 514 callback.onComplete(); 515 return; 516 } 517 } 518 if (sUceUtilsProxy.isPresenceCapExchangeEnabled(mContext, mSubId) && 519 sUceUtilsProxy.isPresenceSupported(mContext, mSubId)) { 520 requestCoordinator = createSubscribeRequestCoordinator(type, nonCachedUris, 521 skipFromCache, callback); 522 } else if (sUceUtilsProxy.isSipOptionsSupported(mContext, mSubId)) { 523 requestCoordinator = createOptionsRequestCoordinator(type, nonCachedUris, callback); 524 } 525 526 if (requestCoordinator == null) { 527 logw("sendRequestInternal: Neither Presence nor OPTIONS are supported"); 528 callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L); 529 return; 530 } 531 532 StringBuilder builder = new StringBuilder("sendRequestInternal: "); 533 builder.append("requestType=").append(type) 534 .append(", requestCoordinatorId=").append(requestCoordinator.getCoordinatorId()) 535 .append(", taskId={") 536 .append(requestCoordinator.getActivatedRequestTaskIds().stream() 537 .map(Object::toString).collect(Collectors.joining(","))).append("}"); 538 logd(builder.toString()); 539 540 // Add this RequestCoordinator to the UceRequestRepository. 541 addRequestCoordinator(requestCoordinator); 542 } 543 544 /** 545 * Try to get the valid capabilities associated with the URI List specified from the EAB cache. 546 * If one or more of the numbers from the URI List have valid cached capabilities, return them 547 * to the requester now and remove them from the returned List of URIs that will require a 548 * network query. 549 * @param type The type of query 550 * @param uriList The List of URIs that we want to send cached capabilities for 551 * @param callback The callback used to communicate with the remote requester 552 * @return The List of URIs that were not found in the capability cache and will require a 553 * network query. 554 */ sendCachedCapInfoToRequester(int type, List<Uri> uriList, IRcsUceControllerCallback callback)555 private List<Uri> sendCachedCapInfoToRequester(int type, List<Uri> uriList, 556 IRcsUceControllerCallback callback) { 557 List<Uri> nonCachedUris = new ArrayList<>(uriList); 558 List<RcsContactUceCapability> numbersWithCachedCaps = 559 getCapabilitiesFromCache(type, nonCachedUris); 560 try { 561 if (!numbersWithCachedCaps.isEmpty()) { 562 logd("sendCachedCapInfoToRequester: cached caps found for " 563 + numbersWithCachedCaps.size() + " entries. Notifying requester."); 564 // Notify caller of the numbers that have cached caps 565 callback.onCapabilitiesReceived(numbersWithCachedCaps); 566 } 567 } catch (RemoteException e) { 568 logw("sendCachedCapInfoToRequester, error sending cap info back to requester: " + e); 569 } 570 // remove these numbers from the numbers pending a cap query from the network. 571 for (RcsContactUceCapability c : numbersWithCachedCaps) { 572 nonCachedUris.removeIf(uri -> c.getContactUri().equals(uri)); 573 } 574 return nonCachedUris; 575 } 576 577 /** 578 * Get the capabilities for the List of given URIs 579 * @param requestType The request type, used to determine if the cached info is "fresh" enough. 580 * @param uriList The List of URIs that we will be requesting cached capabilities for. 581 * @return A list of capabilities corresponding to the subset of numbers that still have 582 * valid cache data associated with them. 583 */ getCapabilitiesFromCache(int requestType, List<Uri> uriList)584 private List<RcsContactUceCapability> getCapabilitiesFromCache(int requestType, 585 List<Uri> uriList) { 586 List<EabCapabilityResult> resultList = Collections.emptyList(); 587 if (requestType == UceRequest.REQUEST_TYPE_CAPABILITY) { 588 resultList = mRequestMgrCallback.getCapabilitiesFromCache(uriList); 589 } else if (requestType == UceRequest.REQUEST_TYPE_AVAILABILITY) { 590 // Always get the first element if the request type is availability. 591 resultList = Collections.singletonList( 592 mRequestMgrCallback.getAvailabilityFromCache(uriList.get(0))); 593 } 594 // Map from EabCapabilityResult -> RcsContactUceCapability. 595 // Pull out only items that have valid cache data. 596 return resultList.stream().filter(Objects::nonNull) 597 .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL) 598 .map(EabCapabilityResult::getContactCapabilities) 599 .filter(Objects::nonNull).collect(Collectors.toList()); 600 } 601 createSubscribeRequestCoordinator(final @UceRequestType int type, final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)602 private UceRequestCoordinator createSubscribeRequestCoordinator(final @UceRequestType int type, 603 final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) { 604 SubscribeRequestCoordinator.Builder builder; 605 606 if (!sUceUtilsProxy.isPresenceGroupSubscribeEnabled(mContext, mSubId)) { 607 // When the group subscribe is disabled, each contact is required to be encapsulated 608 // into individual UceRequest. 609 List<UceRequest> requestList = new ArrayList<>(); 610 uriList.forEach(uri -> { 611 List<Uri> individualUri = Collections.singletonList(uri); 612 // Entity-uri, which is used as a request-uri, uses only a single subscription case 613 List<RcsContactUceCapability> capabilities = 614 getCapabilitiesFromCache(type, individualUri); 615 if (!capabilities.isEmpty()) { 616 RcsContactUceCapability capability = capabilities.get(0); 617 Uri entityUri = capability.getEntityUri(); 618 if (entityUri != null) { 619 // The query uri has been replaced by the stored entity uri. 620 individualUri = Collections.singletonList(entityUri); 621 } else { 622 if (UceUtils.isSipUriForPresenceSubscribeEnabled(mContext, mSubId)) { 623 individualUri = Collections.singletonList(getSipUriFromUri(uri)); 624 } 625 } 626 } else { 627 if (UceUtils.isSipUriForPresenceSubscribeEnabled(mContext, mSubId)) { 628 individualUri = Collections.singletonList(getSipUriFromUri(uri)); 629 } 630 } 631 UceRequest request = createSubscribeRequest(type, individualUri, skipFromCache); 632 requestList.add(request); 633 }); 634 builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList, 635 mRequestMgrCallback); 636 builder.setCapabilitiesCallback(callback); 637 } else { 638 // Even when the group subscribe is supported by the network, the number of contacts in 639 // a UceRequest still cannot exceed the maximum. 640 List<UceRequest> requestList = new ArrayList<>(); 641 final int rclMaxNumber = sUceUtilsProxy.getRclMaxNumberEntries(mSubId); 642 int numRequestCoordinators = uriList.size() / rclMaxNumber; 643 for (int count = 0; count < numRequestCoordinators; count++) { 644 List<Uri> subUriList = new ArrayList<>(); 645 for (int index = 0; index < rclMaxNumber; index++) { 646 subUriList.add(uriList.get(count * rclMaxNumber + index)); 647 } 648 requestList.add(createSubscribeRequest(type, subUriList, skipFromCache)); 649 } 650 651 List<Uri> subUriList = new ArrayList<>(); 652 for (int i = numRequestCoordinators * rclMaxNumber; i < uriList.size(); i++) { 653 subUriList.add(uriList.get(i)); 654 } 655 requestList.add(createSubscribeRequest(type, subUriList, skipFromCache)); 656 657 builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList, 658 mRequestMgrCallback); 659 builder.setCapabilitiesCallback(callback); 660 } 661 return builder.build(); 662 } 663 createOptionsRequestCoordinator(@ceRequestType int type, List<Uri> uriList, IRcsUceControllerCallback callback)664 private UceRequestCoordinator createOptionsRequestCoordinator(@UceRequestType int type, 665 List<Uri> uriList, IRcsUceControllerCallback callback) { 666 OptionsRequestCoordinator.Builder builder; 667 List<UceRequest> requestList = new ArrayList<>(); 668 uriList.forEach(uri -> { 669 List<Uri> individualUri = Collections.singletonList(uri); 670 UceRequest request = createOptionsRequest(type, individualUri, false); 671 requestList.add(request); 672 }); 673 builder = new OptionsRequestCoordinator.Builder(mSubId, requestList, mRequestMgrCallback); 674 builder.setCapabilitiesCallback(callback); 675 return builder.build(); 676 } 677 createSubscribeRequest(int type, List<Uri> uriList, boolean skipFromCache)678 private CapabilityRequest createSubscribeRequest(int type, List<Uri> uriList, 679 boolean skipFromCache) { 680 CapabilityRequest request = new SubscribeRequest(mSubId, type, mRequestMgrCallback, 681 mSubscribeCtrl); 682 request.setContactUri(uriList); 683 request.setSkipGettingFromCache(skipFromCache); 684 return request; 685 } 686 createOptionsRequest(int type, List<Uri> uriList, boolean skipFromCache)687 private CapabilityRequest createOptionsRequest(int type, List<Uri> uriList, 688 boolean skipFromCache) { 689 CapabilityRequest request = new OptionsRequest(mSubId, type, mRequestMgrCallback, 690 mOptionsCtrl); 691 request.setContactUri(uriList); 692 request.setSkipGettingFromCache(skipFromCache); 693 return request; 694 } 695 696 /** 697 * Retrieve the device's capabilities. This request is from the ImsService to send the 698 * capabilities to the remote side. 699 */ retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback requestCallback)700 public void retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities, 701 IOptionsRequestCallback requestCallback) { 702 RemoteOptionsRequest request = new RemoteOptionsRequest(mSubId, mRequestMgrCallback); 703 request.setContactUri(Collections.singletonList(contactUri)); 704 request.setRemoteFeatureTags(remoteCapabilities); 705 706 // If the remote number is blocked, do not send capabilities back. 707 String number = getNumberFromUri(contactUri); 708 if (!TextUtils.isEmpty(number)) { 709 request.setIsRemoteNumberBlocked(sUceUtilsProxy.isNumberBlocked(mContext, number)); 710 } 711 712 // Create the RemoteOptionsCoordinator instance 713 RemoteOptionsCoordinator.Builder CoordBuilder = new RemoteOptionsCoordinator.Builder( 714 mSubId, Collections.singletonList(request), mRequestMgrCallback); 715 CoordBuilder.setOptionsRequestCallback(requestCallback); 716 RemoteOptionsCoordinator requestCoordinator = CoordBuilder.build(); 717 718 StringBuilder builder = new StringBuilder("retrieveCapabilitiesForRemote: "); 719 builder.append("requestCoordinatorId ").append(requestCoordinator.getCoordinatorId()) 720 .append(", taskId={") 721 .append(requestCoordinator.getActivatedRequestTaskIds().stream() 722 .map(Object::toString).collect(Collectors.joining(","))).append("}"); 723 logd(builder.toString()); 724 725 // Add this RequestCoordinator to the UceRequestRepository. 726 addRequestCoordinator(requestCoordinator); 727 } 728 729 private static class UceRequestHandler extends Handler { 730 private static final int EVENT_EXECUTE_REQUEST = 1; 731 private static final int EVENT_REQUEST_UPDATED = 2; 732 private static final int EVENT_REQUEST_TIMEOUT = 3; 733 private static final int EVENT_REQUEST_FINISHED = 4; 734 private static final int EVENT_COORDINATOR_FINISHED = 5; 735 736 private final Map<Long, SomeArgs> mRequestTimeoutTimers; 737 private final WeakReference<UceRequestManager> mUceRequestMgrRef; 738 UceRequestHandler(UceRequestManager requestManager, Looper looper)739 public UceRequestHandler(UceRequestManager requestManager, Looper looper) { 740 super(looper); 741 mRequestTimeoutTimers = new HashMap<>(); 742 mUceRequestMgrRef = new WeakReference<>(requestManager); 743 } 744 745 /** 746 * Send the capabilities request message. 747 */ sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs)748 public void sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs) { 749 SomeArgs args = SomeArgs.obtain(); 750 args.arg1 = coordinatorId; 751 args.arg2 = taskId; 752 753 Message message = obtainMessage(); 754 message.what = EVENT_EXECUTE_REQUEST; 755 message.obj = args; 756 sendMessageDelayed(message, delayTimeMs); 757 } 758 759 /** 760 * Send the Uce request updated message. 761 */ sendRequestUpdatedMessage(Long coordinatorId, Long taskId, @UceRequestUpdate int requestEvent)762 public void sendRequestUpdatedMessage(Long coordinatorId, Long taskId, 763 @UceRequestUpdate int requestEvent) { 764 SomeArgs args = SomeArgs.obtain(); 765 args.arg1 = coordinatorId; 766 args.arg2 = taskId; 767 args.argi1 = requestEvent; 768 769 Message message = obtainMessage(); 770 message.what = EVENT_REQUEST_UPDATED; 771 message.obj = args; 772 sendMessage(message); 773 } 774 775 /** 776 * Set the timeout timer to cancel the capabilities request. 777 */ sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs)778 public void sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs) { 779 synchronized (mRequestTimeoutTimers) { 780 SomeArgs args = SomeArgs.obtain(); 781 args.arg1 = coordId; 782 args.arg2 = taskId; 783 784 // Add the message object to the collection. It can be used to find this message 785 // when the request is completed and remove the timeout timer. 786 mRequestTimeoutTimers.put(taskId, args); 787 788 Message message = obtainMessage(); 789 message.what = EVENT_REQUEST_TIMEOUT; 790 message.obj = args; 791 sendMessageDelayed(message, timeoutAfterMs); 792 } 793 } 794 795 /** 796 * Remove the timeout timer because the capabilities request is finished. 797 */ removeRequestTimeoutTimer(Long taskId)798 public void removeRequestTimeoutTimer(Long taskId) { 799 synchronized (mRequestTimeoutTimers) { 800 SomeArgs args = mRequestTimeoutTimers.remove(taskId); 801 if (args == null) { 802 return; 803 } 804 Log.d(LOG_TAG, "removeRequestTimeoutTimer: taskId=" + taskId); 805 removeMessages(EVENT_REQUEST_TIMEOUT, args); 806 args.recycle(); 807 } 808 } 809 sendRequestFinishedMessage(Long coordinatorId, Long taskId)810 public void sendRequestFinishedMessage(Long coordinatorId, Long taskId) { 811 SomeArgs args = SomeArgs.obtain(); 812 args.arg1 = coordinatorId; 813 args.arg2 = taskId; 814 815 Message message = obtainMessage(); 816 message.what = EVENT_REQUEST_FINISHED; 817 message.obj = args; 818 sendMessage(message); 819 } 820 821 /** 822 * Finish the UceRequestCoordinator associated with the given id. 823 */ sendRequestCoordinatorFinishedMessage(Long coordinatorId)824 public void sendRequestCoordinatorFinishedMessage(Long coordinatorId) { 825 SomeArgs args = SomeArgs.obtain(); 826 args.arg1 = coordinatorId; 827 828 Message message = obtainMessage(); 829 message.what = EVENT_COORDINATOR_FINISHED; 830 message.obj = args; 831 sendMessage(message); 832 } 833 834 /** 835 * Remove all the messages from the handler 836 */ onDestroy()837 public void onDestroy() { 838 removeCallbacksAndMessages(null); 839 // Recycle all the arguments in the mRequestTimeoutTimers 840 synchronized (mRequestTimeoutTimers) { 841 mRequestTimeoutTimers.forEach((taskId, args) -> { 842 try { 843 args.recycle(); 844 } catch (Exception e) {} 845 }); 846 mRequestTimeoutTimers.clear(); 847 } 848 } 849 850 @Override handleMessage(Message msg)851 public void handleMessage(Message msg) { 852 UceRequestManager requestManager = mUceRequestMgrRef.get(); 853 if (requestManager == null) { 854 return; 855 } 856 SomeArgs args = (SomeArgs) msg.obj; 857 final Long coordinatorId = (Long) args.arg1; 858 final Long taskId = (Long) Optional.ofNullable(args.arg2).orElse(-1L); 859 final Integer requestEvent = Optional.ofNullable(args.argi1).orElse(-1); 860 args.recycle(); 861 862 requestManager.logd("handleMessage: " + EVENT_DESCRIPTION.get(msg.what) 863 + ", coordinatorId=" + coordinatorId + ", taskId=" + taskId); 864 switch (msg.what) { 865 case EVENT_EXECUTE_REQUEST: { 866 UceRequest request = requestManager.getUceRequest(taskId); 867 if (request == null) { 868 requestManager.logw("handleMessage: cannot find request, taskId=" + taskId); 869 return; 870 } 871 request.executeRequest(); 872 break; 873 } 874 case EVENT_REQUEST_UPDATED: { 875 UceRequestCoordinator requestCoordinator = 876 requestManager.getRequestCoordinator(coordinatorId); 877 if (requestCoordinator == null) { 878 requestManager.logw("handleMessage: cannot find UceRequestCoordinator"); 879 return; 880 } 881 requestCoordinator.onRequestUpdated(taskId, requestEvent); 882 break; 883 } 884 case EVENT_REQUEST_TIMEOUT: { 885 UceRequestCoordinator requestCoordinator = 886 requestManager.getRequestCoordinator(coordinatorId); 887 if (requestCoordinator == null) { 888 requestManager.logw("handleMessage: cannot find UceRequestCoordinator"); 889 return; 890 } 891 // The timeout timer is triggered, remove this record from the collection. 892 synchronized (mRequestTimeoutTimers) { 893 mRequestTimeoutTimers.remove(taskId); 894 } 895 // Notify that the request is timeout. 896 requestCoordinator.onRequestUpdated(taskId, 897 UceRequestCoordinator.REQUEST_UPDATE_TIMEOUT); 898 break; 899 } 900 case EVENT_REQUEST_FINISHED: { 901 // Notify the repository that the request is finished. 902 requestManager.notifyRepositoryRequestFinished(taskId); 903 break; 904 } 905 case EVENT_COORDINATOR_FINISHED: { 906 UceRequestCoordinator requestCoordinator = 907 requestManager.removeRequestCoordinator(coordinatorId); 908 if (requestCoordinator != null) { 909 requestCoordinator.onFinish(); 910 } 911 break; 912 } 913 default: { 914 break; 915 } 916 } 917 } 918 919 private static Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>(); 920 static { EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST")921 EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST"); EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE")922 EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE"); EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT")923 EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT"); EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED")924 EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED"); EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR")925 EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR"); 926 } 927 } 928 addRequestCoordinator(UceRequestCoordinator coordinator)929 private void addRequestCoordinator(UceRequestCoordinator coordinator) { 930 mRequestRepository.addRequestCoordinator(coordinator); 931 } 932 removeRequestCoordinator(Long coordinatorId)933 private UceRequestCoordinator removeRequestCoordinator(Long coordinatorId) { 934 return mRequestRepository.removeRequestCoordinator(coordinatorId); 935 } 936 getRequestCoordinator(Long coordinatorId)937 private UceRequestCoordinator getRequestCoordinator(Long coordinatorId) { 938 return mRequestRepository.getRequestCoordinator(coordinatorId); 939 } 940 getUceRequest(Long taskId)941 private UceRequest getUceRequest(Long taskId) { 942 return mRequestRepository.getUceRequest(taskId); 943 } 944 notifyRepositoryRequestFinished(Long taskId)945 private void notifyRepositoryRequestFinished(Long taskId) { 946 mRequestRepository.notifyRequestFinished(taskId); 947 } 948 getSipUriFromUri(Uri uri)949 private Uri getSipUriFromUri(Uri uri) { 950 Uri convertedUri = uri; 951 String number = convertedUri.getSchemeSpecificPart(); 952 String[] numberParts = number.split("[@;:]"); 953 number = numberParts[0]; 954 955 TelephonyManager manager = mContext.getSystemService(TelephonyManager.class); 956 if (manager.getIsimDomain() == null) { 957 return convertedUri; 958 } 959 String simCountryIso = manager.getSimCountryIso(); 960 if (TextUtils.isEmpty(simCountryIso)) { 961 return convertedUri; 962 } 963 simCountryIso = simCountryIso.toUpperCase(); 964 PhoneNumberUtil util = PhoneNumberUtil.getInstance(); 965 try { 966 Phonenumber.PhoneNumber phoneNumber = util.parse(number, simCountryIso); 967 number = util.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); 968 String sipUri = "sip:" + number + "@" + manager.getIsimDomain(); 969 convertedUri = Uri.parse(sipUri); 970 } catch (NumberParseException e) { 971 Log.w(LOG_TAG, "formatNumber: could not format " + number + ", error: " + e); 972 } 973 return convertedUri; 974 } 975 976 @VisibleForTesting getUceRequestHandler()977 public UceRequestHandler getUceRequestHandler() { 978 return mHandler; 979 } 980 981 @VisibleForTesting getRequestManagerCallback()982 public RequestManagerCallback getRequestManagerCallback() { 983 return mRequestMgrCallback; 984 } 985 logi(String log)986 private void logi(String log) { 987 Log.i(LOG_TAG, getLogPrefix().append(log).toString()); 988 } 989 logd(String log)990 private void logd(String log) { 991 Log.d(LOG_TAG, getLogPrefix().append(log).toString()); 992 } 993 logw(String log)994 private void logw(String log) { 995 Log.w(LOG_TAG, getLogPrefix().append(log).toString()); 996 } 997 getLogPrefix()998 private StringBuilder getLogPrefix() { 999 StringBuilder builder = new StringBuilder("["); 1000 builder.append(mSubId); 1001 builder.append("] "); 1002 return builder; 1003 } 1004 getNumberFromUri(Uri uri)1005 private String getNumberFromUri(Uri uri) { 1006 if (uri == null) return null; 1007 String number = uri.getSchemeSpecificPart(); 1008 String[] numberParts = number.split("[@;:]"); 1009 1010 if (numberParts.length == 0) { 1011 return null; 1012 } 1013 return numberParts[0]; 1014 } 1015 } 1016