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