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