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