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