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.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.telephony.ims.RcsUceAdapter; 23 import android.telephony.ims.SipDetails; 24 import android.util.Log; 25 26 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; 27 import com.android.ims.rcs.uce.util.UceUtils; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Optional; 36 import java.util.stream.Collectors; 37 38 /** 39 * The base class that is responsible for the communication and interaction between the UceRequests. 40 */ 41 public abstract class UceRequestCoordinator { 42 43 private static final String LOG_TAG = UceUtils.getLogPrefix() + "ReqCoordinator"; 44 45 /** 46 * The UceRequest encountered error. 47 */ 48 public static final int REQUEST_UPDATE_ERROR = 0; 49 50 /** 51 * The UceRequest received the onCommandError callback. 52 */ 53 public static final int REQUEST_UPDATE_COMMAND_ERROR = 1; 54 55 /** 56 * The UceRequest received the onNetworkResponse callback. 57 */ 58 public static final int REQUEST_UPDATE_NETWORK_RESPONSE = 2; 59 60 /** 61 * The UceRequest received the onNotifyCapabilitiesUpdate callback. 62 */ 63 public static final int REQUEST_UPDATE_CAPABILITY_UPDATE = 3; 64 65 /** 66 * The UceRequest received the onResourceTerminated callback. 67 */ 68 public static final int REQUEST_UPDATE_RESOURCE_TERMINATED = 4; 69 70 /** 71 * The UceRequest retrieve the valid capabilities from the cache. 72 */ 73 public static final int REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE = 5; 74 75 /** 76 * The UceRequest receive the onTerminated callback. 77 */ 78 public static final int REQUEST_UPDATE_TERMINATED = 6; 79 80 /** 81 * The UceRequest does not need to request capabilities to network because all the capabilities 82 * can be retrieved from the cache. 83 */ 84 public static final int REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK = 7; 85 86 /** 87 * The remote options request is done. 88 */ 89 public static final int REQUEST_UPDATE_REMOTE_REQUEST_DONE = 8; 90 91 /** 92 * The capabilities request is timeout. 93 */ 94 public static final int REQUEST_UPDATE_TIMEOUT = 9; 95 96 @IntDef(value = { 97 REQUEST_UPDATE_ERROR, 98 REQUEST_UPDATE_COMMAND_ERROR, 99 REQUEST_UPDATE_NETWORK_RESPONSE, 100 REQUEST_UPDATE_TERMINATED, 101 REQUEST_UPDATE_RESOURCE_TERMINATED, 102 REQUEST_UPDATE_CAPABILITY_UPDATE, 103 REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE, 104 REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK, 105 REQUEST_UPDATE_REMOTE_REQUEST_DONE, 106 REQUEST_UPDATE_TIMEOUT, 107 }, prefix="REQUEST_UPDATE_") 108 @Retention(RetentionPolicy.SOURCE) 109 @interface UceRequestUpdate {} 110 111 protected static Map<Integer, String> REQUEST_EVENT_DESC = new HashMap<>(); 112 static { REQUEST_EVENT_DESC.put(REQUEST_UPDATE_ERROR, "REQUEST_ERROR")113 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_ERROR, "REQUEST_ERROR"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_COMMAND_ERROR, "RETRIEVE_COMMAND_ERROR")114 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_COMMAND_ERROR, "RETRIEVE_COMMAND_ERROR"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_NETWORK_RESPONSE, "REQUEST_NETWORK_RESPONSE")115 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_NETWORK_RESPONSE, "REQUEST_NETWORK_RESPONSE"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_TERMINATED, "REQUEST_TERMINATED")116 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_TERMINATED, "REQUEST_TERMINATED"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_RESOURCE_TERMINATED, "REQUEST_RESOURCE_TERMINATED")117 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_RESOURCE_TERMINATED, "REQUEST_RESOURCE_TERMINATED"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_CAPABILITY_UPDATE, "REQUEST_CAPABILITY_UPDATE")118 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_CAPABILITY_UPDATE, "REQUEST_CAPABILITY_UPDATE"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE, "REQUEST_CACHE_CAP_UPDATE")119 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE, "REQUEST_CACHE_CAP_UPDATE"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK, "NO_NEED_REQUEST")120 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK, "NO_NEED_REQUEST"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_REMOTE_REQUEST_DONE, "REMOTE_REQUEST_DONE")121 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_REMOTE_REQUEST_DONE, "REMOTE_REQUEST_DONE"); REQUEST_EVENT_DESC.put(REQUEST_UPDATE_TIMEOUT, "REQUEST_TIMEOUT")122 REQUEST_EVENT_DESC.put(REQUEST_UPDATE_TIMEOUT, "REQUEST_TIMEOUT"); 123 } 124 125 /** 126 * The result of the UceRequest. This is the used by the RequestCoordinator to record the 127 * result of each sub-requests. 128 */ 129 static class RequestResult { 130 /** 131 * Create a RequestResult that successfully completes the request. 132 * @param taskId the task id of the UceRequest 133 */ createSuccessResult(long taskId)134 public static RequestResult createSuccessResult(long taskId) { 135 return new RequestResult(taskId); 136 } 137 138 /** 139 * Create a RequestResult that successfully completes the request. 140 * @param taskId the task id of the UceRequest 141 * @param details The SIP information related to this request. 142 */ createSuccessResult(long taskId, @Nullable SipDetails details)143 public static RequestResult createSuccessResult(long taskId, @Nullable SipDetails details) { 144 return new RequestResult(taskId, details); 145 } 146 147 /** 148 * Create a RequestResult for the failed request. 149 * @param taskId the task id of the UceRequest 150 * @param errorCode the error code of the failed request 151 * @param retry When the request can be retried. 152 */ createFailedResult(long taskId, int errorCode, long retry)153 public static RequestResult createFailedResult(long taskId, int errorCode, long retry) { 154 return new RequestResult(taskId, errorCode, retry); 155 } 156 157 /** 158 * Create a RequestResult for the failed request. 159 * @param taskId the task id of the UceRequest 160 * @param errorCode the error code of the failed request 161 * @param retry When the request can be retried. 162 * @param details The SIP information related to this request. 163 */ createFailedResult(long taskId, int errorCode, long retry, @Nullable SipDetails details)164 public static RequestResult createFailedResult(long taskId, int errorCode, long retry, 165 @Nullable SipDetails details) { 166 return new RequestResult(taskId, errorCode, retry, details); 167 } 168 169 private final Long mTaskId; 170 private final Boolean mIsSuccess; 171 private final Optional<Integer> mErrorCode; 172 private final Optional<Long> mRetryMillis; 173 private final Optional<SipDetails> mSipDetails; 174 175 /** 176 * The private constructor for the successful request. 177 */ RequestResult(long taskId)178 private RequestResult(long taskId) { 179 this(taskId, null); 180 } 181 182 /** 183 * The private constructor for the successful request. 184 */ RequestResult(long taskId, SipDetails details)185 private RequestResult(long taskId, SipDetails details) { 186 mTaskId = taskId; 187 mIsSuccess = true; 188 mErrorCode = Optional.empty(); 189 mRetryMillis = Optional.empty(); 190 if (details == null) { 191 mSipDetails = Optional.empty(); 192 } else { 193 mSipDetails = Optional.ofNullable(details); 194 } 195 } 196 197 /** 198 * The private constructor for the failed request. 199 */ RequestResult(long taskId, int errorCode, long retryMillis)200 private RequestResult(long taskId, int errorCode, long retryMillis) { 201 this(taskId, errorCode, retryMillis, null); 202 } 203 204 /** 205 * The private constructor for the failed request. 206 */ RequestResult(long taskId, int errorCode, long retryMillis, SipDetails details)207 private RequestResult(long taskId, int errorCode, long retryMillis, SipDetails details) { 208 mTaskId = taskId; 209 mIsSuccess = false; 210 mErrorCode = Optional.of(errorCode); 211 mRetryMillis = Optional.of(retryMillis); 212 if (details == null) { 213 mSipDetails = Optional.empty(); 214 } else { 215 mSipDetails = Optional.ofNullable(details); 216 } 217 } 218 getTaskId()219 public long getTaskId() { 220 return mTaskId; 221 } 222 isRequestSuccess()223 public boolean isRequestSuccess() { 224 return mIsSuccess; 225 } 226 getErrorCode()227 public Optional<Integer> getErrorCode() { 228 return mErrorCode; 229 } 230 getRetryMillis()231 public Optional<Long> getRetryMillis() { 232 return mRetryMillis; 233 } 234 getSipDetails()235 public Optional<SipDetails> getSipDetails() { 236 return mSipDetails; 237 } 238 } 239 240 // The default capability error code. 241 protected static final int DEFAULT_ERROR_CODE = RcsUceAdapter.ERROR_GENERIC_FAILURE; 242 243 protected final int mSubId; 244 protected final long mCoordinatorId; 245 protected volatile boolean mIsFinished; 246 247 // The collection of activated requests. 248 protected final Map<Long, UceRequest> mActivatedRequests; 249 // The collection of the finished requests. 250 protected final Map<Long, RequestResult> mFinishedRequests; 251 // The lock of the activated and finished collection. 252 protected final Object mCollectionLock = new Object(); 253 254 // The callback to communicate with UceRequestManager 255 protected final RequestManagerCallback mRequestManagerCallback; 256 UceRequestCoordinator(int subId, Collection<UceRequest> requests, RequestManagerCallback requestMgrCallback)257 public UceRequestCoordinator(int subId, Collection<UceRequest> requests, 258 RequestManagerCallback requestMgrCallback) { 259 mSubId = subId; 260 mCoordinatorId = UceUtils.generateRequestCoordinatorId(); 261 mRequestManagerCallback = requestMgrCallback; 262 263 // Set the coordinatorId to all the given UceRequests 264 requests.forEach(request -> request.setRequestCoordinatorId(mCoordinatorId)); 265 266 // All the given requests are put in the activated request at the beginning. 267 mFinishedRequests = new HashMap<>(); 268 mActivatedRequests = requests.stream().collect( 269 Collectors.toMap(UceRequest::getTaskId, request -> request)); 270 } 271 272 /** 273 * @return Get the request coordinator ID. 274 */ getCoordinatorId()275 public long getCoordinatorId() { 276 return mCoordinatorId; 277 } 278 279 /** 280 * @return Get the collection of task ID of all the activated requests. 281 */ getActivatedRequestTaskIds()282 public @NonNull List<Long> getActivatedRequestTaskIds() { 283 synchronized (mCollectionLock) { 284 return mActivatedRequests.values().stream() 285 .map(request -> request.getTaskId()) 286 .collect(Collectors.toList()); 287 } 288 } 289 290 /** 291 * @return Get the UceRequest associated with the given taskId from the activated requests. 292 */ getUceRequest(Long taskId)293 public @Nullable UceRequest getUceRequest(Long taskId) { 294 synchronized (mCollectionLock) { 295 return mActivatedRequests.get(taskId); 296 } 297 } 298 299 /** 300 * Remove the UceRequest associated with the given taskId from the activated collection and 301 * add the {@link RequestResult} into the finished request collection. This method is called by 302 * the coordinator instance when it receives the request updated event and judges this request 303 * is finished. 304 */ moveRequestToFinishedCollection(Long taskId, RequestResult requestResult)305 protected void moveRequestToFinishedCollection(Long taskId, RequestResult requestResult) { 306 synchronized (mCollectionLock) { 307 mActivatedRequests.remove(taskId); 308 mFinishedRequests.put(taskId, requestResult); 309 mRequestManagerCallback.notifyUceRequestFinished(getCoordinatorId(), taskId); 310 } 311 } 312 313 /** 314 * Notify this coordinator instance is finished. This method sets the finish flag and clear all 315 * the UceRequest collections and it can be used anymore after the method is called. 316 */ onFinish()317 public void onFinish() { 318 mIsFinished = true; 319 synchronized (mCollectionLock) { 320 mActivatedRequests.forEach((taskId, request) -> request.onFinish()); 321 mActivatedRequests.clear(); 322 mFinishedRequests.clear(); 323 } 324 } 325 326 /** 327 * Notify the UceRequest associated with the given taskId in the coordinator is updated. 328 */ onRequestUpdated(long taskId, @UceRequestUpdate int event)329 public abstract void onRequestUpdated(long taskId, @UceRequestUpdate int event); 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 getLogPrefix()339 private StringBuilder getLogPrefix() { 340 StringBuilder builder = new StringBuilder("["); 341 builder.append(mSubId).append("][coordId=").append(mCoordinatorId).append("] "); 342 return builder; 343 } 344 } 345