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