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