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