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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.content.Context; 22 import android.net.Uri; 23 import android.os.HandlerThread; 24 import android.os.Looper; 25 import android.os.RemoteException; 26 import android.telephony.ims.RcsContactUceCapability; 27 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism; 28 import android.telephony.ims.RcsUceAdapter; 29 import android.telephony.ims.RcsUceAdapter.PublishState; 30 import android.telephony.ims.RcsUceAdapter.StackPublishTriggerType; 31 import android.telephony.ims.aidl.IOptionsRequestCallback; 32 import android.telephony.ims.aidl.IRcsUceControllerCallback; 33 import android.telephony.ims.aidl.IRcsUcePublishStateCallback; 34 import android.util.IndentingPrintWriter; 35 import android.util.LocalLog; 36 import android.util.Log; 37 38 import com.android.ims.RcsFeatureManager; 39 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult; 40 import com.android.ims.rcs.uce.eab.EabCapabilityResult; 41 import com.android.ims.rcs.uce.eab.EabController; 42 import com.android.ims.rcs.uce.eab.EabControllerImpl; 43 import com.android.ims.rcs.uce.options.OptionsController; 44 import com.android.ims.rcs.uce.options.OptionsControllerImpl; 45 import com.android.ims.rcs.uce.presence.publish.PublishController; 46 import com.android.ims.rcs.uce.presence.publish.PublishControllerImpl; 47 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; 48 import com.android.ims.rcs.uce.presence.subscribe.SubscribeControllerImpl; 49 import com.android.ims.rcs.uce.request.UceRequestManager; 50 import com.android.ims.rcs.uce.util.UceUtils; 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.internal.os.SomeArgs; 53 54 import java.io.PrintWriter; 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.util.HashMap; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.Optional; 61 import java.util.Set; 62 63 /** 64 * The UceController will manage the RCS UCE requests on a per subscription basis. When it receives 65 * the UCE requests from the RCS applications and from the ImsService, it will coordinate the 66 * cooperation between the publish/subscribe/options components to complete the requests. 67 */ 68 public class UceController { 69 70 private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceController"; 71 72 /** 73 * The callback interface is called by the internal controllers to receive information from 74 * others controllers. 75 */ 76 public interface UceControllerCallback { 77 /** 78 * Retrieve the capabilities associated with the given uris from the cache. 79 */ getCapabilitiesFromCache(@onNull List<Uri> uris)80 List<EabCapabilityResult> getCapabilitiesFromCache(@NonNull List<Uri> uris); 81 82 /** 83 * Retrieve the capabilities associated with the given uris from the cache including 84 * expired capabilities. 85 */ getCapabilitiesFromCacheIncludingExpired(@onNull List<Uri> uris)86 List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(@NonNull List<Uri> uris); 87 88 /** 89 * Retrieve the contact's capabilities from the availability cache. 90 */ getAvailabilityFromCache(@onNull Uri uri)91 EabCapabilityResult getAvailabilityFromCache(@NonNull Uri uri); 92 93 /** 94 * Retrieve the contact's capabilities from the availability cache including expired 95 * capabilities 96 */ getAvailabilityFromCacheIncludingExpired(@onNull Uri uri)97 EabCapabilityResult getAvailabilityFromCacheIncludingExpired(@NonNull Uri uri); 98 99 /** 100 * Store the given capabilities to the cache. 101 */ saveCapabilities(List<RcsContactUceCapability> contactCapabilities)102 void saveCapabilities(List<RcsContactUceCapability> contactCapabilities); 103 104 /** 105 * Retrieve the device's capabilities. 106 */ getDeviceCapabilities(@apabilityMechanism int mechanism)107 RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism); 108 109 /** 110 * Refresh the device state. It is called when receive the UCE request response. 111 * @param sipCode The SIP code of the request response. 112 * @param reason The reason from the network response. 113 * @param type The type of the request 114 */ refreshDeviceState(int sipCode, String reason, @RequestType int type)115 void refreshDeviceState(int sipCode, String reason, @RequestType int type); 116 117 /** 118 * Reset the device state when then device disallowed state is expired. 119 */ resetDeviceState()120 void resetDeviceState(); 121 122 /** 123 * Get the current device state to check if the device is allowed to send UCE requests. 124 */ getDeviceState()125 DeviceStateResult getDeviceState(); 126 127 /** 128 * Setup timer to exit device disallowed state. 129 */ setupResetDeviceStateTimer(long resetAfterSec)130 void setupResetDeviceStateTimer(long resetAfterSec); 131 132 /** 133 * The device state is already reset, clear the timer. 134 */ clearResetDeviceStateTimer()135 void clearResetDeviceStateTimer(); 136 137 /** 138 * The method is called when the given contacts' capabilities are expired and need to be 139 * refreshed. 140 */ refreshCapabilities(@onNull List<Uri> contactNumbers, @NonNull IRcsUceControllerCallback callback)141 void refreshCapabilities(@NonNull List<Uri> contactNumbers, 142 @NonNull IRcsUceControllerCallback callback) throws RemoteException; 143 } 144 145 /** 146 * Used to inject RequestManger instances for testing. 147 */ 148 @VisibleForTesting 149 public interface RequestManagerFactory { createRequestManager(Context context, int subId, Looper looper, UceControllerCallback callback)150 UceRequestManager createRequestManager(Context context, int subId, Looper looper, 151 UceControllerCallback callback); 152 } 153 154 private RequestManagerFactory mRequestManagerFactory = (context, subId, looper, callback) -> 155 new UceRequestManager(context, subId, looper, callback); 156 157 /** 158 * Used to inject Controller instances for testing. 159 */ 160 @VisibleForTesting 161 public interface ControllerFactory { 162 /** 163 * @return an {@link EabController} associated with the subscription id specified. 164 */ createEabController(Context context, int subId, UceControllerCallback c, Looper looper)165 EabController createEabController(Context context, int subId, UceControllerCallback c, 166 Looper looper); 167 168 /** 169 * @return an {@link PublishController} associated with the subscription id specified. 170 */ createPublishController(Context context, int subId, UceControllerCallback c, Looper looper)171 PublishController createPublishController(Context context, int subId, 172 UceControllerCallback c, Looper looper); 173 174 /** 175 * @return an {@link SubscribeController} associated with the subscription id specified. 176 */ createSubscribeController(Context context, int subId)177 SubscribeController createSubscribeController(Context context, int subId); 178 179 /** 180 * @return an {@link OptionsController} associated with the subscription id specified. 181 */ createOptionsController(Context context, int subId)182 OptionsController createOptionsController(Context context, int subId); 183 } 184 185 private ControllerFactory mControllerFactory = new ControllerFactory() { 186 @Override 187 public EabController createEabController(Context context, int subId, 188 UceControllerCallback c, Looper looper) { 189 return new EabControllerImpl(context, subId, c, looper); 190 } 191 192 @Override 193 public PublishController createPublishController(Context context, int subId, 194 UceControllerCallback c, Looper looper) { 195 return new PublishControllerImpl(context, subId, c, looper); 196 } 197 198 @Override 199 public SubscribeController createSubscribeController(Context context, int subId) { 200 return new SubscribeControllerImpl(context, subId); 201 } 202 203 @Override 204 public OptionsController createOptionsController(Context context, int subId) { 205 return new OptionsControllerImpl(context, subId); 206 } 207 }; 208 209 /** 210 * Cache the capabilities events triggered by the ImsService during the RCS connected procedure. 211 */ 212 private static class CachedCapabilityEvent { 213 private Optional<Integer> mRequestPublishCapabilitiesEvent; 214 private Optional<Boolean> mUnpublishEvent; 215 private Optional<SomeArgs> mPublishUpdatedEvent; 216 private Optional<SomeArgs> mRemoteCapabilityRequestEvent; 217 CachedCapabilityEvent()218 public CachedCapabilityEvent() { 219 mRequestPublishCapabilitiesEvent = Optional.empty(); 220 mUnpublishEvent = Optional.empty(); 221 mPublishUpdatedEvent = Optional.empty(); 222 mRemoteCapabilityRequestEvent = Optional.empty(); 223 } 224 225 /** 226 * Cache the publish capabilities request event triggered by the ImsService. 227 */ setRequestPublishCapabilitiesEvent(int triggerType)228 public synchronized void setRequestPublishCapabilitiesEvent(int triggerType) { 229 mRequestPublishCapabilitiesEvent = Optional.of(triggerType); 230 } 231 232 /** 233 * Cache the unpublish event triggered by the ImsService. 234 */ setOnUnpublishEvent()235 public synchronized void setOnUnpublishEvent() { 236 mUnpublishEvent = Optional.of(Boolean.TRUE); 237 } 238 239 /** 240 * Cache the publish update event triggered by the ImsService. 241 */ setOnPublishUpdatedEvent(int reasonCode, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText)242 public synchronized void setOnPublishUpdatedEvent(int reasonCode, String reasonPhrase, 243 int reasonHeaderCause, String reasonHeaderText) { 244 SomeArgs args = SomeArgs.obtain(); 245 args.arg1 = reasonCode; 246 args.arg2 = reasonPhrase; 247 args.arg3 = reasonHeaderCause; 248 args.arg4 = reasonHeaderText; 249 mPublishUpdatedEvent = Optional.of(args); 250 } 251 252 /** 253 * Cache the remote capability request event triggered by the ImsService. 254 */ setRemoteCapabilityRequestEvent(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback callback)255 public synchronized void setRemoteCapabilityRequestEvent(Uri contactUri, 256 List<String> remoteCapabilities, IOptionsRequestCallback callback) { 257 SomeArgs args = SomeArgs.obtain(); 258 args.arg1 = contactUri; 259 args.arg2 = remoteCapabilities; 260 args.arg3 = callback; 261 mRemoteCapabilityRequestEvent = Optional.of(args); 262 } 263 264 /** @Return the cached publish request event */ getRequestPublishEvent()265 public synchronized Optional<Integer> getRequestPublishEvent() { 266 return mRequestPublishCapabilitiesEvent; 267 } 268 269 /** @Return the cached unpublish event */ getUnpublishEvent()270 public synchronized Optional<Boolean> getUnpublishEvent() { 271 return mUnpublishEvent; 272 } 273 274 /** @Return the cached pubilsh update event */ getPublishUpdatedEvent()275 public synchronized Optional<SomeArgs> getPublishUpdatedEvent() { 276 return mPublishUpdatedEvent; 277 } 278 279 /** @Return the cached remote capability request event */ getRemoteCapabilityRequestEvent()280 public synchronized Optional<SomeArgs> getRemoteCapabilityRequestEvent() { 281 return mRemoteCapabilityRequestEvent; 282 } 283 284 /** Clear the cached */ clear()285 public synchronized void clear() { 286 mRequestPublishCapabilitiesEvent = Optional.empty(); 287 mUnpublishEvent = Optional.empty(); 288 mPublishUpdatedEvent.ifPresent(args -> args.recycle()); 289 mPublishUpdatedEvent = Optional.empty(); 290 mRemoteCapabilityRequestEvent.ifPresent(args -> args.recycle()); 291 mRemoteCapabilityRequestEvent = Optional.empty(); 292 } 293 } 294 295 /** 296 * The request type is PUBLISH. 297 */ 298 public static final int REQUEST_TYPE_PUBLISH = 1; 299 300 /** 301 * The request type is CAPABILITY. 302 */ 303 public static final int REQUEST_TYPE_CAPABILITY = 2; 304 305 @IntDef(value = { 306 REQUEST_TYPE_PUBLISH, 307 REQUEST_TYPE_CAPABILITY, 308 }, prefix="REQUEST_TYPE_") 309 @Retention(RetentionPolicy.SOURCE) 310 public @interface RequestType {} 311 312 public static final Map<Integer, String> REQUEST_TYPE_DESCRIPTION = new HashMap<>(); 313 static { REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_PUBLISH, "REQUEST_TYPE_PUBLISH")314 REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_PUBLISH, "REQUEST_TYPE_PUBLISH"); REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_CAPABILITY, "REQUEST_TYPE_CAPABILITY")315 REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_CAPABILITY, "REQUEST_TYPE_CAPABILITY"); 316 } 317 318 /** The RCS state is disconnected */ 319 private static final int RCS_STATE_DISCONNECTED = 0; 320 321 /** The RCS state is connecting */ 322 private static final int RCS_STATE_CONNECTING = 1; 323 324 /** The RCS state is connected */ 325 private static final int RCS_STATE_CONNECTED = 2; 326 327 @IntDef(value = { 328 RCS_STATE_DISCONNECTED, 329 RCS_STATE_CONNECTING, 330 RCS_STATE_CONNECTED, 331 }, prefix="RCS_STATE_") 332 @Retention(RetentionPolicy.SOURCE) 333 @interface RcsConnectedState {} 334 335 private final int mSubId; 336 private final Context mContext; 337 private final LocalLog mLocalLog = new LocalLog(UceUtils.LOG_SIZE); 338 339 private volatile Looper mLooper; 340 private volatile boolean mIsDestroyedFlag; 341 private volatile @RcsConnectedState int mRcsConnectedState; 342 343 private RcsFeatureManager mRcsFeatureManager; 344 private EabController mEabController; 345 private PublishController mPublishController; 346 private SubscribeController mSubscribeController; 347 private OptionsController mOptionsController; 348 private UceRequestManager mRequestManager; 349 // The device state to execute UCE requests. 350 private UceDeviceState mDeviceState; 351 // The cache of the capability request event triggered by ImsService 352 private final CachedCapabilityEvent mCachedCapabilityEvent; 353 UceController(Context context, int subId)354 public UceController(Context context, int subId) { 355 mSubId = subId; 356 mContext = context; 357 mCachedCapabilityEvent = new CachedCapabilityEvent(); 358 mRcsConnectedState = RCS_STATE_DISCONNECTED; 359 logi("create"); 360 361 initLooper(); 362 initControllers(); 363 initRequestManager(); 364 initUceDeviceState(); 365 } 366 367 @VisibleForTesting UceController(Context context, int subId, UceDeviceState deviceState, ControllerFactory controllerFactory, RequestManagerFactory requestManagerFactory)368 public UceController(Context context, int subId, UceDeviceState deviceState, 369 ControllerFactory controllerFactory, RequestManagerFactory requestManagerFactory) { 370 mSubId = subId; 371 mContext = context; 372 mDeviceState = deviceState; 373 mControllerFactory = controllerFactory; 374 mRequestManagerFactory = requestManagerFactory; 375 mCachedCapabilityEvent = new CachedCapabilityEvent(); 376 mRcsConnectedState = RCS_STATE_DISCONNECTED; 377 initLooper(); 378 initControllers(); 379 initRequestManager(); 380 } 381 initLooper()382 private void initLooper() { 383 // Init the looper, it will be passed to each controller. 384 HandlerThread handlerThread = new HandlerThread("UceControllerHandlerThread"); 385 handlerThread.start(); 386 mLooper = handlerThread.getLooper(); 387 } 388 initControllers()389 private void initControllers() { 390 mEabController = mControllerFactory.createEabController(mContext, mSubId, mCtrlCallback, 391 mLooper); 392 mPublishController = mControllerFactory.createPublishController(mContext, mSubId, 393 mCtrlCallback, mLooper); 394 mSubscribeController = mControllerFactory.createSubscribeController(mContext, mSubId); 395 mOptionsController = mControllerFactory.createOptionsController(mContext, mSubId); 396 } 397 initRequestManager()398 private void initRequestManager() { 399 mRequestManager = mRequestManagerFactory.createRequestManager(mContext, mSubId, mLooper, 400 mCtrlCallback); 401 mRequestManager.setSubscribeController(mSubscribeController); 402 mRequestManager.setOptionsController(mOptionsController); 403 } 404 initUceDeviceState()405 private void initUceDeviceState() { 406 mDeviceState = new UceDeviceState(mSubId, mContext, mCtrlCallback); 407 mDeviceState.checkSendResetDeviceStateTimer(); 408 } 409 410 /** 411 * The RcsFeature has been connected to the framework. This method runs on main thread. 412 */ onRcsConnected(RcsFeatureManager manager)413 public void onRcsConnected(RcsFeatureManager manager) { 414 logi("onRcsConnected"); 415 // Set the RCS is connecting flag 416 mRcsConnectedState = RCS_STATE_CONNECTING; 417 418 // Listen to the capability exchange event which is triggered by the ImsService 419 mRcsFeatureManager = manager; 420 mRcsFeatureManager.addCapabilityEventCallback(mCapabilityEventListener); 421 422 // Notify each controllers that RCS is connected. 423 mEabController.onRcsConnected(manager); 424 mPublishController.onRcsConnected(manager); 425 mSubscribeController.onRcsConnected(manager); 426 mOptionsController.onRcsConnected(manager); 427 428 // Set the RCS is connected flag and check if there is any capability event received during 429 // the connecting process. 430 mRcsConnectedState = RCS_STATE_CONNECTED; 431 handleCachedCapabilityEvent(); 432 } 433 434 /** 435 * The framework has lost the binding to the RcsFeature. This method runs on main thread. 436 */ onRcsDisconnected()437 public void onRcsDisconnected() { 438 logi("onRcsDisconnected"); 439 mRcsConnectedState = RCS_STATE_DISCONNECTED; 440 // Remove the listener because RCS is disconnected. 441 if (mRcsFeatureManager != null) { 442 mRcsFeatureManager.removeCapabilityEventCallback(mCapabilityEventListener); 443 mRcsFeatureManager = null; 444 } 445 // Notify each controllers that RCS is disconnected. 446 mEabController.onRcsDisconnected(); 447 mPublishController.onRcsDisconnected(); 448 mSubscribeController.onRcsDisconnected(); 449 mOptionsController.onRcsDisconnected(); 450 } 451 452 /** 453 * Notify to destroy this instance. This instance is unusable after destroyed. 454 */ onDestroy()455 public void onDestroy() { 456 logi("onDestroy"); 457 mIsDestroyedFlag = true; 458 // Remove the listener because the UceController instance is destroyed. 459 if (mRcsFeatureManager != null) { 460 mRcsFeatureManager.removeCapabilityEventCallback(mCapabilityEventListener); 461 mRcsFeatureManager = null; 462 } 463 // Destroy all the controllers 464 mRequestManager.onDestroy(); 465 mEabController.onDestroy(); 466 mPublishController.onDestroy(); 467 mSubscribeController.onDestroy(); 468 mOptionsController.onDestroy(); 469 470 // Execute all the existing requests before quitting the looper. 471 mLooper.quitSafely(); 472 } 473 474 /** 475 * Notify all associated classes that the carrier configuration has changed for the subId. 476 */ onCarrierConfigChanged()477 public void onCarrierConfigChanged() { 478 mEabController.onCarrierConfigChanged(); 479 mPublishController.onCarrierConfigChanged(); 480 mSubscribeController.onCarrierConfigChanged(); 481 mOptionsController.onCarrierConfigChanged(); 482 } 483 handleCachedCapabilityEvent()484 private void handleCachedCapabilityEvent() { 485 Optional<Integer> requestPublishEvent = mCachedCapabilityEvent.getRequestPublishEvent(); 486 requestPublishEvent.ifPresent(triggerType -> 487 onRequestPublishCapabilitiesFromService(triggerType)); 488 489 Optional<Boolean> unpublishEvent = mCachedCapabilityEvent.getUnpublishEvent(); 490 unpublishEvent.ifPresent(unpublish -> onUnpublish()); 491 492 Optional<SomeArgs> publishUpdatedEvent = mCachedCapabilityEvent.getPublishUpdatedEvent(); 493 publishUpdatedEvent.ifPresent(args -> { 494 int reasonCode = (Integer) args.arg1; 495 String reasonPhrase = (String) args.arg2; 496 int reasonHeaderCause = (Integer) args.arg3; 497 String reasonHeaderText = (String) args.arg4; 498 onPublishUpdated(reasonCode, reasonPhrase, reasonHeaderCause, reasonHeaderText); 499 }); 500 501 Optional<SomeArgs> remoteRequest = mCachedCapabilityEvent.getRemoteCapabilityRequestEvent(); 502 remoteRequest.ifPresent(args -> { 503 Uri contactUri = (Uri) args.arg1; 504 List<String> remoteCapabilities = (List<String>) args.arg2; 505 IOptionsRequestCallback callback = (IOptionsRequestCallback) args.arg3; 506 retrieveOptionsCapabilitiesForRemote(contactUri, remoteCapabilities, callback); 507 }); 508 mCachedCapabilityEvent.clear(); 509 } 510 511 /* 512 * The implementation of the interface UceControllerCallback. These methods are called by other 513 * controllers. 514 */ 515 private UceControllerCallback mCtrlCallback = new UceControllerCallback() { 516 @Override 517 public List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uris) { 518 return mEabController.getCapabilities(uris); 519 } 520 521 @Override 522 public List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uris) { 523 return mEabController.getCapabilitiesIncludingExpired(uris); 524 } 525 526 @Override 527 public EabCapabilityResult getAvailabilityFromCache(Uri contactUri) { 528 return mEabController.getAvailability(contactUri); 529 } 530 531 @Override 532 public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri contactUri) { 533 return mEabController.getAvailabilityIncludingExpired(contactUri); 534 } 535 536 @Override 537 public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) { 538 mEabController.saveCapabilities(contactCapabilities); 539 } 540 541 @Override 542 public RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism) { 543 return mPublishController.getDeviceCapabilities(mechanism); 544 } 545 546 @Override 547 public void refreshDeviceState(int sipCode, String reason, @RequestType int type) { 548 mDeviceState.refreshDeviceState(sipCode, reason, type); 549 } 550 551 @Override 552 public void resetDeviceState() { 553 mDeviceState.resetDeviceState(); 554 } 555 556 @Override 557 public DeviceStateResult getDeviceState() { 558 return mDeviceState.getCurrentState(); 559 } 560 561 @Override 562 public void setupResetDeviceStateTimer(long resetAfterSec) { 563 mPublishController.setupResetDeviceStateTimer(resetAfterSec); 564 } 565 566 @Override 567 public void clearResetDeviceStateTimer() { 568 mPublishController.clearResetDeviceStateTimer(); 569 } 570 571 @Override 572 public void refreshCapabilities(@NonNull List<Uri> contactNumbers, 573 @NonNull IRcsUceControllerCallback callback) throws RemoteException{ 574 logd("refreshCapabilities: " + contactNumbers.size()); 575 UceController.this.requestCapabilitiesInternal(contactNumbers, true, callback); 576 } 577 }; 578 579 @VisibleForTesting setUceControllerCallback(UceControllerCallback callback)580 public void setUceControllerCallback(UceControllerCallback callback) { 581 mCtrlCallback = callback; 582 } 583 584 /* 585 * Setup the listener to listen to the requests and updates from ImsService. 586 */ 587 private RcsFeatureManager.CapabilityExchangeEventCallback mCapabilityEventListener = 588 new RcsFeatureManager.CapabilityExchangeEventCallback() { 589 @Override 590 public void onRequestPublishCapabilities( 591 @StackPublishTriggerType int triggerType) { 592 if (isRcsConnecting()) { 593 mCachedCapabilityEvent.setRequestPublishCapabilitiesEvent(triggerType); 594 return; 595 } 596 onRequestPublishCapabilitiesFromService(triggerType); 597 } 598 599 @Override 600 public void onUnpublish() { 601 if (isRcsConnecting()) { 602 mCachedCapabilityEvent.setOnUnpublishEvent(); 603 return; 604 } 605 UceController.this.onUnpublish(); 606 } 607 608 @Override 609 public void onPublishUpdated(int reasonCode, String reasonPhrase, 610 int reasonHeaderCause, String reasonHeaderText) { 611 if (isRcsConnecting()) { 612 mCachedCapabilityEvent.setOnPublishUpdatedEvent(reasonCode, reasonPhrase, 613 reasonHeaderCause, reasonHeaderText); 614 return; 615 } 616 UceController.this.onPublishUpdated(reasonCode, reasonPhrase, 617 reasonHeaderCause, reasonHeaderText); 618 } 619 620 @Override 621 public void onRemoteCapabilityRequest(Uri contactUri, 622 List<String> remoteCapabilities, IOptionsRequestCallback cb) { 623 if (contactUri == null || remoteCapabilities == null || cb == null) { 624 logw("onRemoteCapabilityRequest: parameter cannot be null"); 625 return; 626 } 627 if (isRcsConnecting()) { 628 mCachedCapabilityEvent.setRemoteCapabilityRequestEvent(contactUri, 629 remoteCapabilities, cb); 630 return; 631 } 632 retrieveOptionsCapabilitiesForRemote(contactUri, remoteCapabilities, cb); 633 } 634 }; 635 636 /** 637 * Request to get the contacts' capabilities. This method will retrieve the capabilities from 638 * the cache If the capabilities are out of date, it will trigger another request to get the 639 * latest contact's capabilities from the network. 640 */ requestCapabilities(@onNull List<Uri> uriList, @NonNull IRcsUceControllerCallback c)641 public void requestCapabilities(@NonNull List<Uri> uriList, 642 @NonNull IRcsUceControllerCallback c) throws RemoteException { 643 requestCapabilitiesInternal(uriList, false, c); 644 } 645 requestCapabilitiesInternal(@onNull List<Uri> uriList, boolean skipFromCache, @NonNull IRcsUceControllerCallback c)646 private void requestCapabilitiesInternal(@NonNull List<Uri> uriList, boolean skipFromCache, 647 @NonNull IRcsUceControllerCallback c) throws RemoteException { 648 if (uriList == null || uriList.isEmpty() || c == null) { 649 logw("requestCapabilities: parameter is empty"); 650 if (c != null) { 651 c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 652 } 653 return; 654 } 655 656 if (isUnavailable()) { 657 logw("requestCapabilities: controller is unavailable"); 658 c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 659 return; 660 } 661 662 // Return if the device is not allowed to execute UCE requests. 663 DeviceStateResult deviceStateResult = mDeviceState.getCurrentState(); 664 if (deviceStateResult.isRequestForbidden()) { 665 int deviceState = deviceStateResult.getDeviceState(); 666 int errorCode = deviceStateResult.getErrorCode() 667 .orElse(RcsUceAdapter.ERROR_GENERIC_FAILURE); 668 long retryAfterMillis = deviceStateResult.getRequestRetryAfterMillis(); 669 logw("requestCapabilities: The device is disallowed, deviceState= " + deviceState + 670 ", errorCode=" + errorCode + ", retryAfterMillis=" + retryAfterMillis); 671 c.onError(errorCode, retryAfterMillis); 672 return; 673 } 674 675 // Trigger the capabilities request task 676 logd("requestCapabilities: size=" + uriList.size()); 677 mRequestManager.sendCapabilityRequest(uriList, skipFromCache, c); 678 } 679 680 /** 681 * Request to get the contact's capabilities. It will check the availability cache first. If 682 * the capability in the availability cache is expired then it will retrieve the capability 683 * from the network. 684 */ requestAvailability(@onNull Uri uri, @NonNull IRcsUceControllerCallback c)685 public void requestAvailability(@NonNull Uri uri, @NonNull IRcsUceControllerCallback c) 686 throws RemoteException { 687 if (uri == null || c == null) { 688 logw("requestAvailability: parameter is empty"); 689 if (c != null) { 690 c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 691 } 692 return; 693 } 694 695 if (isUnavailable()) { 696 logw("requestAvailability: controller is unavailable"); 697 c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L); 698 return; 699 } 700 701 // Return if the device is not allowed to execute UCE requests. 702 DeviceStateResult deviceStateResult = mDeviceState.getCurrentState(); 703 if (deviceStateResult.isRequestForbidden()) { 704 int deviceState = deviceStateResult.getDeviceState(); 705 int errorCode = deviceStateResult.getErrorCode() 706 .orElse(RcsUceAdapter.ERROR_GENERIC_FAILURE); 707 long retryAfterMillis = deviceStateResult.getRequestRetryAfterMillis(); 708 logw("requestAvailability: The device is disallowed, deviceState= " + deviceState + 709 ", errorCode=" + errorCode + ", retryAfterMillis=" + retryAfterMillis); 710 c.onError(errorCode, retryAfterMillis); 711 return; 712 } 713 714 // Trigger the availability request task 715 logd("requestAvailability"); 716 mRequestManager.sendAvailabilityRequest(uri, c); 717 } 718 719 /** 720 * Publish the device's capabilities. This request is triggered from the ImsService. 721 */ onRequestPublishCapabilitiesFromService(@tackPublishTriggerType int triggerType)722 public void onRequestPublishCapabilitiesFromService(@StackPublishTriggerType int triggerType) { 723 logd("onRequestPublishCapabilitiesFromService: " + triggerType); 724 // Reset the device state when the service triggers to publish the device's capabilities 725 mDeviceState.resetDeviceState(); 726 // Send the publish request. 727 mPublishController.requestPublishCapabilitiesFromService(triggerType); 728 } 729 730 /** 731 * This method is triggered by the ImsService to notify framework that the device's 732 * capabilities has been unpublished from the network. 733 */ onUnpublish()734 public void onUnpublish() { 735 logi("onUnpublish"); 736 mPublishController.onUnpublish(); 737 } 738 739 /** 740 * This method is triggered by the ImsService to notify framework that the device's 741 * publish status has been changed. 742 */ onPublishUpdated(int reasonCode, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText)743 public void onPublishUpdated(int reasonCode, String reasonPhrase, 744 int reasonHeaderCause, String reasonHeaderText) { 745 logi("onPublishUpdated"); 746 mPublishController.onPublishUpdated(reasonCode, reasonPhrase, 747 reasonHeaderCause, reasonHeaderText); 748 } 749 750 /** 751 * Request publish the device's capabilities. This request is from the ImsService to send the 752 * capabilities to the remote side. 753 */ retrieveOptionsCapabilitiesForRemote(@onNull Uri contactUri, @NonNull List<String> remoteCapabilities, @NonNull IOptionsRequestCallback c)754 public void retrieveOptionsCapabilitiesForRemote(@NonNull Uri contactUri, 755 @NonNull List<String> remoteCapabilities, @NonNull IOptionsRequestCallback c) { 756 logi("retrieveOptionsCapabilitiesForRemote"); 757 mRequestManager.retrieveCapabilitiesForRemote(contactUri, remoteCapabilities, c); 758 } 759 760 /** 761 * Register a {@link PublishStateCallback} to receive the published state changed. 762 */ registerPublishStateCallback(@onNull IRcsUcePublishStateCallback c, boolean supportPublishingState)763 public void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c, 764 boolean supportPublishingState) { 765 mPublishController.registerPublishStateCallback(c, supportPublishingState); 766 } 767 768 /** 769 * Removes an existing {@link PublishStateCallback}. 770 */ unregisterPublishStateCallback(@onNull IRcsUcePublishStateCallback c)771 public void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) { 772 mPublishController.unregisterPublishStateCallback(c); 773 } 774 775 /** 776 * Get the UCE publish state if the PUBLISH is supported by the carrier. 777 */ getUcePublishState(boolean isSupportPublishingState)778 public @PublishState int getUcePublishState(boolean isSupportPublishingState) { 779 return mPublishController.getUcePublishState(isSupportPublishingState); 780 } 781 782 /** 783 * Add new feature tags to the Set used to calculate the capabilities in PUBLISH. 784 * <p> 785 * Used for testing ONLY. 786 * @return the new capabilities that will be used for PUBLISH. 787 */ addRegistrationOverrideCapabilities(Set<String> featureTags)788 public RcsContactUceCapability addRegistrationOverrideCapabilities(Set<String> featureTags) { 789 return mPublishController.addRegistrationOverrideCapabilities(featureTags); 790 } 791 792 /** 793 * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH. 794 * <p> 795 * Used for testing ONLY. 796 * @return the new capabilities that will be used for PUBLISH. 797 */ removeRegistrationOverrideCapabilities(Set<String> featureTags)798 public RcsContactUceCapability removeRegistrationOverrideCapabilities(Set<String> featureTags) { 799 return mPublishController.removeRegistrationOverrideCapabilities(featureTags); 800 } 801 802 /** 803 * Clear all overrides in the Set used to calculate the capabilities in PUBLISH. 804 * <p> 805 * Used for testing ONLY. 806 * @return the new capabilities that will be used for PUBLISH. 807 */ clearRegistrationOverrideCapabilities()808 public RcsContactUceCapability clearRegistrationOverrideCapabilities() { 809 return mPublishController.clearRegistrationOverrideCapabilities(); 810 } 811 812 /** 813 * @return current RcsContactUceCapability instance that will be used for PUBLISH. 814 */ getLatestRcsContactUceCapability()815 public RcsContactUceCapability getLatestRcsContactUceCapability() { 816 return mPublishController.getLatestRcsContactUceCapability(); 817 } 818 819 /** 820 * Get the PIDF XML associated with the last successful publish or null if not PUBLISHed to the 821 * network. 822 */ getLastPidfXml()823 public String getLastPidfXml() { 824 return mPublishController.getLastPidfXml(); 825 } 826 827 /** 828 * Remove the device disallowed state. 829 * <p> 830 * Used for testing ONLY. 831 */ removeRequestDisallowedStatus()832 public void removeRequestDisallowedStatus() { 833 logd("removeRequestDisallowedStatus"); 834 mDeviceState.resetDeviceState(); 835 mRequestManager.resetThrottlingList(); 836 } 837 838 /** 839 * Set the milliseconds of capabilities request timeout. 840 * <p> 841 * Used for testing ONLY. 842 */ setCapabilitiesRequestTimeout(long timeoutAfterMs)843 public void setCapabilitiesRequestTimeout(long timeoutAfterMs) { 844 logd("setCapabilitiesRequestTimeout: " + timeoutAfterMs); 845 UceUtils.setCapRequestTimeoutAfterMillis(timeoutAfterMs); 846 } 847 848 /** 849 * Get the subscription ID. 850 */ getSubId()851 public int getSubId() { 852 return mSubId; 853 } 854 855 /** 856 * Check if the UceController is available. 857 * @return true if RCS is connected without destroyed. 858 */ isUnavailable()859 public boolean isUnavailable() { 860 if (!isRcsConnected() || mIsDestroyedFlag) { 861 return true; 862 } 863 return false; 864 } 865 isRcsConnecting()866 private boolean isRcsConnecting() { 867 return mRcsConnectedState == RCS_STATE_CONNECTING; 868 } 869 isRcsConnected()870 private boolean isRcsConnected() { 871 return mRcsConnectedState == RCS_STATE_CONNECTED; 872 } 873 dump(PrintWriter printWriter)874 public void dump(PrintWriter printWriter) { 875 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 876 pw.println("UceController" + "[subId: " + mSubId + "]:"); 877 pw.increaseIndent(); 878 879 pw.println("Log:"); 880 pw.increaseIndent(); 881 mLocalLog.dump(pw); 882 pw.decreaseIndent(); 883 pw.println("---"); 884 885 mPublishController.dump(pw); 886 887 pw.decreaseIndent(); 888 } 889 logd(String log)890 private void logd(String log) { 891 Log.d(LOG_TAG, getLogPrefix().append(log).toString()); 892 mLocalLog.log("[D] " + log); 893 } 894 logi(String log)895 private void logi(String log) { 896 Log.i(LOG_TAG, getLogPrefix().append(log).toString()); 897 mLocalLog.log("[I] " + log); 898 } 899 logw(String log)900 private void logw(String log) { 901 Log.w(LOG_TAG, getLogPrefix().append(log).toString()); 902 mLocalLog.log("[W] " + log); 903 } 904 getLogPrefix()905 private StringBuilder getLogPrefix() { 906 StringBuilder builder = new StringBuilder("["); 907 builder.append(mSubId); 908 builder.append("] "); 909 return builder; 910 } 911 } 912