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