• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
22 import android.content.Context;
23 import android.net.Uri;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.RemoteException;
27 import android.telephony.ims.RcsContactUceCapability;
28 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism;
29 import android.telephony.ims.RcsUceAdapter;
30 import android.telephony.ims.RcsUceAdapter.PublishState;
31 import android.telephony.ims.RcsUceAdapter.StackPublishTriggerType;
32 import android.telephony.ims.SipDetails;
33 import android.telephony.ims.aidl.IOptionsRequestCallback;
34 import android.telephony.ims.aidl.IRcsUceControllerCallback;
35 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
36 import android.util.IndentingPrintWriter;
37 import android.util.LocalLog;
38 import android.util.Log;
39 
40 import com.android.ims.RcsFeatureManager;
41 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
42 import com.android.ims.rcs.uce.eab.EabCapabilityResult;
43 import com.android.ims.rcs.uce.eab.EabController;
44 import com.android.ims.rcs.uce.eab.EabControllerImpl;
45 import com.android.ims.rcs.uce.options.OptionsController;
46 import com.android.ims.rcs.uce.options.OptionsControllerImpl;
47 import com.android.ims.rcs.uce.presence.publish.PublishController;
48 import com.android.ims.rcs.uce.presence.publish.PublishControllerImpl;
49 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
50 import com.android.ims.rcs.uce.presence.subscribe.SubscribeControllerImpl;
51 import com.android.ims.rcs.uce.request.UceRequestManager;
52 import com.android.ims.rcs.uce.util.UceUtils;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.os.SomeArgs;
55 
56 import java.io.PrintWriter;
57 import java.lang.annotation.Retention;
58 import java.lang.annotation.RetentionPolicy;
59 import java.util.HashMap;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Optional;
63 import 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  */
70 public 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          */
getCapabilitiesFromCache(@onNull List<Uri> uris)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          */
getCapabilitiesFromCacheIncludingExpired(@onNull List<Uri> uris)88         List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(@NonNull List<Uri> uris);
89 
90         /**
91          * Retrieve the contact's capabilities from the availability cache.
92          */
getAvailabilityFromCache(@onNull Uri uri)93         EabCapabilityResult getAvailabilityFromCache(@NonNull Uri uri);
94 
95         /**
96          * Retrieve the contact's capabilities from the availability cache including expired
97          * capabilities
98          */
getAvailabilityFromCacheIncludingExpired(@onNull Uri uri)99         EabCapabilityResult getAvailabilityFromCacheIncludingExpired(@NonNull Uri uri);
100 
101         /**
102          * Store the given capabilities to the cache.
103          */
saveCapabilities(List<RcsContactUceCapability> contactCapabilities)104         void saveCapabilities(List<RcsContactUceCapability> contactCapabilities);
105 
106         /**
107          * Retrieve the device's capabilities.
108          */
getDeviceCapabilities(@apabilityMechanism int mechanism)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          */
refreshDeviceState(int sipCode, String reason, @RequestType int type)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          */
resetDeviceState()122         void resetDeviceState();
123 
124         /**
125          * Get the current device state to check if the device is allowed to send UCE requests.
126          */
getDeviceState()127         DeviceStateResult getDeviceState();
128 
129         /**
130          * Setup timer to exit device disallowed state.
131          */
setupResetDeviceStateTimer(long resetAfterSec)132         void setupResetDeviceStateTimer(long resetAfterSec);
133 
134         /**
135          * The device state is already reset, clear the timer.
136          */
clearResetDeviceStateTimer()137         void clearResetDeviceStateTimer();
138 
139         /**
140          * The method is called when the given contacts' capabilities are expired and need to be
141          * refreshed.
142          */
refreshCapabilities(@onNull List<Uri> contactNumbers, @NonNull IRcsUceControllerCallback callback)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 {
createRequestManager(Context context, int subId, Looper looper, UceControllerCallback callback)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          */
createEabController(Context context, int subId, UceControllerCallback c, Looper looper)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          */
createPublishController(Context context, int subId, UceControllerCallback c, Looper looper)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          */
createSubscribeController(Context context, int subId)179         SubscribeController createSubscribeController(Context context, int subId);
180 
181         /**
182          * @return an {@link OptionsController} associated with the subscription id specified.
183          */
createOptionsController(Context context, int subId)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 
CachedCapabilityEvent()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          */
setRequestPublishCapabilitiesEvent(int triggerType)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          */
setOnUnpublishEvent()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          */
setOnPublishUpdatedEvent(SipDetails details)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          */
setRemoteCapabilityRequestEvent(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback callback)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 */
getRequestPublishEvent()261         public synchronized Optional<Integer> getRequestPublishEvent() {
262             return mRequestPublishCapabilitiesEvent;
263         }
264 
265         /** @Return the cached unpublish event */
getUnpublishEvent()266         public synchronized Optional<Boolean> getUnpublishEvent() {
267             return mUnpublishEvent;
268         }
269 
270         /** @Return the cached pubilsh update event */
getPublishUpdatedEvent()271         public synchronized Optional<SipDetails> getPublishUpdatedEvent() {
272             return mPublishUpdatedEvent;
273         }
274 
275         /** @Return the cached remote capability request event */
getRemoteCapabilityRequestEvent()276         public synchronized Optional<SomeArgs> getRemoteCapabilityRequestEvent() {
277             return mRemoteCapabilityRequestEvent;
278         }
279 
280         /** Clear the cached */
clear()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 {
REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_PUBLISH, "REQUEST_TYPE_PUBLISH")309         REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_PUBLISH, "REQUEST_TYPE_PUBLISH");
REQUEST_TYPE_DESCRIPTION.put(REQUEST_TYPE_CAPABILITY, "REQUEST_TYPE_CAPABILITY")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 
UceController(Context context, int subId)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
UceController(Context context, int subId, UceDeviceState deviceState, ControllerFactory controllerFactory, RequestManagerFactory requestManagerFactory)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 
initLooper()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 
initControllers()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 
initRequestManager()393     private void initRequestManager() {
394         mRequestManager = mRequestManagerFactory.createRequestManager(mContext, mSubId, mLooper,
395                 mCtrlCallback);
396         mRequestManager.setSubscribeController(mSubscribeController);
397         mRequestManager.setOptionsController(mOptionsController);
398     }
399 
initUceDeviceState()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      */
onRcsConnected(RcsFeatureManager manager)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      */
onRcsDisconnected()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      */
onDestroy()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      */
onCarrierConfigChanged()472     public void onCarrierConfigChanged() {
473         mEabController.onCarrierConfigChanged();
474         mPublishController.onCarrierConfigChanged();
475         mSubscribeController.onCarrierConfigChanged();
476         mOptionsController.onCarrierConfigChanged();
477     }
478 
handleCachedCapabilityEvent()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
setUceControllerCallback(UceControllerCallback callback)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      */
requestCapabilities(@onNull List<Uri> uriList, @NonNull IRcsUceControllerCallback c)628     public void requestCapabilities(@NonNull List<Uri> uriList,
629             @NonNull IRcsUceControllerCallback c) throws RemoteException {
630         requestCapabilitiesInternal(uriList, false, c);
631     }
632 
requestCapabilitiesInternal(@onNull List<Uri> uriList, boolean skipFromCache, @NonNull IRcsUceControllerCallback c)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      */
requestAvailability(@onNull Uri uri, @NonNull IRcsUceControllerCallback c)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      */
onRequestPublishCapabilitiesFromService(@tackPublishTriggerType int triggerType)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      */
onUnpublish()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      */
onPublishUpdated(@onNull SipDetails details)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      */
retrieveOptionsCapabilitiesForRemote(@onNull Uri contactUri, @NonNull List<String> remoteCapabilities, @NonNull IOptionsRequestCallback c)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      */
registerPublishStateCallback(@onNull IRcsUcePublishStateCallback c, boolean supportPublishingState)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      */
unregisterPublishStateCallback(@onNull IRcsUcePublishStateCallback c)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      */
getUcePublishState(boolean isSupportPublishingState)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      */
addRegistrationOverrideCapabilities(Set<String> featureTags)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      */
removeRegistrationOverrideCapabilities(Set<String> featureTags)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      */
clearRegistrationOverrideCapabilities()793     public RcsContactUceCapability clearRegistrationOverrideCapabilities() {
794         return mPublishController.clearRegistrationOverrideCapabilities();
795     }
796 
797     /**
798      * @return current RcsContactUceCapability instance that will be used for PUBLISH.
799      */
getLatestRcsContactUceCapability()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      */
getLastPidfXml()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      */
removeRequestDisallowedStatus()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      */
setCapabilitiesRequestTimeout(long timeoutAfterMs)828     public void setCapabilitiesRequestTimeout(long timeoutAfterMs) {
829         logd("setCapabilitiesRequestTimeout: " + timeoutAfterMs);
830         UceUtils.setCapRequestTimeoutAfterMillis(timeoutAfterMs);
831     }
832 
833     /**
834      * Get the subscription ID.
835      */
getSubId()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      */
isUnavailable()844     public boolean isUnavailable() {
845         if (!isRcsConnected() || mIsDestroyedFlag) {
846             return true;
847         }
848         return false;
849     }
850 
isRcsConnecting()851     private boolean isRcsConnecting() {
852         return mRcsConnectedState == RCS_STATE_CONNECTING;
853     }
854 
isRcsConnected()855     private boolean isRcsConnected() {
856         return mRcsConnectedState == RCS_STATE_CONNECTED;
857     }
858 
dump(PrintWriter printWriter)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 
logd(String log)875     private void logd(String log) {
876         Log.d(LOG_TAG, getLogPrefix().append(log).toString());
877         mLocalLog.log("[D] " + log);
878     }
879 
logi(String log)880     private void logi(String log) {
881         Log.i(LOG_TAG, getLogPrefix().append(log).toString());
882         mLocalLog.log("[I] " + log);
883     }
884 
logw(String log)885     private void logw(String log) {
886         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
887         mLocalLog.log("[W] " + log);
888     }
889 
getLogPrefix()890     private StringBuilder getLogPrefix() {
891         StringBuilder builder = new StringBuilder("[");
892         builder.append(mSubId);
893         builder.append("] ");
894         return builder;
895     }
896 }
897