• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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.net.Uri;
20 import android.telephony.ims.RcsUceAdapter;
21 import android.telephony.ims.RcsContactUceCapability;
22 import android.util.Log;
23 
24 import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
25 import com.android.ims.rcs.uce.eab.EabCapabilityResult;
26 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
27 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
28 import com.android.ims.rcs.uce.util.UceUtils;
29 import com.android.internal.annotations.VisibleForTesting;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.stream.Collectors;
36 
37 /**
38  * The base class of the UCE request to request the capabilities from the carrier network.
39  */
40 public abstract class CapabilityRequest implements UceRequest {
41 
42     private static final String LOG_TAG = UceUtils.getLogPrefix() + "CapabilityRequest";
43 
44     protected final int mSubId;
45     protected final long mTaskId;
46     protected final List<Uri> mUriList;
47     protected final @UceRequestType int mRequestType;
48     protected final RequestManagerCallback mRequestManagerCallback;
49     protected final CapabilityRequestResponse mRequestResponse;
50 
51     protected volatile long mCoordinatorId;
52     protected volatile boolean mIsFinished;
53     protected volatile boolean mSkipGettingFromCache;
54     protected int mCurrentRetryCount;
55     protected boolean mRetryEnabled;
56 
CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback)57     public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback) {
58         mSubId = subId;
59         mRequestType = type;
60         mUriList = new ArrayList<>();
61         mRequestManagerCallback = callback;
62         mRequestResponse = new CapabilityRequestResponse();
63         mTaskId = UceUtils.generateTaskId();
64         mCurrentRetryCount = 0;
65         mRetryEnabled = false;
66     }
67 
68     @VisibleForTesting
CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback, CapabilityRequestResponse requestResponse)69     public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback,
70             CapabilityRequestResponse requestResponse) {
71         mSubId = subId;
72         mRequestType = type;
73         mUriList = new ArrayList<>();
74         mRequestManagerCallback = callback;
75         mRequestResponse = requestResponse;
76         mTaskId = UceUtils.generateTaskId();
77         mCurrentRetryCount = 0;
78         mRetryEnabled = false;
79     }
80 
81     @Override
setRequestCoordinatorId(long coordinatorId)82     public void setRequestCoordinatorId(long coordinatorId) {
83         mCoordinatorId = coordinatorId;
84     }
85 
86     @Override
getRequestCoordinatorId()87     public long getRequestCoordinatorId() {
88         return mCoordinatorId;
89     }
90 
91     @Override
getTaskId()92     public long getTaskId() {
93         return mTaskId;
94     }
95 
96     @Override
onFinish()97     public void onFinish() {
98         mIsFinished = true;
99         // Remove the timeout timer of this request
100         mRequestManagerCallback.removeRequestTimeoutTimer(mTaskId);
101     }
102 
103     @Override
setContactUri(List<Uri> uris)104     public void setContactUri(List<Uri> uris) {
105         mUriList.addAll(uris);
106         mRequestResponse.setRequestContacts(uris);
107     }
108 
getContactUri()109     public List<Uri> getContactUri() {
110         return Collections.unmodifiableList(mUriList);
111     }
112 
113     /**
114      * Set to check if this request should be getting the capabilities from the cache. The flag is
115      * set when the request is triggered by the capability polling service. The contacts from the
116      * capability polling service are already expired, skip checking from the cache.
117      */
setSkipGettingFromCache(boolean skipFromCache)118     public void setSkipGettingFromCache(boolean skipFromCache) {
119         mSkipGettingFromCache = skipFromCache;
120     }
121 
122     /**
123      * Return if the capabilities request should skip getting from the cache. The flag is set when
124      * the request is triggered by the capability polling service and the request doesn't need to
125      * check the cache again.
126      */
isSkipGettingFromCache()127     private boolean isSkipGettingFromCache() {
128         return mSkipGettingFromCache;
129     }
130 
131     /**
132      * @return The RequestResponse instance associated with this request.
133      */
getRequestResponse()134     public CapabilityRequestResponse getRequestResponse() {
135         return mRequestResponse;
136     }
137 
138     /**
139      * Start executing this request.
140      */
141     @Override
executeRequest()142     public void executeRequest() {
143         // Return if this request is not allowed to be executed.
144         if (!isRequestAllowed()) {
145             logd("executeRequest: The request is not allowed.");
146             mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId);
147             return;
148         }
149 
150         // Get the contact capabilities from the cache including the expired capabilities.
151         final List<EabCapabilityResult> eabResultList = getCapabilitiesFromCache();
152 
153         // Get all the unexpired capabilities from the EAB result list and add to the response.
154         final List<RcsContactUceCapability> cachedCapList = isSkipGettingFromCache() ?
155                 Collections.EMPTY_LIST : getUnexpiredCapabilities(eabResultList);
156         mRequestResponse.addCachedCapabilities(cachedCapList);
157 
158         logd("executeRequest: cached capabilities size=" + cachedCapList.size());
159 
160         // Get the rest contacts which are not in the cache or has expired.
161         final List<Uri> expiredUris = getRequestingFromNetworkUris(cachedCapList);
162 
163         // For those uris that are not in the cache or have expired, we should request their
164         // capabilities from the network. However, we still need to check whether these contacts
165         // are in the throttling list. If the contact is in the throttling list, even if it has
166         // expired, we will get the cached capabilities.
167         final List<RcsContactUceCapability> throttlingUris =
168                 getFromThrottlingList(expiredUris, eabResultList);
169         mRequestResponse.addCachedCapabilities(throttlingUris);
170 
171         logd("executeRequest: contacts in throttling list size=" + throttlingUris.size());
172 
173         // Notify that the cached capabilities are updated.
174         if (!cachedCapList.isEmpty() || !throttlingUris.isEmpty()) {
175             mRequestManagerCallback.notifyCachedCapabilitiesUpdated(mCoordinatorId, mTaskId);
176         }
177 
178         // Get the rest contacts which need to request capabilities from the network.
179         List<Uri> requestCapUris = getRequestingFromNetworkUris(cachedCapList, throttlingUris);
180 
181         logd("executeRequest: requestCapUris size=" + requestCapUris.size());
182 
183         // Notify that it doesn't need to request capabilities from the network when all the
184         // requested capabilities can be retrieved from cache. Otherwise, it needs to request
185         // capabilities from the network for those contacts which cannot retrieve capabilities from
186         // the cache.
187         if (requestCapUris.isEmpty()) {
188             mRequestManagerCallback.notifyNoNeedRequestFromNetwork(mCoordinatorId, mTaskId);
189         } else {
190             requestCapabilities(requestCapUris);
191         }
192     }
193 
getRetryCount()194     public int getRetryCount() {
195         return mCurrentRetryCount;
196     }
197 
setRetryCount(int retryCount)198     public void setRetryCount(int retryCount) {
199         mCurrentRetryCount = retryCount;
200     }
201 
isRetryEnabled()202     public boolean isRetryEnabled() {
203         return mRetryEnabled;
204     }
205 
setRetryEnabled(boolean enabled)206     public void setRetryEnabled(boolean enabled) {
207         mRetryEnabled = enabled;
208     }
209 
210     // Check whether this request is allowed to be executed or not.
isRequestAllowed()211     private boolean isRequestAllowed() {
212         if (mUriList == null || mUriList.isEmpty()) {
213             logw("isRequestAllowed: uri is empty");
214             mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
215             return false;
216         }
217 
218         if (mIsFinished) {
219             logw("isRequestAllowed: This request is finished");
220             mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
221             return false;
222         }
223 
224         DeviceStateResult deviceStateResult = mRequestManagerCallback.getDeviceState();
225         if (deviceStateResult.isRequestForbidden()) {
226             logw("isRequestAllowed: The device is disallowed.");
227             mRequestResponse.setRequestInternalError(
228                     deviceStateResult.getErrorCode().orElse(RcsUceAdapter.ERROR_GENERIC_FAILURE));
229             return false;
230         }
231         return true;
232     }
233 
234     // Get the cached capabilities by the given request type.
getCapabilitiesFromCache()235     private List<EabCapabilityResult> getCapabilitiesFromCache() {
236         List<EabCapabilityResult> resultList = null;
237         if (mRequestType == REQUEST_TYPE_CAPABILITY) {
238             resultList = mRequestManagerCallback.getCapabilitiesFromCacheIncludingExpired(mUriList);
239         } else if (mRequestType == REQUEST_TYPE_AVAILABILITY) {
240             // Always get the first element if the request type is availability.
241             Uri uri = mUriList.get(0);
242             EabCapabilityResult eabResult =
243                     mRequestManagerCallback.getAvailabilityFromCacheIncludingExpired(uri);
244             resultList = new ArrayList<>();
245             resultList.add(eabResult);
246         }
247         if (resultList == null) {
248             return Collections.emptyList();
249         }
250         return resultList;
251     }
252 
253     /**
254      * Get the unexpired contact capabilities from the given EAB result list.
255      * @param list the query result from the EAB
256      */
getUnexpiredCapabilities(List<EabCapabilityResult> list)257     private List<RcsContactUceCapability> getUnexpiredCapabilities(List<EabCapabilityResult> list) {
258         return list.stream()
259                 .filter(Objects::nonNull)
260                 .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL)
261                 .map(EabCapabilityResult::getContactCapabilities)
262                 .filter(Objects::nonNull)
263                 .collect(Collectors.toList());
264     }
265 
266     /**
267      * Get the contact uris which cannot retrieve capabilities from the cache.
268      * @param cachedCapList The capabilities which are already stored in the cache.
269      */
getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList)270     private List<Uri> getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList) {
271         return mUriList.stream()
272                 .filter(uri -> cachedCapList.stream()
273                         .noneMatch(cap -> cap.getContactUri().equals(uri)))
274                         .collect(Collectors.toList());
275     }
276 
277     /**
278      * Get the contact uris which cannot retrieve capabilities from the cache.
279      * @param cachedCapList The capabilities which are already stored in the cache.
280      * @param throttlingUris The capabilities which are in the throttling list.
281      */
getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList, List<RcsContactUceCapability> throttlingUris)282     private List<Uri> getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList,
283             List<RcsContactUceCapability> throttlingUris) {
284         // We won't request the network query for those contacts in the cache and in the
285         // throttling list. Merging the two list and get the rest contact uris.
286         List<RcsContactUceCapability> notNetworkQueryList = new ArrayList<>(cachedCapList);
287         notNetworkQueryList.addAll(throttlingUris);
288         return getRequestingFromNetworkUris(notNetworkQueryList);
289     }
290 
291     /**
292      * Get the contact capabilities for those uri are in the throttling list. If the contact uri is
293      * in the throttling list, the capabilities will be retrieved from cache even if it has expired.
294      * If the capabilities cannot be found, return the non-RCS contact capabilities instead.
295      * @param expiredUris the expired/unknown uris to check whether are in the throttling list
296      * @return the contact capabilities for the uris are in the throttling list
297      */
getFromThrottlingList(final List<Uri> expiredUris, final List<EabCapabilityResult> eabResultList)298     private List<RcsContactUceCapability> getFromThrottlingList(final List<Uri> expiredUris,
299             final List<EabCapabilityResult> eabResultList) {
300         List<RcsContactUceCapability> resultList = new ArrayList<>();
301         List<RcsContactUceCapability> notFoundFromCacheList = new ArrayList<>();
302 
303         // Retrieve the uris put in the throttling list from the expired/unknown contacts.
304         List<Uri> throttlingUris = mRequestManagerCallback.getInThrottlingListUris(expiredUris);
305 
306         // For these uris in the throttling list, check whether their capabilities are in the cache.
307         List<EabCapabilityResult> throttlingUriFoundInEab = new ArrayList<>();
308         for (Uri uri : throttlingUris) {
309             for (EabCapabilityResult eabResult : eabResultList) {
310                 if (eabResult.getContact().equals(uri)) {
311                     throttlingUriFoundInEab.add(eabResult);
312                     break;
313                 }
314             }
315         }
316 
317         throttlingUriFoundInEab.forEach(eabResult -> {
318             if (eabResult.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL ||
319                 eabResult.getStatus() == EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE) {
320                 // The capabilities are found, add to the result list
321                 resultList.add(eabResult.getContactCapabilities());
322             } else {
323                 // Cannot get the capabilities from cache, create the non-RCS capabilities instead.
324                 notFoundFromCacheList.add(PidfParserUtils.getNotFoundContactCapabilities(
325                         eabResult.getContact()));
326             }
327         });
328 
329         if (!notFoundFromCacheList.isEmpty()) {
330             resultList.addAll(notFoundFromCacheList);
331         }
332 
333         logd("getFromThrottlingList: requesting uris in the list size=" + throttlingUris.size() +
334                 ", generate non-RCS size=" + notFoundFromCacheList.size());
335         return resultList;
336     }
337 
338     /**
339      * Set the timeout timer of this request.
340      */
setupRequestTimeoutTimer()341     protected void setupRequestTimeoutTimer() {
342         long timeoutAfterMs = UceUtils.getCapRequestTimeoutAfterMillis();
343         logd("setupRequestTimeoutTimer(ms): " + timeoutAfterMs);
344         mRequestManagerCallback.setRequestTimeoutTimer(mCoordinatorId, mTaskId, timeoutAfterMs);
345     }
346 
347     /*
348      * Requests capabilities from IMS. The inherited request is required to override this method
349      * to define the behavior of requesting capabilities.
350      */
requestCapabilities(List<Uri> requestCapUris)351     protected abstract void requestCapabilities(List<Uri> requestCapUris);
352 
logd(String log)353     protected void logd(String log) {
354         Log.d(LOG_TAG, getLogPrefix().append(log).toString());
355     }
356 
logw(String log)357     protected void logw(String log) {
358         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
359     }
360 
logi(String log)361     protected void logi(String log) {
362         Log.i(LOG_TAG, getLogPrefix().append(log).toString());
363     }
364 
getLogPrefix()365     private StringBuilder getLogPrefix() {
366         StringBuilder builder = new StringBuilder("[");
367         builder.append(mSubId).append("][taskId=").append(mTaskId).append("] ");
368         return builder;
369     }
370 }
371