aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/ims/rcs/uce/request/UceRequestManager.java')
-rw-r--r--src/java/com/android/ims/rcs/uce/request/UceRequestManager.java829
1 files changed, 829 insertions, 0 deletions
diff --git a/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java
new file mode 100644
index 00000000..3e12ba30
--- /dev/null
+++ b/src/java/com/android/ims/rcs/uce/request/UceRequestManager.java
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.rcs.uce.request;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsContactUceCapability.CapabilityMechanism;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.aidl.IOptionsRequestCallback;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.ims.rcs.uce.UceController;
+import com.android.ims.rcs.uce.UceController.UceControllerCallback;
+import com.android.ims.rcs.uce.UceDeviceState;
+import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
+import com.android.ims.rcs.uce.eab.EabCapabilityResult;
+import com.android.ims.rcs.uce.options.OptionsController;
+import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
+import com.android.ims.rcs.uce.request.UceRequest.UceRequestType;
+import com.android.ims.rcs.uce.request.UceRequestCoordinator.UceRequestUpdate;
+import com.android.ims.rcs.uce.util.UceUtils;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Managers the capabilities requests and the availability requests from UceController.
+ */
+public class UceRequestManager {
+
+ private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceRequestManager";
+
+ /**
+ * Testing interface used to mock UceUtils in testing.
+ */
+ @VisibleForTesting
+ public interface UceUtilsProxy {
+ /**
+ * The interface for {@link UceUtils#isPresenceCapExchangeEnabled(Context, int)} used for
+ * testing.
+ */
+ boolean isPresenceCapExchangeEnabled(Context context, int subId);
+
+ /**
+ * The interface for {@link UceUtils#isPresenceSupported(Context, int)} used for testing.
+ */
+ boolean isPresenceSupported(Context context, int subId);
+
+ /**
+ * The interface for {@link UceUtils#isSipOptionsSupported(Context, int)} used for testing.
+ */
+ boolean isSipOptionsSupported(Context context, int subId);
+
+ /**
+ * @return true when the Presence group subscribe is enabled.
+ */
+ boolean isPresenceGroupSubscribeEnabled(Context context, int subId);
+
+ /**
+ * Retrieve the maximum number of contacts that can be included in a request.
+ */
+ int getRclMaxNumberEntries(int subId);
+
+ /**
+ * @return true if the given phone number is blocked by the network.
+ */
+ boolean isNumberBlocked(Context context, String phoneNumber);
+ }
+
+ private static UceUtilsProxy sUceUtilsProxy = new UceUtilsProxy() {
+ @Override
+ public boolean isPresenceCapExchangeEnabled(Context context, int subId) {
+ return UceUtils.isPresenceCapExchangeEnabled(context, subId);
+ }
+
+ @Override
+ public boolean isPresenceSupported(Context context, int subId) {
+ return UceUtils.isPresenceSupported(context, subId);
+ }
+
+ @Override
+ public boolean isSipOptionsSupported(Context context, int subId) {
+ return UceUtils.isSipOptionsSupported(context, subId);
+ }
+
+ @Override
+ public boolean isPresenceGroupSubscribeEnabled(Context context, int subId) {
+ return UceUtils.isPresenceGroupSubscribeEnabled(context, subId);
+ }
+
+ @Override
+ public int getRclMaxNumberEntries(int subId) {
+ return UceUtils.getRclMaxNumberEntries(subId);
+ }
+
+ @Override
+ public boolean isNumberBlocked(Context context, String phoneNumber) {
+ return UceUtils.isNumberBlocked(context, phoneNumber);
+ }
+ };
+
+ @VisibleForTesting
+ public void setsUceUtilsProxy(UceUtilsProxy uceUtilsProxy) {
+ sUceUtilsProxy = uceUtilsProxy;
+ }
+
+ /**
+ * The callback interface to receive the request and the result from the UceRequest.
+ */
+ public interface RequestManagerCallback {
+ /**
+ * Notify sending the UceRequest
+ */
+ void notifySendingRequest(long coordinator, long taskId, long delayTimeMs);
+
+ /**
+ * Retrieve the contact capabilities from the cache.
+ */
+ List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList);
+
+ /**
+ * Retrieve the contact availability from the cache.
+ */
+ EabCapabilityResult getAvailabilityFromCache(Uri uri);
+
+ /**
+ * Store the given contact capabilities to the cache.
+ */
+ void saveCapabilities(List<RcsContactUceCapability> contactCapabilities);
+
+ /**
+ * Retrieve the device's capabilities.
+ */
+ RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int capMechanism);
+
+ /**
+ * Get the device state to check whether the device is disallowed by the network or not.
+ */
+ DeviceStateResult getDeviceState();
+
+ /**
+ * Refresh the device state. It is called when receive the UCE request response.
+ */
+ void refreshDeviceState(int sipCode, String reason);
+
+ /**
+ * Notify that the UceRequest associated with the given taskId encounters error.
+ */
+ void notifyRequestError(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the UceRequest received the onCommandError callback from the ImsService.
+ */
+ void notifyCommandError(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the UceRequest received the onNetworkResponse callback from the ImsService.
+ */
+ void notifyNetworkResponse(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the UceRequest received the onTerminated callback from the ImsService.
+ */
+ void notifyTerminated(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that some contacts are not RCS anymore. It will updated the cached capabilities
+ * and trigger the callback IRcsUceControllerCallback#onCapabilitiesReceived
+ */
+ void notifyResourceTerminated(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the capabilities updates. It will update the cached and trigger the callback
+ * IRcsUceControllerCallback#onCapabilitiesReceived
+ */
+ void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that some of the request capabilities can be retrieved from the cached.
+ */
+ void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that all the requested capabilities can be retrieved from the cache. It does not
+ * need to request capabilities from the network.
+ */
+ void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the remote options request is done. This is sent by RemoteOptionsRequest and
+ * it will notify the RemoteOptionsCoordinator to handle it.
+ */
+ void notifyRemoteRequestDone(long requestCoordinatorId, long taskId);
+
+ /**
+ * Set the timer for the request timeout. It will cancel the request when the time is up.
+ */
+ void setRequestTimeoutTimer(long requestCoordinatorId, long taskId, long timeoutAfterMs);
+
+ /**
+ * Remove the timeout timer of the capabilities request.
+ */
+ void removeRequestTimeoutTimer(long taskId);
+
+ /**
+ * Notify that the UceRequest has finished. This is sent by UceRequestCoordinator.
+ */
+ void notifyUceRequestFinished(long requestCoordinatorId, long taskId);
+
+ /**
+ * Notify that the RequestCoordinator has finished. This is sent by UceRequestCoordinator
+ * to remove the coordinator from the UceRequestRepository.
+ */
+ void notifyRequestCoordinatorFinished(long requestCoordinatorId);
+ }
+
+ private RequestManagerCallback mRequestMgrCallback = new RequestManagerCallback() {
+ @Override
+ public void notifySendingRequest(long coordinatorId, long taskId, long delayTimeMs) {
+ mHandler.sendRequestMessage(coordinatorId, taskId, delayTimeMs);
+ }
+
+ @Override
+ public List<EabCapabilityResult> getCapabilitiesFromCache(List<Uri> uriList) {
+ return mControllerCallback.getCapabilitiesFromCache(uriList);
+ }
+
+ @Override
+ public EabCapabilityResult getAvailabilityFromCache(Uri uri) {
+ return mControllerCallback.getAvailabilityFromCache(uri);
+ }
+
+ @Override
+ public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) {
+ mControllerCallback.saveCapabilities(contactCapabilities);
+ }
+
+ @Override
+ public RcsContactUceCapability getDeviceCapabilities(@CapabilityMechanism int mechanism) {
+ return mControllerCallback.getDeviceCapabilities(mechanism);
+ }
+
+ @Override
+ public DeviceStateResult getDeviceState() {
+ return mControllerCallback.getDeviceState();
+ }
+
+ @Override
+ public void refreshDeviceState(int sipCode, String reason) {
+ mControllerCallback.refreshDeviceState(sipCode, reason,
+ UceController.REQUEST_TYPE_CAPABILITY);
+ }
+
+ @Override
+ public void notifyRequestError(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_ERROR);
+ }
+
+ @Override
+ public void notifyCommandError(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_COMMAND_ERROR);
+ }
+
+ @Override
+ public void notifyNetworkResponse(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_NETWORK_RESPONSE);
+ }
+ @Override
+ public void notifyTerminated(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_TERMINATED);
+ }
+ @Override
+ public void notifyResourceTerminated(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_RESOURCE_TERMINATED);
+ }
+ @Override
+ public void notifyCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_CAPABILITY_UPDATE);
+ }
+
+ @Override
+ public void notifyCachedCapabilitiesUpdated(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE);
+ }
+
+ @Override
+ public void notifyNoNeedRequestFromNetwork(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_NO_NEED_REQUEST_FROM_NETWORK);
+ }
+
+ @Override
+ public void notifyRemoteRequestDone(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestUpdatedMessage(requestCoordinatorId, taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_REMOTE_REQUEST_DONE);
+ }
+
+ @Override
+ public void setRequestTimeoutTimer(long coordinatorId, long taskId, long timeoutAfterMs) {
+ mHandler.sendRequestTimeoutTimerMessage(coordinatorId, taskId, timeoutAfterMs);
+ }
+
+ @Override
+ public void removeRequestTimeoutTimer(long taskId) {
+ mHandler.removeRequestTimeoutTimer(taskId);
+ }
+
+ @Override
+ public void notifyUceRequestFinished(long requestCoordinatorId, long taskId) {
+ mHandler.sendRequestFinishedMessage(requestCoordinatorId, taskId);
+ }
+
+ @Override
+ public void notifyRequestCoordinatorFinished(long requestCoordinatorId) {
+ mHandler.sendRequestCoordinatorFinishedMessage(requestCoordinatorId);
+ }
+ };
+
+ private final int mSubId;
+ private final Context mContext;
+ private final UceRequestHandler mHandler;
+ private final UceRequestRepository mRequestRepository;
+ private volatile boolean mIsDestroyed;
+
+ private OptionsController mOptionsCtrl;
+ private SubscribeController mSubscribeCtrl;
+ private UceControllerCallback mControllerCallback;
+
+ public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c) {
+ mSubId = subId;
+ mContext = context;
+ mControllerCallback = c;
+ mHandler = new UceRequestHandler(this, looper);
+ mRequestRepository = new UceRequestRepository(subId, mRequestMgrCallback);
+ logi("create");
+ }
+
+ @VisibleForTesting
+ public UceRequestManager(Context context, int subId, Looper looper, UceControllerCallback c,
+ UceRequestRepository requestRepository) {
+ mSubId = subId;
+ mContext = context;
+ mControllerCallback = c;
+ mHandler = new UceRequestHandler(this, looper);
+ mRequestRepository = requestRepository;
+ }
+
+ /**
+ * Set the OptionsController for requestiong capabilities by OPTIONS mechanism.
+ */
+ public void setOptionsController(OptionsController controller) {
+ mOptionsCtrl = controller;
+ }
+
+ /**
+ * Set the SubscribeController for requesting capabilities by Subscribe mechanism.
+ */
+ public void setSubscribeController(SubscribeController controller) {
+ mSubscribeCtrl = controller;
+ }
+
+ /**
+ * Notify that the request manager instance is destroyed.
+ */
+ public void onDestroy() {
+ logi("onDestroy");
+ mIsDestroyed = true;
+ mHandler.onDestroy();
+ mRequestRepository.onDestroy();
+ }
+
+ /**
+ * Send a new capability request. It is called by UceController.
+ */
+ public void sendCapabilityRequest(List<Uri> uriList, boolean skipFromCache,
+ IRcsUceControllerCallback callback) throws RemoteException {
+ if (mIsDestroyed) {
+ callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
+ return;
+ }
+ sendRequestInternal(UceRequest.REQUEST_TYPE_CAPABILITY, uriList, skipFromCache, callback);
+ }
+
+ /**
+ * Send a new availability request. It is called by UceController.
+ */
+ public void sendAvailabilityRequest(Uri uri, IRcsUceControllerCallback callback)
+ throws RemoteException {
+ if (mIsDestroyed) {
+ callback.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
+ return;
+ }
+ sendRequestInternal(UceRequest.REQUEST_TYPE_AVAILABILITY,
+ Collections.singletonList(uri), false /* skipFromCache */, callback);
+ }
+
+ private void sendRequestInternal(@UceRequestType int type, List<Uri> uriList,
+ boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException {
+ UceRequestCoordinator requestCoordinator = null;
+ if (sUceUtilsProxy.isPresenceCapExchangeEnabled(mContext, mSubId) &&
+ sUceUtilsProxy.isPresenceSupported(mContext, mSubId)) {
+ requestCoordinator = createSubscribeRequestCoordinator(type, uriList, skipFromCache,
+ callback);
+ } else if (sUceUtilsProxy.isSipOptionsSupported(mContext, mSubId)) {
+ requestCoordinator = createOptionsRequestCoordinator(type, uriList, callback);
+ }
+
+ if (requestCoordinator == null) {
+ logw("sendRequestInternal: Neither Presence nor OPTIONS are supported");
+ callback.onError(RcsUceAdapter.ERROR_NOT_ENABLED, 0L);
+ return;
+ }
+
+ StringBuilder builder = new StringBuilder("sendRequestInternal: ");
+ builder.append("requestType=").append(type)
+ .append(", requestCoordinatorId=").append(requestCoordinator.getCoordinatorId())
+ .append(", taskId={")
+ .append(requestCoordinator.getActivatedRequestTaskIds().stream()
+ .map(Object::toString).collect(Collectors.joining(","))).append("}");
+ logd(builder.toString());
+
+ // Add this RequestCoordinator to the UceRequestRepository.
+ addRequestCoordinator(requestCoordinator);
+ }
+
+ private UceRequestCoordinator createSubscribeRequestCoordinator(final @UceRequestType int type,
+ final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) {
+ SubscribeRequestCoordinator.Builder builder;
+
+ if (!sUceUtilsProxy.isPresenceGroupSubscribeEnabled(mContext, mSubId)) {
+ // When the group subscribe is disabled, each contact is required to be encapsulated
+ // into individual UceRequest.
+ List<UceRequest> requestList = new ArrayList<>();
+ uriList.forEach(uri -> {
+ List<Uri> individualUri = Collections.singletonList(uri);
+ UceRequest request = createSubscribeRequest(type, individualUri, skipFromCache);
+ requestList.add(request);
+ });
+ builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
+ mRequestMgrCallback);
+ builder.setCapabilitiesCallback(callback);
+ } else {
+ // Even when the group subscribe is supported by the network, the number of contacts in
+ // a UceRequest still cannot exceed the maximum.
+ List<UceRequest> requestList = new ArrayList<>();
+ final int rclMaxNumber = sUceUtilsProxy.getRclMaxNumberEntries(mSubId);
+ int numRequestCoordinators = uriList.size() / rclMaxNumber;
+ for (int count = 0; count < numRequestCoordinators; count++) {
+ List<Uri> subUriList = new ArrayList<>();
+ for (int index = 0; index < rclMaxNumber; index++) {
+ subUriList.add(uriList.get(count * rclMaxNumber + index));
+ }
+ requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
+ }
+
+ List<Uri> subUriList = new ArrayList<>();
+ for (int i = numRequestCoordinators * rclMaxNumber; i < uriList.size(); i++) {
+ subUriList.add(uriList.get(i));
+ }
+ requestList.add(createSubscribeRequest(type, subUriList, skipFromCache));
+
+ builder = new SubscribeRequestCoordinator.Builder(mSubId, requestList,
+ mRequestMgrCallback);
+ builder.setCapabilitiesCallback(callback);
+ }
+ return builder.build();
+ }
+
+ private UceRequestCoordinator createOptionsRequestCoordinator(@UceRequestType int type,
+ List<Uri> uriList, IRcsUceControllerCallback callback) {
+ OptionsRequestCoordinator.Builder builder;
+ List<UceRequest> requestList = new ArrayList<>();
+ uriList.forEach(uri -> {
+ List<Uri> individualUri = Collections.singletonList(uri);
+ UceRequest request = createOptionsRequest(type, individualUri, false);
+ requestList.add(request);
+ });
+ builder = new OptionsRequestCoordinator.Builder(mSubId, requestList, mRequestMgrCallback);
+ builder.setCapabilitiesCallback(callback);
+ return builder.build();
+ }
+
+ private CapabilityRequest createSubscribeRequest(int type, List<Uri> uriList,
+ boolean skipFromCache) {
+ CapabilityRequest request = new SubscribeRequest(mSubId, type, mRequestMgrCallback,
+ mSubscribeCtrl);
+ request.setContactUri(uriList);
+ request.setSkipGettingFromCache(skipFromCache);
+ return request;
+ }
+
+ private CapabilityRequest createOptionsRequest(int type, List<Uri> uriList,
+ boolean skipFromCache) {
+ CapabilityRequest request = new OptionsRequest(mSubId, type, mRequestMgrCallback,
+ mOptionsCtrl);
+ request.setContactUri(uriList);
+ request.setSkipGettingFromCache(skipFromCache);
+ return request;
+ }
+
+ /**
+ * Retrieve the device's capabilities. This request is from the ImsService to send the
+ * capabilities to the remote side.
+ */
+ public void retrieveCapabilitiesForRemote(Uri contactUri, List<String> remoteCapabilities,
+ IOptionsRequestCallback requestCallback) {
+ RemoteOptionsRequest request = new RemoteOptionsRequest(mSubId, mRequestMgrCallback);
+ request.setContactUri(Collections.singletonList(contactUri));
+ request.setRemoteFeatureTags(remoteCapabilities);
+
+ // If the remote number is blocked, do not send capabilities back.
+ String number = getNumberFromUri(contactUri);
+ if (!TextUtils.isEmpty(number)) {
+ request.setIsRemoteNumberBlocked(sUceUtilsProxy.isNumberBlocked(mContext, number));
+ }
+
+ // Create the RemoteOptionsCoordinator instance
+ RemoteOptionsCoordinator.Builder CoordBuilder = new RemoteOptionsCoordinator.Builder(
+ mSubId, Collections.singletonList(request), mRequestMgrCallback);
+ CoordBuilder.setOptionsRequestCallback(requestCallback);
+ RemoteOptionsCoordinator requestCoordinator = CoordBuilder.build();
+
+ StringBuilder builder = new StringBuilder("retrieveCapabilitiesForRemote: ");
+ builder.append("requestCoordinatorId ").append(requestCoordinator.getCoordinatorId())
+ .append(", taskId={")
+ .append(requestCoordinator.getActivatedRequestTaskIds().stream()
+ .map(Object::toString).collect(Collectors.joining(","))).append("}");
+ logd(builder.toString());
+
+ // Add this RequestCoordinator to the UceRequestRepository.
+ addRequestCoordinator(requestCoordinator);
+ }
+
+ private static class UceRequestHandler extends Handler {
+ private static final int EVENT_EXECUTE_REQUEST = 1;
+ private static final int EVENT_REQUEST_UPDATED = 2;
+ private static final int EVENT_REQUEST_TIMEOUT = 3;
+ private static final int EVENT_REQUEST_FINISHED = 4;
+ private static final int EVENT_COORDINATOR_FINISHED = 5;
+
+ private final Map<Long, SomeArgs> mRequestTimeoutTimers;
+ private final WeakReference<UceRequestManager> mUceRequestMgrRef;
+
+ public UceRequestHandler(UceRequestManager requestManager, Looper looper) {
+ super(looper);
+ mRequestTimeoutTimers = new HashMap<>();
+ mUceRequestMgrRef = new WeakReference<>(requestManager);
+ }
+
+ /**
+ * Send the capabilities request message.
+ */
+ public void sendRequestMessage(Long coordinatorId, Long taskId, long delayTimeMs) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = coordinatorId;
+ args.arg2 = taskId;
+
+ Message message = obtainMessage();
+ message.what = EVENT_EXECUTE_REQUEST;
+ message.obj = args;
+ sendMessageDelayed(message, delayTimeMs);
+ }
+
+ /**
+ * Send the Uce request updated message.
+ */
+ public void sendRequestUpdatedMessage(Long coordinatorId, Long taskId,
+ @UceRequestUpdate int requestEvent) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = coordinatorId;
+ args.arg2 = taskId;
+ args.argi1 = requestEvent;
+
+ Message message = obtainMessage();
+ message.what = EVENT_REQUEST_UPDATED;
+ message.obj = args;
+ sendMessage(message);
+ }
+
+ /**
+ * Set the timeout timer to cancel the capabilities request.
+ */
+ public void sendRequestTimeoutTimerMessage(Long coordId, Long taskId, Long timeoutAfterMs) {
+ synchronized (mRequestTimeoutTimers) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = coordId;
+ args.arg2 = taskId;
+
+ // Add the message object to the collection. It can be used to find this message
+ // when the request is completed and remove the timeout timer.
+ mRequestTimeoutTimers.put(taskId, args);
+
+ Message message = obtainMessage();
+ message.what = EVENT_REQUEST_TIMEOUT;
+ message.obj = args;
+ sendMessageDelayed(message, timeoutAfterMs);
+ }
+ }
+
+ /**
+ * Remove the timeout timer because the capabilities request is finished.
+ */
+ public void removeRequestTimeoutTimer(Long taskId) {
+ synchronized (mRequestTimeoutTimers) {
+ SomeArgs args = mRequestTimeoutTimers.remove(taskId);
+ if (args == null) {
+ return;
+ }
+ Log.d(LOG_TAG, "removeRequestTimeoutTimer: taskId=" + taskId);
+ removeMessages(EVENT_REQUEST_TIMEOUT, args);
+ args.recycle();
+ }
+ }
+
+ public void sendRequestFinishedMessage(Long coordinatorId, Long taskId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = coordinatorId;
+ args.arg2 = taskId;
+
+ Message message = obtainMessage();
+ message.what = EVENT_REQUEST_FINISHED;
+ message.obj = args;
+ sendMessage(message);
+ }
+
+ /**
+ * Finish the UceRequestCoordinator associated with the given id.
+ */
+ public void sendRequestCoordinatorFinishedMessage(Long coordinatorId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = coordinatorId;
+
+ Message message = obtainMessage();
+ message.what = EVENT_COORDINATOR_FINISHED;
+ message.obj = args;
+ sendMessage(message);
+ }
+
+ /**
+ * Remove all the messages from the handler
+ */
+ public void onDestroy() {
+ removeCallbacksAndMessages(null);
+ // Recycle all the arguments in the mRequestTimeoutTimers
+ synchronized (mRequestTimeoutTimers) {
+ mRequestTimeoutTimers.forEach((taskId, args) -> {
+ try {
+ args.recycle();
+ } catch (Exception e) {}
+ });
+ mRequestTimeoutTimers.clear();
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ UceRequestManager requestManager = mUceRequestMgrRef.get();
+ if (requestManager == null) {
+ return;
+ }
+ SomeArgs args = (SomeArgs) msg.obj;
+ final Long coordinatorId = (Long) args.arg1;
+ final Long taskId = (Long) Optional.ofNullable(args.arg2).orElse(-1L);
+ final Integer requestEvent = Optional.of(args.argi1).orElse(-1);
+ args.recycle();
+
+ requestManager.logd("handleMessage: " + EVENT_DESCRIPTION.get(msg.what)
+ + ", coordinatorId=" + coordinatorId + ", taskId=" + taskId);
+ switch (msg.what) {
+ case EVENT_EXECUTE_REQUEST: {
+ UceRequest request = requestManager.getUceRequest(taskId);
+ if (request == null) {
+ requestManager.logw("handleMessage: cannot find request, taskId=" + taskId);
+ return;
+ }
+ request.executeRequest();
+ break;
+ }
+ case EVENT_REQUEST_UPDATED: {
+ UceRequestCoordinator requestCoordinator =
+ requestManager.getRequestCoordinator(coordinatorId);
+ if (requestCoordinator == null) {
+ requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
+ return;
+ }
+ requestCoordinator.onRequestUpdated(taskId, requestEvent);
+ break;
+ }
+ case EVENT_REQUEST_TIMEOUT: {
+ UceRequestCoordinator requestCoordinator =
+ requestManager.getRequestCoordinator(coordinatorId);
+ if (requestCoordinator == null) {
+ requestManager.logw("handleMessage: cannot find UceRequestCoordinator");
+ return;
+ }
+ // The timeout timer is triggered, remove this record from the collection.
+ synchronized (mRequestTimeoutTimers) {
+ mRequestTimeoutTimers.remove(taskId);
+ }
+ // Notify that the request is timeout.
+ requestCoordinator.onRequestUpdated(taskId,
+ UceRequestCoordinator.REQUEST_UPDATE_TIMEOUT);
+ break;
+ }
+ case EVENT_REQUEST_FINISHED: {
+ // Notify the repository that the request is finished.
+ requestManager.notifyRepositoryRequestFinished(taskId);
+ break;
+ }
+ case EVENT_COORDINATOR_FINISHED: {
+ UceRequestCoordinator requestCoordinator =
+ requestManager.removeRequestCoordinator(coordinatorId);
+ if (requestCoordinator != null) {
+ requestCoordinator.onFinish();
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+
+ private static Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>();
+ static {
+ EVENT_DESCRIPTION.put(EVENT_EXECUTE_REQUEST, "EXECUTE_REQUEST");
+ EVENT_DESCRIPTION.put(EVENT_REQUEST_UPDATED, "REQUEST_UPDATE");
+ EVENT_DESCRIPTION.put(EVENT_REQUEST_TIMEOUT, "REQUEST_TIMEOUT");
+ EVENT_DESCRIPTION.put(EVENT_REQUEST_FINISHED, "REQUEST_FINISHED");
+ EVENT_DESCRIPTION.put(EVENT_COORDINATOR_FINISHED, "REMOVE_COORDINATOR");
+ }
+ }
+
+ private void addRequestCoordinator(UceRequestCoordinator coordinator) {
+ mRequestRepository.addRequestCoordinator(coordinator);
+ }
+
+ private UceRequestCoordinator removeRequestCoordinator(Long coordinatorId) {
+ return mRequestRepository.removeRequestCoordinator(coordinatorId);
+ }
+
+ private UceRequestCoordinator getRequestCoordinator(Long coordinatorId) {
+ return mRequestRepository.getRequestCoordinator(coordinatorId);
+ }
+
+ private UceRequest getUceRequest(Long taskId) {
+ return mRequestRepository.getUceRequest(taskId);
+ }
+
+ private void notifyRepositoryRequestFinished(Long taskId) {
+ mRequestRepository.notifyRequestFinished(taskId);
+ }
+
+ @VisibleForTesting
+ public UceRequestHandler getUceRequestHandler() {
+ return mHandler;
+ }
+
+ @VisibleForTesting
+ public RequestManagerCallback getRequestManagerCallback() {
+ return mRequestMgrCallback;
+ }
+
+ private void logi(String log) {
+ Log.i(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private void logd(String log) {
+ Log.d(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private void logw(String log) {
+ Log.w(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private StringBuilder getLogPrefix() {
+ StringBuilder builder = new StringBuilder("[");
+ builder.append(mSubId);
+ builder.append("] ");
+ return builder;
+ }
+
+ private String getNumberFromUri(Uri uri) {
+ if (uri == null) return null;
+ String number = uri.getSchemeSpecificPart();
+ String[] numberParts = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ return null;
+ }
+ return numberParts[0];
+ }
+}