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