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.request.UceRequestManager.RequestManagerCallback; 27 import com.android.ims.rcs.uce.util.UceUtils; 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.stream.Collectors; 35 36 /** 37 * The base class of the UCE request to request the capabilities from the carrier network. 38 */ 39 public abstract class CapabilityRequest implements UceRequest { 40 41 private static final String LOG_TAG = UceUtils.getLogPrefix() + "CapabilityRequest"; 42 43 protected final int mSubId; 44 protected final long mTaskId; 45 protected final List<Uri> mUriList; 46 protected final @UceRequestType int mRequestType; 47 protected final RequestManagerCallback mRequestManagerCallback; 48 protected final CapabilityRequestResponse mRequestResponse; 49 50 protected volatile long mCoordinatorId; 51 protected volatile boolean mIsFinished; 52 protected volatile boolean mSkipGettingFromCache; 53 CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback)54 public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback) { 55 mSubId = subId; 56 mRequestType = type; 57 mUriList = new ArrayList<>(); 58 mRequestManagerCallback = callback; 59 mRequestResponse = new CapabilityRequestResponse(); 60 mTaskId = UceUtils.generateTaskId(); 61 } 62 63 @VisibleForTesting CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback, CapabilityRequestResponse requestResponse)64 public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback, 65 CapabilityRequestResponse requestResponse) { 66 mSubId = subId; 67 mRequestType = type; 68 mUriList = new ArrayList<>(); 69 mRequestManagerCallback = callback; 70 mRequestResponse = requestResponse; 71 mTaskId = UceUtils.generateTaskId(); 72 } 73 74 @Override setRequestCoordinatorId(long coordinatorId)75 public void setRequestCoordinatorId(long coordinatorId) { 76 mCoordinatorId = coordinatorId; 77 } 78 79 @Override getRequestCoordinatorId()80 public long getRequestCoordinatorId() { 81 return mCoordinatorId; 82 } 83 84 @Override getTaskId()85 public long getTaskId() { 86 return mTaskId; 87 } 88 89 @Override onFinish()90 public void onFinish() { 91 mIsFinished = true; 92 // Remove the timeout timer of this request 93 mRequestManagerCallback.removeRequestTimeoutTimer(mTaskId); 94 } 95 96 @Override setContactUri(List<Uri> uris)97 public void setContactUri(List<Uri> uris) { 98 mUriList.addAll(uris); 99 mRequestResponse.setRequestContacts(uris); 100 } 101 getContactUri()102 public List<Uri> getContactUri() { 103 return Collections.unmodifiableList(mUriList); 104 } 105 106 /** 107 * Set to check if this request should be getting the capabilities from the cache. The flag is 108 * set when the request is triggered by the capability polling service. The contacts from the 109 * capability polling service are already expired, skip checking from the cache. 110 */ setSkipGettingFromCache(boolean skipFromCache)111 public void setSkipGettingFromCache(boolean skipFromCache) { 112 mSkipGettingFromCache = skipFromCache; 113 } 114 115 /** 116 * Return if the capabilities request should skip getting from the cache. The flag is set when 117 * the request is triggered by the capability polling service and the request doesn't need to 118 * check the cache again. 119 */ isSkipGettingFromCache()120 private boolean isSkipGettingFromCache() { 121 return mSkipGettingFromCache; 122 } 123 124 /** 125 * @return The RequestResponse instance associated with this request. 126 */ getRequestResponse()127 public CapabilityRequestResponse getRequestResponse() { 128 return mRequestResponse; 129 } 130 131 /** 132 * Start executing this request. 133 */ 134 @Override executeRequest()135 public void executeRequest() { 136 // Return if this request is not allowed to be executed. 137 if (!isRequestAllowed()) { 138 logd("executeRequest: The request is not allowed."); 139 mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); 140 return; 141 } 142 143 // Get the capabilities from the cache. 144 final List<RcsContactUceCapability> cachedCapList 145 = isSkipGettingFromCache() ? Collections.EMPTY_LIST : getCapabilitiesFromCache(); 146 mRequestResponse.addCachedCapabilities(cachedCapList); 147 148 logd("executeRequest: cached capabilities size=" + cachedCapList.size()); 149 150 // Notify that the cached capabilities are updated. 151 if (!cachedCapList.isEmpty()) { 152 mRequestManagerCallback.notifyCachedCapabilitiesUpdated(mCoordinatorId, mTaskId); 153 } 154 155 // Get the rest contacts which need to request capabilities from the network. 156 final List<Uri> requestCapUris = getRequestingFromNetworkUris(cachedCapList); 157 158 logd("executeRequest: requestCapUris size=" + requestCapUris.size()); 159 160 // Notify that it doesn't need to request capabilities from the network when all the 161 // requested capabilities can be retrieved from cache. Otherwise, it needs to request 162 // capabilities from the network for those contacts which cannot retrieve capabilities from 163 // the cache. 164 if (requestCapUris.isEmpty()) { 165 mRequestManagerCallback.notifyNoNeedRequestFromNetwork(mCoordinatorId, mTaskId); 166 } else { 167 requestCapabilities(requestCapUris); 168 } 169 } 170 171 // Check whether this request is allowed to be executed or not. isRequestAllowed()172 private boolean isRequestAllowed() { 173 if (mUriList == null || mUriList.isEmpty()) { 174 logw("isRequestAllowed: uri is empty"); 175 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 176 return false; 177 } 178 179 if (mIsFinished) { 180 logw("isRequestAllowed: This request is finished"); 181 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 182 return false; 183 } 184 185 DeviceStateResult deviceStateResult = mRequestManagerCallback.getDeviceState(); 186 if (deviceStateResult.isRequestForbidden()) { 187 logw("isRequestAllowed: The device is disallowed."); 188 mRequestResponse.setRequestInternalError( 189 deviceStateResult.getErrorCode().orElse(RcsUceAdapter.ERROR_GENERIC_FAILURE)); 190 return false; 191 } 192 return true; 193 } 194 195 // Get the cached capabilities by the given request type. getCapabilitiesFromCache()196 private List<RcsContactUceCapability> getCapabilitiesFromCache() { 197 List<EabCapabilityResult> resultList = null; 198 if (mRequestType == REQUEST_TYPE_CAPABILITY) { 199 resultList = mRequestManagerCallback.getCapabilitiesFromCache(mUriList); 200 } else if (mRequestType == REQUEST_TYPE_AVAILABILITY) { 201 // Always get the first element if the request type is availability. 202 Uri uri = mUriList.get(0); 203 EabCapabilityResult eabResult = mRequestManagerCallback.getAvailabilityFromCache(uri); 204 resultList = new ArrayList<>(); 205 resultList.add(eabResult); 206 } 207 if (resultList == null) { 208 return Collections.emptyList(); 209 } 210 return resultList.stream() 211 .filter(Objects::nonNull) 212 .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL) 213 .map(EabCapabilityResult::getContactCapabilities) 214 .filter(Objects::nonNull) 215 .collect(Collectors.toList()); 216 } 217 218 /** 219 * Get the contact uris which cannot retrieve capabilities from the cache. 220 * @param cachedCapList The capabilities which are already stored in the cache. 221 */ getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList)222 private List<Uri> getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList) { 223 return mUriList.stream() 224 .filter(uri -> cachedCapList.stream() 225 .noneMatch(cap -> cap.getContactUri().equals(uri))) 226 .collect(Collectors.toList()); 227 } 228 229 /** 230 * Set the timeout timer of this request. 231 */ setupRequestTimeoutTimer()232 protected void setupRequestTimeoutTimer() { 233 long timeoutAfterMs = UceUtils.getCapRequestTimeoutAfterMillis(); 234 logd("setupRequestTimeoutTimer(ms): " + timeoutAfterMs); 235 mRequestManagerCallback.setRequestTimeoutTimer(mCoordinatorId, mTaskId, timeoutAfterMs); 236 } 237 238 /* 239 * Requests capabilities from IMS. The inherited request is required to override this method 240 * to define the behavior of requesting capabilities. 241 */ requestCapabilities(List<Uri> requestCapUris)242 protected abstract void requestCapabilities(List<Uri> requestCapUris); 243 logd(String log)244 protected void logd(String log) { 245 Log.d(LOG_TAG, getLogPrefix().append(log).toString()); 246 } 247 logw(String log)248 protected void logw(String log) { 249 Log.w(LOG_TAG, getLogPrefix().append(log).toString()); 250 } 251 logi(String log)252 protected void logi(String log) { 253 Log.i(LOG_TAG, getLogPrefix().append(log).toString()); 254 } 255 getLogPrefix()256 private StringBuilder getLogPrefix() { 257 StringBuilder builder = new StringBuilder("["); 258 builder.append(mSubId).append("][taskId=").append(mTaskId).append("] "); 259 return builder; 260 } 261 } 262