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