• 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.TelephonyManager;
26 import android.telephony.ims.RcsContactUceCapability;
27 import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism;
28 import android.telephony.ims.RcsUceAdapter;
29 import android.telephony.ims.aidl.IOptionsRequestCallback;
30 import android.telephony.ims.aidl.IRcsUceControllerCallback;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 import com.android.i18n.phonenumbers.NumberParseException;
35 import com.android.i18n.phonenumbers.PhoneNumberUtil;
36 import com.android.i18n.phonenumbers.Phonenumber;
37 import com.android.ims.rcs.uce.UceController;
38 import com.android.ims.rcs.uce.UceController.UceControllerCallback;
39 import com.android.ims.rcs.uce.UceDeviceState;
40 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
41 import com.android.ims.rcs.uce.eab.EabCapabilityResult;
42 import com.android.ims.rcs.uce.options.OptionsController;
43 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
44 import com.android.ims.rcs.uce.request.UceRequest.UceRequestType;
45 import com.android.ims.rcs.uce.request.UceRequestCoordinator.UceRequestUpdate;
46 import com.android.ims.rcs.uce.util.UceUtils;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.os.SomeArgs;
49 
50 import java.lang.ref.WeakReference;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Objects;
57 import java.util.Optional;
58 import java.util.stream.Collectors;
59 
60 /**
61  * Managers the capabilities requests and the availability requests from UceController.
62  */
63 public class UceRequestManager {
64 
65     private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceRequestManager";
66 
67     /**
68      * When enabled, skip the request queue for requests that have numbers with valid cached
69      * capabilities and return that cached info directly.
70      * Note: This also has a CTS test associated with it, so this can not be disabled without
71      * disabling the corresponding RcsUceAdapterTest#testCacheQuerySuccessWhenNetworkBlocked test.
72      */
73     private static final boolean FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS = true;
74 
75     /**
76      * Testing interface used to mock UceUtils in testing.
77      */
78     @VisibleForTesting
79     public interface UceUtilsProxy {
80         /**
81          * The interface for {@link UceUtils#isPresenceCapExchangeEnabled(Context, int)} used for
82          * testing.
83          */
isPresenceCapExchangeEnabled(Context context, int subId)84         boolean isPresenceCapExchangeEnabled(Context context, int subId);
85 
86         /**
87          * The interface for {@link UceUtils#isPresenceSupported(Context, int)} used for testing.
88          */
isPresenceSupported(Context context, int subId)89         boolean isPresenceSupported(Context context, int subId);
90 
91         /**
92          * The interface for {@link UceUtils#isSipOptionsSupported(Context, int)} used for testing.
93          */
isSipOptionsSupported(Context context, int subId)94         boolean isSipOptionsSupported(Context context, int subId);
95 
96         /**
97          * @return true when the Presence group subscribe is enabled.
98          */
isPresenceGroupSubscribeEnabled(Context context, int subId)99         boolean isPresenceGroupSubscribeEnabled(Context context, int subId);
100 
101         /**
102          * Retrieve the maximum number of contacts that can be included in a request.
103          */
getRclMaxNumberEntries(int subId)104         int getRclMaxNumberEntries(int subId);
105 
106         /**
107          * @return true if the given phone number is blocked by the network.
108          */
isNumberBlocked(Context context, String phoneNumber)109         boolean isNumberBlocked(Context context, String phoneNumber);
110     }
111 
112     private static UceUtilsProxy sUceUtilsProxy = new UceUtilsProxy() {
113         @Override
114         public boolean isPresenceCapExchangeEnabled(Context context, int subId) {
115             return UceUtils.isPresenceCapExchangeEnabled(context, subId);
116         }
117 
118         @Override
119         public boolean isPresenceSupported(Context context, int subId) {
120             return UceUtils.isPresenceSupported(context, subId);
121         }
122 
123         @Override
124         public boolean isSipOptionsSupported(Context context, int subId) {
125             return UceUtils.isSipOptionsSupported(context, subId);
126         }
127 
128         @Override
129         public boolean isPresenceGroupSubscribeEnabled(Context context, int subId) {
130             return UceUtils.isPresenceGroupSubscribeEnabled(context, subId);
131         }
132 
133         @Override
134         public int getRclMaxNumberEntries(int subId) {
135             return UceUtils.getRclMaxNumberEntries(subId);
136         }
137 
138         @Override
139         public boolean isNumberBlocked(Context context, String phoneNumber) {
140             return UceUtils.isNumberBlocked(context, phoneNumber);
141         }
142     };
143 
144     @VisibleForTesting
setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy)145     public void setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy) {
146         sUceUtilsProxy = uceUtilsProxy;
147     }
148 
149     /**
150      * The callback interface to receive the request and the result from the UceRequest.
151      */
152     public interface RequestManagerCallback {
153         /**
154          * Notify sending the UceRequest
155          */
notifySendingRequest(long coordinator, long taskId, long delayTimeMs)156         void notifySendingRequest(long coordinator, long taskId, long delayTimeMs);
157 
158         /**
159          * Retrieve the contact capabilities from the cache.
160          */
getCapabilitiesFromCache(List<Uri> uriList)161         List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList);
162 
163         /**
164          * Retrieve the contact capabilities from the cache including the expired capabilities.
165          */
getCapabilitiesFromCacheIncludingExpired(List<Uri> uriList)166         List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uriList);
167 
168         /**
169          * Retrieve the contact availability from the cache.
170          */
getAvailabilityFromCache(Uri uri)171         EabCapabilityResult getAvailabilityFromCache(Uri uri);
172 
173         /**
174          * Retrieve the contact availability from the cache including the expired capabilities.
175          */
getAvailabilityFromCacheIncludingExpired(Uri uri)176         EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri);
177 
178         /**
179          * Store the given contact capabilities to the cache.
180          */
saveCapabilities(List<RcsContactUceCapability> contactCapabilities)181         void saveCapabilities(List<RcsContactUceCapability> contactCapabilities);
182 
183         /**
184          * Retrieve the device's capabilities.
185          */
getDeviceCapabilities(@apabilityMechanism int capMechanism)186         RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int capMechanism);
187 
188         /**
189          * Get the device state to check whether the device is disallowed by the network or not.
190          */
getDeviceState()191         DeviceStateResult getDeviceState();
192 
193         /**
194          * Refresh the device state. It is called when receive the UCE request response.
195          */
refreshDeviceState(int sipCode, String reason)196         void refreshDeviceState(int sipCode, String reason);
197 
198         /**
199          * Notify that the UceRequest associated with the given taskId encounters error.
200          */
notifyRequestError(long requestCoordinatorId, long taskId)201         void notifyRequestError(long requestCoordinatorId, long taskId);
202 
203         /**
204          * Notify that the UceRequest received the onCommandError callback from the ImsService.
205          */
notifyCommandError(long requestCoordinatorId, long taskId)206         void notifyCommandError(long requestCoordinatorId, long taskId);
207 
208         /**
209          * Notify that the UceRequest received the onNetworkResponse callback from the ImsService.
210          */
notifyNetworkResponse(long requestCoordinatorId, long taskId)211         void notifyNetworkResponse(long requestCoordinatorId, long taskId);
212 
213         /**
214          * Notify that the UceRequest received the onTerminated callback from the ImsService.
215          */
notifyTerminated(long requestCoordinatorId, long taskId)216         void notifyTerminated(long requestCoordinatorId, long taskId);
217 
218         /**
219          * Notify that some contacts are not RCS anymore. It will updated the cached capabilities
220          * and trigger the callback IRcsUceControllerCallback#onCapabilitiesReceived
221          */
notifyResourceTerminated(long requestCoordinatorId, long taskId)222         void notifyResourceTerminated(long requestCoordinatorId, long taskId);
223 
224         /**
225          * Notify that the capabilities updates. It will update the cached and trigger the callback
226          * IRcsUceControllerCallback#onCapabilitiesReceived
227          */
notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId)228         void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId);
229 
230         /**
231          * Notify that some of the request capabilities can be retrieved from the cached.
232          */
notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId)233         void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId);
234 
235         /**
236          * Notify that all the requested capabilities can be retrieved from the cache. It does not
237          * need to request capabilities from the network.
238          */
notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId)239         void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId);
240 
241         /**
242          * Notify that the remote options request is done. This is sent by RemoteOptionsRequest and
243          * it will notify the RemoteOptionsCoordinator to handle it.
244          */
notifyRemoteRequestDone(long requestCoordinatorId, long taskId)245         void notifyRemoteRequestDone(long requestCoordinatorId, long taskId);
246 
247         /**
248          * Set the timer for the request timeout. It will cancel the request when the time is up.
249          */
setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs)250         void setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs);
251 
252         /**
253          * Remove the timeout timer of the capabilities request.
254          */
removeRequestTimeoutTimer(long taskId)255         void removeRequestTimeoutTimer(long taskId);
256 
257         /**
258          * Notify that the UceRequest has finished. This is sent by UceRequestCoordinator.
259          */
notifyUceRequestFinished(long requestCoordinatorId, long taskId)260         void notifyUceRequestFinished(long requestCoordinatorId, long taskId);
261 
262         /**
263          * Notify that the RequestCoordinator has finished. This is sent by UceRequestCoordinator
264          * to remove the coordinator from the UceRequestRepository.
265          */
notifyRequestCoordinatorFinished(long requestCoordinatorId)266         void notifyRequestCoordinatorFinished(long requestCoordinatorId);
267 
268         /**
269          * Check whether the given uris are in the throttling list.
270          * @param uriList the uris to check if it is in the throttling list
271          * @return the uris in the throttling list
272          */
getInThrottlingListUris(List<Uri> uriList)273         List<Uri> getInThrottlingListUris(List<Uri> uriList);
274 
275         /**
276          * Add the given uris to the throttling list because the capabilities request result
277          * is inconclusive.
278          */
addToThrottlingList(List<Uri> uriList, int sipCode)279         void addToThrottlingList(List<Uri> uriList, int sipCode);
280     }
281 
282     private RequestManagerCallback mRequestMgrCallback = new RequestManagerCallback() {
283         @Override
284         public void notifySendingRequest(long coordinatorId, long taskId, long delayTimeMs) {
285             mHandler.sendRequestMessage(coordinatorId, taskId, delayTimeMs);
286         }
287 
288         @Override
289         public List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList) {
290             return mControllerCallback.getCapabilitiesFromCache(uriList);
291         }
292 
293         @Override
294         public List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uris) {
295             return mControllerCallback.getCapabilitiesFromCacheIncludingExpired(uris);
296         }
297 
298         @Override
299         public EabCapabilityResult getAvailabilityFromCache(Uri uri) {
300             return mControllerCallback.getAvailabilityFromCache(uri);
301         }
302 
303         @Override
304         public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri uri) {
305             return mControllerCallback.getAvailabilityFromCacheIncludingExpired(uri);
306         }
307 
308         @Override
309         public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) {
310             mControllerCallback.saveCapabilities(contactCapabilities);
311         }
312 
313         @Override
314         public RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism) {
315             return mControllerCallback.getDeviceCapabilities(mechanism);
316         }
317 
318         @Override
319         public DeviceStateResult getDeviceState() {
320             return mControllerCallback.getDeviceState();
321         }
322 
323         @Override
324         public void refreshDeviceState(int sipCode, String reason) {
325             mControllerCallback.refreshDeviceState(sipCode, reason,
326                     UceController.REQUEST_TYPE_CAPABILITY);
327         }
328 
329         @Override
330         public void notifyRequestError(long requestCoordinatorId, long taskId) {
331             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
332                     UceRequestCoordinator.REQUEST_UPDATE_ERROR);
333         }
334 
335         @Override
336         public void notifyCommandError(long requestCoordinatorId, long taskId) {
337             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
338                     UceRequestCoordinator.REQUEST_UPDATE_COMMAND_ERROR);
339         }
340 
341         @Override
342         public void notifyNetworkResponse(long requestCoordinatorId, long taskId) {
343             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
344                     UceRequestCoordinator.REQUEST_UPDATE_NETWORK_RESPONSE);
345         }
346         @Override
347         public void notifyTerminated(long requestCoordinatorId, long taskId) {
348             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
349                     UceRequestCoordinator.REQUEST_UPDATE_TERMINATED);
350         }
351         @Override
352         public void notifyResourceTerminated(long requestCoordinatorId, long taskId) {
353             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
354                     UceRequestCoordinator.REQUEST_UPDATE_RESOURCE_TERMINATED);
355         }
356         @Override
357         public void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
358             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
359                     UceRequestCoordinator.REQUEST_UPDATE_CAPABILITY_UPDATE);
360         }
361 
362         @Override
363         public void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
364             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
365                     UceRequestCoordinator.REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE);
366         }
367 
368         @Override
369         public void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId) {
370             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
371                     UceRequestCoordinator.REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK);
372         }
373 
374         @Override
375         public void notifyRemoteRequestDone(long requestCoordinatorId, long taskId) {
376             mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
377                     UceRequestCoordinator.REQUEST_UPDATE_REMOTE_REQUEST_DONE);
378         }
379 
380         @Override
381         public void setRequestTimeoutTimer(long coordinatorId, long taskId, long timeoutAfterMs) {
382             mHandler.sendRequestTimeoutTimerMessage(coordinatorId, taskId, timeoutAfterMs);
383         }
384 
385         @Override
386         public void removeRequestTimeoutTimer(long taskId) {
387             mHandler.removeRequestTimeoutTimer(taskId);
388         }
389 
390         @Override
391         public void notifyUceRequestFinished(long requestCoordinatorId, long taskId) {
392             mHandler.sendRequestFinishedMessage(requestCoordinatorId, taskId);
393         }
394 
395         @Override
396         public void notifyRequestCoordinatorFinished(long requestCoordinatorId) {
397             mHandler.sendRequestCoordinatorFinishedMessage(requestCoordinatorId);
398         }
399 
400         @Override
401         public List<Uri> getInThrottlingListUris(List<Uri> uriList) {
402             return mThrottlingList.getInThrottlingListUris(uriList);
403         }
404 
405         @Override
406         public void addToThrottlingList(List<Uri> uriList, int sipCode) {
407             mThrottlingList.addToThrottlingList(uriList, sipCode);
408         }
409     };
410 
411     private final int mSubId;
412     private final Context mContext;
413     private final UceRequestHandler mHandler;
414     private final UceRequestRepository mRequestRepository;
415     private final ContactThrottlingList mThrottlingList;
416     private volatile boolean mIsDestroyed;
417 
418     private OptionsController mOptionsCtrl;
419     private SubscribeController mSubscribeCtrl;
420     private UceControllerCallback mControllerCallback;
421 
UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c)422     public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c) {
423         mSubId = subId;
424         mContext = context;
425         mControllerCallback = c;
426         mHandler = new UceRequestHandler(this, looper);
427         mThrottlingList = new ContactThrottlingList(mSubId);
428         mRequestRepository = new UceRequestRepository(subId, mRequestMgrCallback);
429         logi("create");
430     }
431 
432     @VisibleForTesting
UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c, UceRequestRepository requestRepository)433     public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c,
434             UceRequestRepository requestRepository) {
435         mSubId = subId;
436         mContext = context;
437         mControllerCallback = c;
438         mHandler = new UceRequestHandler(this, looper);
439         mRequestRepository = requestRepository;
440         mThrottlingList = new ContactThrottlingList(mSubId);
441     }
442 
443     /**
444      * Set the OptionsController for requestiong capabilities by OPTIONS mechanism.
445      */
setOptionsController(OptionsController controller)446     public void setOptionsController(OptionsController controller) {
447         mOptionsCtrl = controller;
448     }
449 
450     /**
451      * Set the SubscribeController for requesting capabilities by Subscribe mechanism.
452      */
setSubscribeController(SubscribeController controller)453     public void setSubscribeController(SubscribeController controller) {
454         mSubscribeCtrl = controller;
455     }
456 
457     /**
458      * Notify that the request manager instance is destroyed.
459      */
onDestroy()460     public void onDestroy() {
461         logi("onDestroy");
462         mIsDestroyed = true;
463         mHandler.onDestroy();
464         mThrottlingList.reset();
465         mRequestRepository.onDestroy();
466     }
467 
468     /**
469      * Clear the throttling list.
470      */
resetThrottlingList()471     public void resetThrottlingList() {
472         mThrottlingList.reset();
473     }
474 
475     /**
476      * Send a new capability request. It is called by UceController.
477      */
sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)478     public void sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache,
479             IRcsUceControllerCallback callback) throws RemoteException {
480         if (mIsDestroyed) {
481             callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
482             return;
483         }
484         sendRequestInternal(UceRequest.REQUEST_TYPE_CAPABILITY, uriList, skipFromCache, callback);
485     }
486 
487     /**
488      * Send a new availability request. It is called by UceController.
489      */
sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)490     public void sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)
491             throws RemoteException {
492         if (mIsDestroyed) {
493             callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
494             return;
495         }
496         sendRequestInternal(UceRequest.REQUEST_TYPE_AVAILABILITY,
497                 Collections.singletonList(uri), false /* skipFromCache */, callback);
498     }
499 
sendRequestInternal(@ceRequestType int type, List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)500     private void sendRequestInternal(@UceRequestType int type, List<Uri> uriList,
501             boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException {
502         UceRequestCoordinator requestCoordinator = null;
503         List<Uri> nonCachedUris = uriList;
504         if (FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS && !skipFromCache) {
505             nonCachedUris = sendCachedCapInfoToRequester(type, uriList, callback);
506             if (uriList.size() != nonCachedUris.size()) {
507                 logd("sendRequestInternal: shortcut queue for caps - request reduced from "
508                         + uriList.size() + " entries to " + nonCachedUris.size() + " entries");
509             } else {
510                 logd("sendRequestInternal: shortcut queue for caps - no cached caps.");
511             }
512             if (nonCachedUris.isEmpty()) {
513                 logd("sendRequestInternal: shortcut complete, sending success result");
514                 callback.onComplete();
515                 return;
516             }
517         }
518         if (sUceUtilsProxy.isPresenceCapExchangeEnabled(mContext, mSubId) &&
519                 sUceUtilsProxy.isPresenceSupported(mContext, mSubId)) {
520             requestCoordinator = createSubscribeRequestCoordinator(type, nonCachedUris,
521                     skipFromCache, callback);
522         } else if (sUceUtilsProxy.isSipOptionsSupported(mContext, mSubId)) {
523             requestCoordinator = createOptionsRequestCoordinator(type, nonCachedUris, callback);
524         }
525 
526         if (requestCoordinator == null) {
527             logw("sendRequestInternal: Neither Presence nor OPTIONS are supported");
528             callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L);
529             return;
530         }
531 
532         StringBuilder builder = new StringBuilder("sendRequestInternal: ");
533         builder.append("requestType=").append(type)
534                 .append(", requestCoordinatorId=").append(requestCoordinator.getCoordinatorId())
535                 .append(", taskId={")
536                 .append(requestCoordinator.getActivatedRequestTaskIds().stream()
537                         .map(Object::toString).collect(Collectors.joining(","))).append("}");
538         logd(builder.toString());
539 
540         // Add this RequestCoordinator to the UceRequestRepository.
541         addRequestCoordinator(requestCoordinator);
542     }
543 
544     /**
545      * Try to get the valid capabilities associated with the URI List specified from the EAB cache.
546      * If one or more of the numbers from the URI List have valid cached capabilities, return them
547      * to the requester now and remove them from the returned List of URIs that will require a
548      * network query.
549      * @param type The type of query
550      * @param uriList The List of URIs that we want to send cached capabilities for
551      * @param callback The callback used to communicate with the remote requester
552      * @return The List of URIs that were not found in the capability cache and will require a
553      * network query.
554      */
sendCachedCapInfoToRequester(int type, List<Uri> uriList, IRcsUceControllerCallback callback)555     private List<Uri> sendCachedCapInfoToRequester(int type, List<Uri> uriList,
556             IRcsUceControllerCallback callback) {
557         List<Uri> nonCachedUris = new ArrayList<>(uriList);
558         List<RcsContactUceCapability> numbersWithCachedCaps =
559                 getCapabilitiesFromCache(type, nonCachedUris);
560         try {
561             if (!numbersWithCachedCaps.isEmpty()) {
562                 logd("sendCachedCapInfoToRequester: cached caps found for "
563                         + numbersWithCachedCaps.size() + " entries. Notifying requester.");
564                 // Notify caller of the numbers that have cached caps
565                 callback.onCapabilitiesReceived(numbersWithCachedCaps);
566             }
567         } catch (RemoteException e) {
568             logw("sendCachedCapInfoToRequester, error sending cap info back to requester: " + e);
569         }
570         // remove these numbers from the numbers pending a cap query from the network.
571         for (RcsContactUceCapability c : numbersWithCachedCaps) {
572             nonCachedUris.removeIf(uri -> c.getContactUri().equals(uri));
573         }
574         return nonCachedUris;
575     }
576 
577     /**
578      * Get the capabilities for the List of given URIs
579      * @param requestType The request type, used to determine if the cached info is "fresh" enough.
580      * @param uriList The List of URIs that we will be requesting cached capabilities for.
581      * @return A list of capabilities corresponding to the subset of numbers that still have
582      * valid cache data associated with them.
583      */
getCapabilitiesFromCache(int requestType, List<Uri> uriList)584     private List<RcsContactUceCapability> getCapabilitiesFromCache(int requestType,
585             List<Uri> uriList) {
586         List<EabCapabilityResult> resultList = Collections.emptyList();
587         if (requestType == UceRequest.REQUEST_TYPE_CAPABILITY) {
588             resultList = mRequestMgrCallback.getCapabilitiesFromCache(uriList);
589         } else if (requestType == UceRequest.REQUEST_TYPE_AVAILABILITY) {
590             // Always get the first element if the request type is availability.
591             resultList = Collections.singletonList(
592                     mRequestMgrCallback.getAvailabilityFromCache(uriList.get(0)));
593         }
594         // Map from EabCapabilityResult -> RcsContactUceCapability.
595         // Pull out only items that have valid cache data.
596         return resultList.stream().filter(Objects::nonNull)
597                 .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL)
598                 .map(EabCapabilityResult::getContactCapabilities)
599                 .filter(Objects::nonNull).collect(Collectors.toList());
600     }
601 
createSubscribeRequestCoordinator(final @UceRequestType int type, final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback)602     private UceRequestCoordinator createSubscribeRequestCoordinator(final @UceRequestType int type,
603             final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) {
604         SubscribeRequestCoordinator.Builder builder;
605 
606         if (!sUceUtilsProxy.isPresenceGroupSubscribeEnabled(mContext, mSubId)) {
607             // When the group subscribe is disabled, each contact is required to be encapsulated
608             // into individual UceRequest.
609             List<UceRequest> requestList = new ArrayList<>();
610             uriList.forEach(uri -> {
611                 List<Uri> individualUri = Collections.singletonList(uri);
612                 // Entity-uri, which is used as a request-uri, uses only a single subscription case
613                 List<RcsContactUceCapability> capabilities =
614                         getCapabilitiesFromCache(type, individualUri);
615                 if (!capabilities.isEmpty()) {
616                     RcsContactUceCapability capability = capabilities.get(0);
617                     Uri entityUri = capability.getEntityUri();
618                     if (entityUri != null) {
619                         // The query uri has been replaced by the stored entity uri.
620                         individualUri = Collections.singletonList(entityUri);
621                     } else {
622                         if (UceUtils.isSipUriForPresenceSubscribeEnabled(mContext, mSubId)) {
623                             individualUri = Collections.singletonList(getSipUriFromUri(uri));
624                         }
625                     }
626                 } else {
627                     if (UceUtils.isSipUriForPresenceSubscribeEnabled(mContext, mSubId)) {
628                         individualUri = Collections.singletonList(getSipUriFromUri(uri));
629                     }
630                 }
631                 UceRequest request = createSubscribeRequest(type, individualUri, skipFromCache);
632                 requestList.add(request);
633             });
634             builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
635                     mRequestMgrCallback);
636             builder.setCapabilitiesCallback(callback);
637         } else {
638             // Even when the group subscribe is supported by the network, the number of contacts in
639             // a UceRequest still cannot exceed the maximum.
640             List<UceRequest> requestList = new ArrayList<>();
641             final int rclMaxNumber = sUceUtilsProxy.getRclMaxNumberEntries(mSubId);
642             int numRequestCoordinators = uriList.size() / rclMaxNumber;
643             for (int count = 0; count < numRequestCoordinators; count++) {
644                 List<Uri> subUriList = new ArrayList<>();
645                 for (int index = 0; index < rclMaxNumber; index++) {
646                     subUriList.add(uriList.get(count * rclMaxNumber + index));
647                 }
648                 requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
649             }
650 
651             List<Uri> subUriList = new ArrayList<>();
652             for (int i = numRequestCoordinators * rclMaxNumber; i < uriList.size(); i++) {
653                 subUriList.add(uriList.get(i));
654             }
655             requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
656 
657             builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
658                     mRequestMgrCallback);
659             builder.setCapabilitiesCallback(callback);
660         }
661         return builder.build();
662     }
663 
createOptionsRequestCoordinator(@ceRequestType int type, List<Uri> uriList, IRcsUceControllerCallback callback)664     private UceRequestCoordinator createOptionsRequestCoordinator(@UceRequestType int type,
665             List<Uri> uriList, IRcsUceControllerCallback callback) {
666         OptionsRequestCoordinator.Builder builder;
667         List<UceRequest> requestList = new ArrayList<>();
668         uriList.forEach(uri -> {
669             List<Uri> individualUri = Collections.singletonList(uri);
670             UceRequest request = createOptionsRequest(type, individualUri, false);
671             requestList.add(request);
672         });
673         builder = new OptionsRequestCoordinator.Builder(mSubId, requestList, mRequestMgrCallback);
674         builder.setCapabilitiesCallback(callback);
675         return builder.build();
676     }
677 
createSubscribeRequest(int type, List<Uri> uriList, boolean skipFromCache)678     private CapabilityRequest createSubscribeRequest(int type, List<Uri> uriList,
679             boolean skipFromCache) {
680         CapabilityRequest request = new SubscribeRequest(mSubId, type, mRequestMgrCallback,
681                 mSubscribeCtrl);
682         request.setContactUri(uriList);
683         request.setSkipGettingFromCache(skipFromCache);
684         return request;
685     }
686 
createOptionsRequest(int type, List<Uri> uriList, boolean skipFromCache)687     private CapabilityRequest createOptionsRequest(int type, List<Uri> uriList,
688             boolean skipFromCache) {
689         CapabilityRequest request = new OptionsRequest(mSubId, type, mRequestMgrCallback,
690                 mOptionsCtrl);
691         request.setContactUri(uriList);
692         request.setSkipGettingFromCache(skipFromCache);
693         return request;
694     }
695 
696     /**
697      * Retrieve the device's capabilities. This request is from the ImsService to send the
698      * capabilities to the remote side.
699      */
retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities, IOptionsRequestCallback requestCallback)700     public void retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities,
701             IOptionsRequestCallback requestCallback) {
702         RemoteOptionsRequest request = new RemoteOptionsRequest(mSubId, mRequestMgrCallback);
703         request.setContactUri(Collections.singletonList(contactUri));
704         request.setRemoteFeatureTags(remoteCapabilities);
705 
706         // If the remote number is blocked, do not send capabilities back.
707         String number = getNumberFromUri(contactUri);
708         if (!TextUtils.isEmpty(number)) {
709             request.setIsRemoteNumberBlocked(sUceUtilsProxy.isNumberBlocked(mContext, number));
710         }
711 
712         // Create the RemoteOptionsCoordinator instance
713         RemoteOptionsCoordinator.Builder CoordBuilder = new RemoteOptionsCoordinator.Builder(
714                 mSubId, Collections.singletonList(request), mRequestMgrCallback);
715         CoordBuilder.setOptionsRequestCallback(requestCallback);
716         RemoteOptionsCoordinator requestCoordinator = CoordBuilder.build();
717 
718         StringBuilder builder = new StringBuilder("retrieveCapabilitiesForRemote: ");
719         builder.append("requestCoordinatorId ").append(requestCoordinator.getCoordinatorId())
720                 .append(", taskId={")
721                 .append(requestCoordinator.getActivatedRequestTaskIds().stream()
722                         .map(Object::toString).collect(Collectors.joining(","))).append("}");
723         logd(builder.toString());
724 
725         // Add this RequestCoordinator to the UceRequestRepository.
726         addRequestCoordinator(requestCoordinator);
727     }
728 
729     private static class UceRequestHandler extends Handler {
730         private static final int EVENT_EXECUTE_REQUEST = 1;
731         private static final int EVENT_REQUEST_UPDATED = 2;
732         private static final int EVENT_REQUEST_TIMEOUT = 3;
733         private static final int EVENT_REQUEST_FINISHED = 4;
734         private static final int EVENT_COORDINATOR_FINISHED = 5;
735 
736         private final Map<Long, SomeArgs> mRequestTimeoutTimers;
737         private final WeakReference<UceRequestManager> mUceRequestMgrRef;
738 
UceRequestHandler(UceRequestManager requestManager, Looper looper)739         public UceRequestHandler(UceRequestManager requestManager, Looper looper) {
740             super(looper);
741             mRequestTimeoutTimers = new HashMap<>();
742             mUceRequestMgrRef = new WeakReference<>(requestManager);
743         }
744 
745         /**
746          * Send the capabilities request message.
747          */
sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs)748         public void sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs) {
749             SomeArgs args = SomeArgs.obtain();
750             args.arg1 = coordinatorId;
751             args.arg2 = taskId;
752 
753             Message message = obtainMessage();
754             message.what = EVENT_EXECUTE_REQUEST;
755             message.obj = args;
756             sendMessageDelayed(message, delayTimeMs);
757         }
758 
759         /**
760          * Send the Uce request updated message.
761          */
sendRequestUpdatedMessage(Long coordinatorId, Long taskId, @UceRequestUpdate int requestEvent)762         public void sendRequestUpdatedMessage(Long coordinatorId, Long taskId,
763                 @UceRequestUpdate int requestEvent) {
764             SomeArgs args = SomeArgs.obtain();
765             args.arg1 = coordinatorId;
766             args.arg2 = taskId;
767             args.argi1 = requestEvent;
768 
769             Message message = obtainMessage();
770             message.what = EVENT_REQUEST_UPDATED;
771             message.obj = args;
772             sendMessage(message);
773         }
774 
775         /**
776          * Set the timeout timer to cancel the capabilities request.
777          */
sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs)778         public void sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs) {
779             synchronized (mRequestTimeoutTimers) {
780                 SomeArgs args = SomeArgs.obtain();
781                 args.arg1 = coordId;
782                 args.arg2 = taskId;
783 
784                 // Add the message object to the collection. It can be used to find this message
785                 // when the request is completed and remove the timeout timer.
786                 mRequestTimeoutTimers.put(taskId, args);
787 
788                 Message message = obtainMessage();
789                 message.what = EVENT_REQUEST_TIMEOUT;
790                 message.obj = args;
791                 sendMessageDelayed(message, timeoutAfterMs);
792             }
793         }
794 
795         /**
796          * Remove the timeout timer because the capabilities request is finished.
797          */
removeRequestTimeoutTimer(Long taskId)798         public void removeRequestTimeoutTimer(Long taskId) {
799             synchronized (mRequestTimeoutTimers) {
800                 SomeArgs args = mRequestTimeoutTimers.remove(taskId);
801                 if (args == null) {
802                     return;
803                 }
804                 Log.d(LOG_TAG, "removeRequestTimeoutTimer: taskId=" + taskId);
805                 removeMessages(EVENT_REQUEST_TIMEOUT, args);
806                 args.recycle();
807             }
808         }
809 
sendRequestFinishedMessage(Long coordinatorId, Long taskId)810         public void sendRequestFinishedMessage(Long coordinatorId, Long taskId) {
811             SomeArgs args = SomeArgs.obtain();
812             args.arg1 = coordinatorId;
813             args.arg2 = taskId;
814 
815             Message message = obtainMessage();
816             message.what = EVENT_REQUEST_FINISHED;
817             message.obj = args;
818             sendMessage(message);
819         }
820 
821         /**
822          * Finish the UceRequestCoordinator associated with the given id.
823          */
sendRequestCoordinatorFinishedMessage(Long coordinatorId)824         public void sendRequestCoordinatorFinishedMessage(Long coordinatorId) {
825             SomeArgs args = SomeArgs.obtain();
826             args.arg1 = coordinatorId;
827 
828             Message message = obtainMessage();
829             message.what = EVENT_COORDINATOR_FINISHED;
830             message.obj = args;
831             sendMessage(message);
832         }
833 
834         /**
835          * Remove all the messages from the handler
836          */
onDestroy()837         public void onDestroy() {
838             removeCallbacksAndMessages(null);
839             // Recycle all the arguments in the mRequestTimeoutTimers
840             synchronized (mRequestTimeoutTimers) {
841                 mRequestTimeoutTimers.forEach((taskId, args) -> {
842                     try {
843                         args.recycle();
844                     } catch (Exception e) {}
845                 });
846                 mRequestTimeoutTimers.clear();
847             }
848         }
849 
850         @Override
handleMessage(Message msg)851         public void handleMessage(Message msg) {
852             UceRequestManager requestManager = mUceRequestMgrRef.get();
853             if (requestManager == null) {
854                 return;
855             }
856             SomeArgs args = (SomeArgs) msg.obj;
857             final Long coordinatorId = (Long) args.arg1;
858             final Long taskId = (Long) Optional.ofNullable(args.arg2).orElse(-1L);
859             final Integer requestEvent = Optional.ofNullable(args.argi1).orElse(-1);
860             args.recycle();
861 
862             requestManager.logd("handleMessage: " + EVENT_DESCRIPTION.get(msg.what)
863                     + ", coordinatorId=" + coordinatorId + ", taskId=" + taskId);
864             switch (msg.what) {
865                 case EVENT_EXECUTE_REQUEST: {
866                     UceRequest request = requestManager.getUceRequest(taskId);
867                     if (request == null) {
868                         requestManager.logw("handleMessage: cannot find request, taskId=" + taskId);
869                         return;
870                     }
871                     request.executeRequest();
872                     break;
873                 }
874                 case EVENT_REQUEST_UPDATED: {
875                     UceRequestCoordinator requestCoordinator =
876                             requestManager.getRequestCoordinator(coordinatorId);
877                     if (requestCoordinator == null) {
878                         requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
879                         return;
880                     }
881                     requestCoordinator.onRequestUpdated(taskId, requestEvent);
882                     break;
883                 }
884                 case EVENT_REQUEST_TIMEOUT: {
885                     UceRequestCoordinator requestCoordinator =
886                             requestManager.getRequestCoordinator(coordinatorId);
887                     if (requestCoordinator == null) {
888                         requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
889                         return;
890                     }
891                     // The timeout timer is triggered, remove this record from the collection.
892                     synchronized (mRequestTimeoutTimers) {
893                         mRequestTimeoutTimers.remove(taskId);
894                     }
895                     // Notify that the request is timeout.
896                     requestCoordinator.onRequestUpdated(taskId,
897                             UceRequestCoordinator.REQUEST_UPDATE_TIMEOUT);
898                     break;
899                 }
900                 case EVENT_REQUEST_FINISHED: {
901                     // Notify the repository that the request is finished.
902                     requestManager.notifyRepositoryRequestFinished(taskId);
903                     break;
904                 }
905                 case EVENT_COORDINATOR_FINISHED: {
906                     UceRequestCoordinator requestCoordinator =
907                             requestManager.removeRequestCoordinator(coordinatorId);
908                     if (requestCoordinator != null) {
909                         requestCoordinator.onFinish();
910                     }
911                     break;
912                 }
913                 default: {
914                     break;
915                 }
916             }
917         }
918 
919         private static Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>();
920         static {
EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST")921             EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST");
EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE")922             EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE");
EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT")923             EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT");
EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED")924             EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED");
EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR")925             EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR");
926         }
927     }
928 
addRequestCoordinator(UceRequestCoordinator coordinator)929     private void addRequestCoordinator(UceRequestCoordinator coordinator) {
930         mRequestRepository.addRequestCoordinator(coordinator);
931     }
932 
removeRequestCoordinator(Long coordinatorId)933     private UceRequestCoordinator removeRequestCoordinator(Long coordinatorId) {
934         return mRequestRepository.removeRequestCoordinator(coordinatorId);
935     }
936 
getRequestCoordinator(Long coordinatorId)937     private UceRequestCoordinator getRequestCoordinator(Long coordinatorId) {
938         return mRequestRepository.getRequestCoordinator(coordinatorId);
939     }
940 
getUceRequest(Long taskId)941     private UceRequest getUceRequest(Long taskId) {
942         return mRequestRepository.getUceRequest(taskId);
943     }
944 
notifyRepositoryRequestFinished(Long taskId)945     private void notifyRepositoryRequestFinished(Long taskId) {
946         mRequestRepository.notifyRequestFinished(taskId);
947     }
948 
getSipUriFromUri(Uri uri)949     private Uri getSipUriFromUri(Uri uri) {
950         Uri convertedUri = uri;
951         String number = convertedUri.getSchemeSpecificPart();
952         String[] numberParts = number.split("[@;:]");
953         number = numberParts[0];
954 
955         TelephonyManager manager = mContext.getSystemService(TelephonyManager.class);
956         if (manager.getIsimDomain() == null) {
957             return convertedUri;
958         }
959         String simCountryIso = manager.getSimCountryIso();
960         if (TextUtils.isEmpty(simCountryIso)) {
961             return convertedUri;
962         }
963         simCountryIso = simCountryIso.toUpperCase();
964         PhoneNumberUtil util = PhoneNumberUtil.getInstance();
965         try {
966             Phonenumber.PhoneNumber phoneNumber = util.parse(number, simCountryIso);
967             number = util.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
968             String sipUri = "sip:" + number + "@" + manager.getIsimDomain();
969             convertedUri = Uri.parse(sipUri);
970         } catch (NumberParseException e) {
971             Log.w(LOG_TAG, "formatNumber: could not format " + number + ", error: " + e);
972         }
973         return convertedUri;
974     }
975 
976     @VisibleForTesting
getUceRequestHandler()977     public UceRequestHandler getUceRequestHandler() {
978         return mHandler;
979     }
980 
981     @VisibleForTesting
getRequestManagerCallback()982     public RequestManagerCallback getRequestManagerCallback() {
983         return mRequestMgrCallback;
984     }
985 
logi(String log)986     private void logi(String log) {
987         Log.i(LOG_TAG, getLogPrefix().append(log).toString());
988     }
989 
logd(String log)990     private void logd(String log) {
991         Log.d(LOG_TAG, getLogPrefix().append(log).toString());
992     }
993 
logw(String log)994     private void logw(String log) {
995         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
996     }
997 
getLogPrefix()998     private StringBuilder getLogPrefix() {
999         StringBuilder builder = new StringBuilder("[");
1000         builder.append(mSubId);
1001         builder.append("] ");
1002         return builder;
1003     }
1004 
getNumberFromUri(Uri uri)1005     private String getNumberFromUri(Uri uri) {
1006         if (uri == null) return null;
1007         String number = uri.getSchemeSpecificPart();
1008         String[] numberParts = number.split("[@;:]");
1009 
1010         if (numberParts.length == 0) {
1011             return null;
1012         }
1013         return numberParts[0];
1014     }
1015 }
1016