• 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.request;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.os.Message;
24 import android.os.RemoteException;
25 import android.telephony.ims.RcsContactUceCapability;
26 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism;
27 import android.telephony.ims.RcsUceAdapter;
28 import android.telephony.ims.aidl.IOptionsRequestCallback;
29 import android.telephony.ims.aidl.IRcsUceControllerCallback;
30 import android.text.TextUtils;
31 import android.util.Log;
32 
33 import com.android.ims.rcs.uce.UceController;
34 import com.android.ims.rcs.uce.UceController.UceControllerCallback;
35 import com.android.ims.rcs.uce.UceDeviceState;
36 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
37 import com.android.ims.rcs.uce.eab.EabCapabilityResult;
38 import com.android.ims.rcs.uce.options.OptionsController;
39 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
40 import com.android.ims.rcs.uce.request.UceRequest.UceRequestType;
41 import com.android.ims.rcs.uce.request.UceRequestCoordinator.UceRequestUpdate;
42 import com.android.ims.rcs.uce.util.UceUtils;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.os.SomeArgs;
45 
46 import java.lang.ref.WeakReference;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Optional;
53 import java.util.stream.Collectors;
54 
55 /**
56  * Managers the capabilities requests and the availability requests from UceController.
57  */
58 public class UceRequestManager {
59 
60     private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceRequestManager";
61 
62     /**
63      * Testing interface used to mock UceUtils in testing.
64      */
65     @VisibleForTesting
66     public interface UceUtilsProxy {
67         /**
68          * The interface for {@link UceUtils#isPresenceCapExchangeEnabled(Context, int)} used for
69          * testing.
70          */
isPresenceCapExchangeEnabled(Context context, int subId)71         boolean isPresenceCapExchangeEnabled(Context context, int subId);
72 
73         /**
74          * The interface for {@link UceUtils#isPresenceSupported(Context, int)} used for testing.
75          */
isPresenceSupported(Context context, int subId)76         boolean isPresenceSupported(Context context, int subId);
77 
78         /**
79          * The interface for {@link UceUtils#isSipOptionsSupported(Context, int)} used for testing.
80          */
isSipOptionsSupported(Context context, int subId)81         boolean isSipOptionsSupported(Context context, int subId);
82 
83         /**
84          * @return true when the Presence group subscribe is enabled.
85          */
isPresenceGroupSubscribeEnabled(Context context, int subId)86         boolean isPresenceGroupSubscribeEnabled(Context context, int subId);
87 
88         /**
89          * Retrieve the maximum number of contacts that can be included in a request.
90          */
getRclMaxNumberEntries(int subId)91         int getRclMaxNumberEntries(int subId);
92 
93         /**
94          * @return true if the given phone number is blocked by the network.
95          */
isNumberBlocked(Context context, String phoneNumber)96         boolean isNumberBlocked(Context context, String phoneNumber);
97     }
98 
99     private static UceUtilsProxy sUceUtilsProxy = new UceUtilsProxy() {
100         @Override
101         public boolean isPresenceCapExchangeEnabled(Context context, int subId) {
102             return UceUtils.isPresenceCapExchangeEnabled(context, subId);
103         }
104 
105         @Override
106         public boolean isPresenceSupported(Context context, int subId) {
107             return UceUtils.isPresenceSupported(context, subId);
108         }
109 
110         @Override
111         public boolean isSipOptionsSupported(Context context, int subId) {
112             return UceUtils.isSipOptionsSupported(context, subId);
113         }
114 
115         @Override
116         public boolean isPresenceGroupSubscribeEnabled(Context context, int subId) {
117             return UceUtils.isPresenceGroupSubscribeEnabled(context, subId);
118         }
119 
120         @Override
121         public int getRclMaxNumberEntries(int subId) {
122             return UceUtils.getRclMaxNumberEntries(subId);
123         }
124 
125         @Override
126         public boolean isNumberBlocked(Context context, String phoneNumber) {
127             return UceUtils.isNumberBlocked(context, phoneNumber);
128         }
129     };
130 
131     @VisibleForTesting
setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy)132     public void setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy) {
133         sUceUtilsProxy = uceUtilsProxy;
134     }
135 
136     /**
137      * The callback interface to receive the request and the result from the UceRequest.
138      */
139     public interface RequestManagerCallback {
140         /**
141          * Notify sending the UceRequest
142          */
notifySendingRequest(long coordinator, long taskId, long delayTimeMs)143         void notifySendingRequest(long coordinator, long taskId, long delayTimeMs);
144 
145         /**
146          * Retrieve the contact capabilities from the cache.
147          */
getCapabilitiesFromCache(List<Uri> uriList)148         List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList);
149 
150         /**
151          * Retrieve the contact availability from the cache.
152          */
getAvailabilityFromCache(Uri uri)153         EabCapabilityResult getAvailabilityFromCache(Uri uri);
154 
155         /**
156          * Store the given contact capabilities to the cache.
157          */
saveCapabilities(List<RcsContactUceCapability> contactCapabilities)158         void saveCapabilities(List<RcsContactUceCapability> contactCapabilities);
159 
160         /**
161          * Retrieve the device's capabilities.
162          */
getDeviceCapabilities(@apabilityMechanism int capMechanism)163         RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int capMechanism);
164 
165         /**
166          * Get the device state to check whether the device is disallowed by the network or not.
167          */
getDeviceState()168         DeviceStateResult getDeviceState();
169 
170         /**
171          * Refresh the device state. It is called when receive the UCE request response.
172          */
refreshDeviceState(int sipCode, String reason)173         void refreshDeviceState(int sipCode, String reason);
174 
175         /**
176          * Notify that the UceRequest associated with the given taskId encounters error.
177          */
notifyRequestError(long requestCoordinatorId, long taskId)178         void notifyRequestError(long requestCoordinatorId, long taskId);
179 
180         /**
181          * Notify that the UceRequest received the onCommandError callback from the ImsService.
182          */
notifyCommandError(long requestCoordinatorId, long taskId)183         void notifyCommandError(long requestCoordinatorId, long taskId);
184 
185         /**
186          * Notify that the UceRequest received the onNetworkResponse callback from the ImsService.
187          */
notifyNetworkResponse(long requestCoordinatorId, long taskId)188         void notifyNetworkResponse(long requestCoordinatorId, long taskId);
189 
190         /**
191          * Notify that the UceRequest received the onTerminated callback from the ImsService.
192          */
notifyTerminated(long requestCoordinatorId, long taskId)193         void notifyTerminated(long requestCoordinatorId, long taskId);
194 
195         /**
196          * Notify that some contacts are not RCS anymore. It will updated the cached capabilities
197          * and trigger the callback IRcsUceControllerCallback#onCapabilitiesReceived
198          */
notifyResourceTerminated(long requestCoordinatorId, long taskId)199         void notifyResourceTerminated(long requestCoordinatorId, long taskId);
200 
201         /**
202          * Notify that the capabilities updates. It will update the cached and trigger the callback
203          * IRcsUceControllerCallback#onCapabilitiesReceived
204          */
notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId)205         void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId);
206 
207         /**
208          * Notify that some of the request capabilities can be retrieved from the cached.
209          */
notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId)210         void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId);
211 
212         /**
213          * Notify that all the requested capabilities can be retrieved from the cache. It does not
214          * need to request capabilities from the network.
215          */
notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId)216         void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId);
217 
218         /**
219          * Notify that the remote options request is done. This is sent by RemoteOptionsRequest and
220          * it will notify the RemoteOptionsCoordinator to handle it.
221          */
notifyRemoteRequestDone(long requestCoordinatorId, long taskId)222         void notifyRemoteRequestDone(long requestCoordinatorId, long taskId);
223 
224         /**
225          * Set the timer for the request timeout. It will cancel the request when the time is up.
226          */
setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs)227         void setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs);
228 
229         /**
230          * Remove the timeout timer of the capabilities request.
231          */
removeRequestTimeoutTimer(long taskId)232         void removeRequestTimeoutTimer(long taskId);
233 
234         /**
235          * Notify that the UceRequest has finished. This is sent by UceRequestCoordinator.
236          */
notifyUceRequestFinished(long requestCoordinatorId, long taskId)237         void notifyUceRequestFinished(long requestCoordinatorId, long taskId);
238 
239         /**
240          * Notify that the RequestCoordinator has finished. This is sent by UceRequestCoordinator
241          * to remove the coordinator from the UceRequestRepository.
242          */
notifyRequestCoordinatorFinished(long requestCoordinatorId)243         void notifyRequestCoordinatorFinished(long requestCoordinatorId);
244     }
245 
246     private RequestManagerCallback mRequestMgrCallback = new RequestManagerCallback() {
247         @Override
248         public void notifySendingRequest(long coordinatorId, long taskId, long delayTimeMs) {
249             mHandler.sendRequestMessage(coordinatorId, taskId, delayTimeMs);
250         }
251 
252         @Override
253         public List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList) {
254             return mControllerCallback.getCapabilitiesFromCache(uriList);
255         }
256 
257         @Override
258         public EabCapabilityResult getAvailabilityFromCache(Uri uri) {
259             return mControllerCallback.getAvailabilityFromCache(uri);
260         }
261 
262         @Override
263         public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) {
264             mControllerCallback.saveCapabilities(contactCapabilities);
265         }
266 
267         @Override
268         public RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism) {
269             return mControllerCallback.getDeviceCapabilities(mechanism);
270         }
271 
272         @Override
273         public DeviceStateResult getDeviceState() {
274             return mControllerCallback.getDeviceState();
275         }
276 
277         @Override
278         public void refreshDeviceState(int sipCode, String reason) {
279             mControllerCallback.refreshDeviceState(sipCode, reason,
280                     UceController.REQUEST_TYPE_CAPABILITY);
281         }
282 
283         @Override
284         public void notifyRequestError(long requestCoordinatorId, long taskId) {
285             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
286                     UceRequestCoordinator.REQUEST_UPDATE_ERROR);
287         }
288 
289         @Override
290         public void notifyCommandError(long requestCoordinatorId, long taskId) {
291             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
292                     UceRequestCoordinator.REQUEST_UPDATE_COMMAND_ERROR);
293         }
294 
295         @Override
296         public void notifyNetworkResponse(long requestCoordinatorId, long taskId) {
297             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
298                     UceRequestCoordinator.REQUEST_UPDATE_NETWORK_RESPONSE);
299         }
300         @Override
301         public void notifyTerminated(long requestCoordinatorId, long taskId) {
302             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
303                     UceRequestCoordinator.REQUEST_UPDATE_TERMINATED);
304         }
305         @Override
306         public void notifyResourceTerminated(long requestCoordinatorId, long taskId) {
307             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
308                     UceRequestCoordinator.REQUEST_UPDATE_RESOURCE_TERMINATED);
309         }
310         @Override
311         public void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
312             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
313                     UceRequestCoordinator.REQUEST_UPDATE_CAPABILITY_UPDATE);
314         }
315 
316         @Override
317         public void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
318             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
319                     UceRequestCoordinator.REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE);
320         }
321 
322         @Override
323         public void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId) {
324             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
325                     UceRequestCoordinator.REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK);
326         }
327 
328         @Override
329         public void notifyRemoteRequestDone(long requestCoordinatorId, long taskId) {
330             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
331                     UceRequestCoordinator.REQUEST_UPDATE_REMOTE_REQUEST_DONE);
332         }
333 
334         @Override
335         public void setRequestTimeoutTimer(long coordinatorId, long taskId, long timeoutAfterMs) {
336             mHandler.sendRequestTimeoutTimerMessage(coordinatorId, taskId, timeoutAfterMs);
337         }
338 
339         @Override
340         public void removeRequestTimeoutTimer(long taskId) {
341             mHandler.removeRequestTimeoutTimer(taskId);
342         }
343 
344         @Override
345         public void notifyUceRequestFinished(long requestCoordinatorId, long taskId) {
346             mHandler.sendRequestFinishedMessage(requestCoordinatorId, taskId);
347         }
348 
349         @Override
350         public void notifyRequestCoordinatorFinished(long requestCoordinatorId) {
351             mHandler.sendRequestCoordinatorFinishedMessage(requestCoordinatorId);
352         }
353     };
354 
355     private final int mSubId;
356     private final Context mContext;
357     private final UceRequestHandler mHandler;
358     private final UceRequestRepository mRequestRepository;
359     private volatile boolean mIsDestroyed;
360 
361     private OptionsController mOptionsCtrl;
362     private SubscribeController mSubscribeCtrl;
363     private UceControllerCallback mControllerCallback;
364 
UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c)365     public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c) {
366         mSubId = subId;
367         mContext = context;
368         mControllerCallback = c;
369         mHandler = new UceRequestHandler(this, looper);
370         mRequestRepository = new UceRequestRepository(subId, mRequestMgrCallback);
371         logi("create");
372     }
373 
374     @VisibleForTesting
UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c, UceRequestRepository requestRepository)375     public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c,
376             UceRequestRepository requestRepository) {
377         mSubId = subId;
378         mContext = context;
379         mControllerCallback = c;
380         mHandler = new UceRequestHandler(this, looper);
381         mRequestRepository = requestRepository;
382     }
383 
384     /**
385      * Set the OptionsController for requestiong capabilities by OPTIONS mechanism.
386      */
setOptionsController(OptionsController controller)387     public void setOptionsController(OptionsController controller) {
388         mOptionsCtrl = controller;
389     }
390 
391     /**
392      * Set the SubscribeController for requesting capabilities by Subscribe mechanism.
393      */
setSubscribeController(SubscribeController controller)394     public void setSubscribeController(SubscribeController controller) {
395         mSubscribeCtrl = controller;
396     }
397 
398     /**
399      * Notify that the request manager instance is destroyed.
400      */
onDestroy()401     public void onDestroy() {
402         logi("onDestroy");
403         mIsDestroyed = true;
404         mHandler.onDestroy();
405         mRequestRepository.onDestroy();
406     }
407 
408     /**
409      * Send a new capability request. It is called by UceController.
410      */
sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)411     public void sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache,
412             IRcsUceControllerCallback callback) throws RemoteException {
413         if (mIsDestroyed) {
414             callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
415             return;
416         }
417         sendRequestInternal(UceRequest.REQUEST_TYPE_CAPABILITY, uriList, skipFromCache, callback);
418     }
419 
420     /**
421      * Send a new availability request. It is called by UceController.
422      */
sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)423     public void sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)
424             throws RemoteException {
425         if (mIsDestroyed) {
426             callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
427             return;
428         }
429         sendRequestInternal(UceRequest.REQUEST_TYPE_AVAILABILITY,
430                 Collections.singletonList(uri), false /* skipFromCache */, callback);
431     }
432 
sendRequestInternal(@ceRequestType int type, List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)433     private void sendRequestInternal(@UceRequestType int type, List<Uri> uriList,
434             boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException {
435         UceRequestCoordinator requestCoordinator = null;
436         if (sUceUtilsProxy.isPresenceCapExchangeEnabled(mContext, mSubId) &&
437                 sUceUtilsProxy.isPresenceSupported(mContext, mSubId)) {
438             requestCoordinator = createSubscribeRequestCoordinator(type, uriList, skipFromCache,
439                     callback);
440         } else if (sUceUtilsProxy.isSipOptionsSupported(mContext, mSubId)) {
441             requestCoordinator = createOptionsRequestCoordinator(type, uriList, callback);
442         }
443 
444         if (requestCoordinator == null) {
445             logw("sendRequestInternal: Neither Presence nor OPTIONS are supported");
446             callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L);
447             return;
448         }
449 
450         StringBuilder builder = new StringBuilder("sendRequestInternal: ");
451         builder.append("requestType=").append(type)
452                 .append(", requestCoordinatorId=").append(requestCoordinator.getCoordinatorId())
453                 .append(", taskId={")
454                 .append(requestCoordinator.getActivatedRequestTaskIds().stream()
455                         .map(Object::toString).collect(Collectors.joining(","))).append("}");
456         logd(builder.toString());
457 
458         // Add this RequestCoordinator to the UceRequestRepository.
459         addRequestCoordinator(requestCoordinator);
460     }
461 
createSubscribeRequestCoordinator(final @UceRequestType int type, final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)462     private UceRequestCoordinator createSubscribeRequestCoordinator(final @UceRequestType int type,
463             final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) {
464         SubscribeRequestCoordinator.Builder builder;
465 
466         if (!sUceUtilsProxy.isPresenceGroupSubscribeEnabled(mContext, mSubId)) {
467             // When the group subscribe is disabled, each contact is required to be encapsulated
468             // into individual UceRequest.
469             List<UceRequest> requestList = new ArrayList<>();
470             uriList.forEach(uri -> {
471                 List<Uri> individualUri = Collections.singletonList(uri);
472                 UceRequest request = createSubscribeRequest(type, individualUri, skipFromCache);
473                 requestList.add(request);
474             });
475             builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
476                     mRequestMgrCallback);
477             builder.setCapabilitiesCallback(callback);
478         } else {
479             // Even when the group subscribe is supported by the network, the number of contacts in
480             // a UceRequest still cannot exceed the maximum.
481             List<UceRequest> requestList = new ArrayList<>();
482             final int rclMaxNumber = sUceUtilsProxy.getRclMaxNumberEntries(mSubId);
483             int numRequestCoordinators = uriList.size() / rclMaxNumber;
484             for (int count = 0; count < numRequestCoordinators; count++) {
485                 List<Uri> subUriList = new ArrayList<>();
486                 for (int index = 0; index < rclMaxNumber; index++) {
487                     subUriList.add(uriList.get(count * rclMaxNumber + index));
488                 }
489                 requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
490             }
491 
492             List<Uri> subUriList = new ArrayList<>();
493             for (int i = numRequestCoordinators * rclMaxNumber; i < uriList.size(); i++) {
494                 subUriList.add(uriList.get(i));
495             }
496             requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
497 
498             builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
499                     mRequestMgrCallback);
500             builder.setCapabilitiesCallback(callback);
501         }
502         return builder.build();
503     }
504 
createOptionsRequestCoordinator(@ceRequestType int type, List<Uri> uriList, IRcsUceControllerCallback callback)505     private UceRequestCoordinator createOptionsRequestCoordinator(@UceRequestType int type,
506             List<Uri> uriList, IRcsUceControllerCallback callback) {
507         OptionsRequestCoordinator.Builder builder;
508         List<UceRequest> requestList = new ArrayList<>();
509         uriList.forEach(uri -> {
510             List<Uri> individualUri = Collections.singletonList(uri);
511             UceRequest request = createOptionsRequest(type, individualUri, false);
512             requestList.add(request);
513         });
514         builder = new OptionsRequestCoordinator.Builder(mSubId, requestList, mRequestMgrCallback);
515         builder.setCapabilitiesCallback(callback);
516         return builder.build();
517     }
518 
createSubscribeRequest(int type, List<Uri> uriList, boolean skipFromCache)519     private CapabilityRequest createSubscribeRequest(int type, List<Uri> uriList,
520             boolean skipFromCache) {
521         CapabilityRequest request = new SubscribeRequest(mSubId, type, mRequestMgrCallback,
522                 mSubscribeCtrl);
523         request.setContactUri(uriList);
524         request.setSkipGettingFromCache(skipFromCache);
525         return request;
526     }
527 
createOptionsRequest(int type, List<Uri> uriList, boolean skipFromCache)528     private CapabilityRequest createOptionsRequest(int type, List<Uri> uriList,
529             boolean skipFromCache) {
530         CapabilityRequest request = new OptionsRequest(mSubId, type, mRequestMgrCallback,
531                 mOptionsCtrl);
532         request.setContactUri(uriList);
533         request.setSkipGettingFromCache(skipFromCache);
534         return request;
535     }
536 
537     /**
538      * Retrieve the device's capabilities. This request is from the ImsService to send the
539      * capabilities to the remote side.
540      */
retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback requestCallback)541     public void retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities,
542             IOptionsRequestCallback requestCallback) {
543         RemoteOptionsRequest request = new RemoteOptionsRequest(mSubId, mRequestMgrCallback);
544         request.setContactUri(Collections.singletonList(contactUri));
545         request.setRemoteFeatureTags(remoteCapabilities);
546 
547         // If the remote number is blocked, do not send capabilities back.
548         String number = getNumberFromUri(contactUri);
549         if (!TextUtils.isEmpty(number)) {
550             request.setIsRemoteNumberBlocked(sUceUtilsProxy.isNumberBlocked(mContext, number));
551         }
552 
553         // Create the RemoteOptionsCoordinator instance
554         RemoteOptionsCoordinator.Builder CoordBuilder = new RemoteOptionsCoordinator.Builder(
555                 mSubId, Collections.singletonList(request), mRequestMgrCallback);
556         CoordBuilder.setOptionsRequestCallback(requestCallback);
557         RemoteOptionsCoordinator requestCoordinator = CoordBuilder.build();
558 
559         StringBuilder builder = new StringBuilder("retrieveCapabilitiesForRemote: ");
560         builder.append("requestCoordinatorId ").append(requestCoordinator.getCoordinatorId())
561                 .append(", taskId={")
562                 .append(requestCoordinator.getActivatedRequestTaskIds().stream()
563                         .map(Object::toString).collect(Collectors.joining(","))).append("}");
564         logd(builder.toString());
565 
566         // Add this RequestCoordinator to the UceRequestRepository.
567         addRequestCoordinator(requestCoordinator);
568     }
569 
570     private static class UceRequestHandler extends Handler {
571         private static final int EVENT_EXECUTE_REQUEST = 1;
572         private static final int EVENT_REQUEST_UPDATED = 2;
573         private static final int EVENT_REQUEST_TIMEOUT = 3;
574         private static final int EVENT_REQUEST_FINISHED = 4;
575         private static final int EVENT_COORDINATOR_FINISHED = 5;
576 
577         private final Map<Long, SomeArgs> mRequestTimeoutTimers;
578         private final WeakReference<UceRequestManager> mUceRequestMgrRef;
579 
UceRequestHandler(UceRequestManager requestManager, Looper looper)580         public UceRequestHandler(UceRequestManager requestManager, Looper looper) {
581             super(looper);
582             mRequestTimeoutTimers = new HashMap<>();
583             mUceRequestMgrRef = new WeakReference<>(requestManager);
584         }
585 
586         /**
587          * Send the capabilities request message.
588          */
sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs)589         public void sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs) {
590             SomeArgs args = SomeArgs.obtain();
591             args.arg1 = coordinatorId;
592             args.arg2 = taskId;
593 
594             Message message = obtainMessage();
595             message.what = EVENT_EXECUTE_REQUEST;
596             message.obj = args;
597             sendMessageDelayed(message, delayTimeMs);
598         }
599 
600         /**
601          * Send the Uce request updated message.
602          */
sendRequestUpdatedMessage(Long coordinatorId, Long taskId, @UceRequestUpdate int requestEvent)603         public void sendRequestUpdatedMessage(Long coordinatorId, Long taskId,
604                 @UceRequestUpdate int requestEvent) {
605             SomeArgs args = SomeArgs.obtain();
606             args.arg1 = coordinatorId;
607             args.arg2 = taskId;
608             args.argi1 = requestEvent;
609 
610             Message message = obtainMessage();
611             message.what = EVENT_REQUEST_UPDATED;
612             message.obj = args;
613             sendMessage(message);
614         }
615 
616         /**
617          * Set the timeout timer to cancel the capabilities request.
618          */
sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs)619         public void sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs) {
620             synchronized (mRequestTimeoutTimers) {
621                 SomeArgs args = SomeArgs.obtain();
622                 args.arg1 = coordId;
623                 args.arg2 = taskId;
624 
625                 // Add the message object to the collection. It can be used to find this message
626                 // when the request is completed and remove the timeout timer.
627                 mRequestTimeoutTimers.put(taskId, args);
628 
629                 Message message = obtainMessage();
630                 message.what = EVENT_REQUEST_TIMEOUT;
631                 message.obj = args;
632                 sendMessageDelayed(message, timeoutAfterMs);
633             }
634         }
635 
636         /**
637          * Remove the timeout timer because the capabilities request is finished.
638          */
removeRequestTimeoutTimer(Long taskId)639         public void removeRequestTimeoutTimer(Long taskId) {
640             synchronized (mRequestTimeoutTimers) {
641                 SomeArgs args = mRequestTimeoutTimers.remove(taskId);
642                 if (args == null) {
643                     return;
644                 }
645                 Log.d(LOG_TAG, "removeRequestTimeoutTimer: taskId=" + taskId);
646                 removeMessages(EVENT_REQUEST_TIMEOUT, args);
647                 args.recycle();
648             }
649         }
650 
sendRequestFinishedMessage(Long coordinatorId, Long taskId)651         public void sendRequestFinishedMessage(Long coordinatorId, Long taskId) {
652             SomeArgs args = SomeArgs.obtain();
653             args.arg1 = coordinatorId;
654             args.arg2 = taskId;
655 
656             Message message = obtainMessage();
657             message.what = EVENT_REQUEST_FINISHED;
658             message.obj = args;
659             sendMessage(message);
660         }
661 
662         /**
663          * Finish the UceRequestCoordinator associated with the given id.
664          */
sendRequestCoordinatorFinishedMessage(Long coordinatorId)665         public void sendRequestCoordinatorFinishedMessage(Long coordinatorId) {
666             SomeArgs args = SomeArgs.obtain();
667             args.arg1 = coordinatorId;
668 
669             Message message = obtainMessage();
670             message.what = EVENT_COORDINATOR_FINISHED;
671             message.obj = args;
672             sendMessage(message);
673         }
674 
675         /**
676          * Remove all the messages from the handler
677          */
onDestroy()678         public void onDestroy() {
679             removeCallbacksAndMessages(null);
680             // Recycle all the arguments in the mRequestTimeoutTimers
681             synchronized (mRequestTimeoutTimers) {
682                 mRequestTimeoutTimers.forEach((taskId, args) -> {
683                     try {
684                         args.recycle();
685                     } catch (Exception e) {}
686                 });
687                 mRequestTimeoutTimers.clear();
688             }
689         }
690 
691         @Override
handleMessage(Message msg)692         public void handleMessage(Message msg) {
693             UceRequestManager requestManager = mUceRequestMgrRef.get();
694             if (requestManager == null) {
695                 return;
696             }
697             SomeArgs args = (SomeArgs) msg.obj;
698             final Long coordinatorId = (Long) args.arg1;
699             final Long taskId = (Long) Optional.ofNullable(args.arg2).orElse(-1L);
700             final Integer requestEvent = Optional.of(args.argi1).orElse(-1);
701             args.recycle();
702 
703             requestManager.logd("handleMessage: " + EVENT_DESCRIPTION.get(msg.what)
704                     + ", coordinatorId=" + coordinatorId + ", taskId=" + taskId);
705             switch (msg.what) {
706                 case EVENT_EXECUTE_REQUEST: {
707                     UceRequest request = requestManager.getUceRequest(taskId);
708                     if (request == null) {
709                         requestManager.logw("handleMessage: cannot find request, taskId=" + taskId);
710                         return;
711                     }
712                     request.executeRequest();
713                     break;
714                 }
715                 case EVENT_REQUEST_UPDATED: {
716                     UceRequestCoordinator requestCoordinator =
717                             requestManager.getRequestCoordinator(coordinatorId);
718                     if (requestCoordinator == null) {
719                         requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
720                         return;
721                     }
722                     requestCoordinator.onRequestUpdated(taskId, requestEvent);
723                     break;
724                 }
725                 case EVENT_REQUEST_TIMEOUT: {
726                     UceRequestCoordinator requestCoordinator =
727                             requestManager.getRequestCoordinator(coordinatorId);
728                     if (requestCoordinator == null) {
729                         requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
730                         return;
731                     }
732                     // The timeout timer is triggered, remove this record from the collection.
733                     synchronized (mRequestTimeoutTimers) {
734                         mRequestTimeoutTimers.remove(taskId);
735                     }
736                     // Notify that the request is timeout.
737                     requestCoordinator.onRequestUpdated(taskId,
738                             UceRequestCoordinator.REQUEST_UPDATE_TIMEOUT);
739                     break;
740                 }
741                 case EVENT_REQUEST_FINISHED: {
742                     // Notify the repository that the request is finished.
743                     requestManager.notifyRepositoryRequestFinished(taskId);
744                     break;
745                 }
746                 case EVENT_COORDINATOR_FINISHED: {
747                     UceRequestCoordinator requestCoordinator =
748                             requestManager.removeRequestCoordinator(coordinatorId);
749                     if (requestCoordinator != null) {
750                         requestCoordinator.onFinish();
751                     }
752                     break;
753                 }
754                 default: {
755                     break;
756                 }
757             }
758         }
759 
760         private static Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>();
761         static {
EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST")762             EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST");
EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE")763             EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE");
EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT")764             EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT");
EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED")765             EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED");
EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR")766             EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR");
767         }
768     }
769 
addRequestCoordinator(UceRequestCoordinator coordinator)770     private void addRequestCoordinator(UceRequestCoordinator coordinator) {
771         mRequestRepository.addRequestCoordinator(coordinator);
772     }
773 
removeRequestCoordinator(Long coordinatorId)774     private UceRequestCoordinator removeRequestCoordinator(Long coordinatorId) {
775         return mRequestRepository.removeRequestCoordinator(coordinatorId);
776     }
777 
getRequestCoordinator(Long coordinatorId)778     private UceRequestCoordinator getRequestCoordinator(Long coordinatorId) {
779         return mRequestRepository.getRequestCoordinator(coordinatorId);
780     }
781 
getUceRequest(Long taskId)782     private UceRequest getUceRequest(Long taskId) {
783         return mRequestRepository.getUceRequest(taskId);
784     }
785 
notifyRepositoryRequestFinished(Long taskId)786     private void notifyRepositoryRequestFinished(Long taskId) {
787         mRequestRepository.notifyRequestFinished(taskId);
788     }
789 
790     @VisibleForTesting
getUceRequestHandler()791     public UceRequestHandler getUceRequestHandler() {
792         return mHandler;
793     }
794 
795     @VisibleForTesting
getRequestManagerCallback()796     public RequestManagerCallback getRequestManagerCallback() {
797         return mRequestMgrCallback;
798     }
799 
logi(String log)800     private void logi(String log) {
801         Log.i(LOG_TAG, getLogPrefix().append(log).toString());
802     }
803 
logd(String log)804     private void logd(String log) {
805         Log.d(LOG_TAG, getLogPrefix().append(log).toString());
806     }
807 
logw(String log)808     private void logw(String log) {
809         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
810     }
811 
getLogPrefix()812     private StringBuilder getLogPrefix() {
813         StringBuilder builder = new StringBuilder("[");
814         builder.append(mSubId);
815         builder.append("] ");
816         return builder;
817     }
818 
getNumberFromUri(Uri uri)819     private String getNumberFromUri(Uri uri) {
820         if (uri == null) return null;
821         String number = uri.getSchemeSpecificPart();
822         String[] numberParts = number.split("[@;:]");
823 
824         if (numberParts.length == 0) {
825             return null;
826         }
827         return numberParts[0];
828     }
829 }
830