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