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