aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames.cf Lin <jamescflin@google.com>2021-05-17 23:04:17 +0800
committerJames.cf Lin <jamescflin@google.com>2021-05-19 07:34:46 +0800
commit2ecae17d5054c4a790146f278267a5290f17f690 (patch)
tree20bef81eefc1b48fc5e63b7360371094970feb7b
parent0a9ad5e0dfd4ebf144f180ad1598019d7d6ff1a3 (diff)
downloadims-2ecae17d5054c4a790146f278267a5290f17f690.tar.gz
Check the the reason of the subscribe request to deter whether client should retry or not.
Bug: 181041161 Test: atest RcsUceAdapterTest Merged-In: I2576cd4601a61944b3c8f4f62096622c530d855a Change-Id: I2576cd4601a61944b3c8f4f62096622c530d855a
-rw-r--r--src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java8
-rw-r--r--src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java17
-rw-r--r--src/java/com/android/ims/rcs/uce/request/SubscriptionTerminatedHelper.java165
-rw-r--r--src/java/com/android/ims/rcs/uce/util/UceUtils.java8
4 files changed, 194 insertions, 4 deletions
diff --git a/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java b/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java
index 56b2a28c..00b11278 100644
--- a/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java
+++ b/src/java/com/android/ims/rcs/uce/request/CapabilityRequestResponse.java
@@ -182,6 +182,14 @@ public class CapabilityRequestResponse {
}
/**
+ * @return The reason of terminating the subscription request. empty string if it has not
+ * been given.
+ */
+ public synchronized String getTerminatedReason() {
+ return mTerminatedReason.orElse("");
+ }
+
+ /**
* @return Return the retryAfterMillis, 0L if the value is not present.
*/
public synchronized long getRetryAfterMillis() {
diff --git a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java
index 960e9974..f242d668 100644
--- a/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java
+++ b/src/java/com/android/ims/rcs/uce/request/SubscribeRequestCoordinator.java
@@ -26,6 +26,7 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback;
import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
+import com.android.ims.rcs.uce.request.SubscriptionTerminatedHelper.TerminatedResult;
import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
import com.android.internal.annotations.VisibleForTesting;
@@ -115,10 +116,18 @@ public class SubscribeRequestCoordinator extends UceRequestCoordinator {
// The RequestResult creator of the request terminated.
private static final RequestResultCreator sTerminatedCreator = (taskId, response,
requestMgrCallback) -> {
- long retryAfterMillis = response.getRetryAfterMillis();
- int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response);
- // If the network response is failed or the retryAfter is not zero, this request is failed.
- if (!response.isNetworkResponseOK() || retryAfterMillis > 0L) {
+ // Check the given terminated reason to determine whether clients should retry or not.
+ TerminatedResult terminatedResult = SubscriptionTerminatedHelper.getAnalysisResult(
+ response.getTerminatedReason(), response.getRetryAfterMillis());
+ if (terminatedResult.getErrorCode().isPresent()) {
+ // If the terminated error code is present, it means that the request is failed.
+ int errorCode = terminatedResult.getErrorCode().get();
+ long terminatedRetry = terminatedResult.getRetryAfterMillis();
+ return RequestResult.createFailedResult(taskId, errorCode, terminatedRetry);
+ } else if (!response.isNetworkResponseOK() || response.getRetryAfterMillis() > 0L) {
+ // If the network response is failed or the retryAfter is not 0, this request is failed.
+ long retryAfterMillis = response.getRetryAfterMillis();
+ int errorCode = CapabilityRequestResponse.getCapabilityErrorFromSipCode(response);
return RequestResult.createFailedResult(taskId, errorCode, retryAfterMillis);
} else {
return RequestResult.createSuccessResult(taskId);
diff --git a/src/java/com/android/ims/rcs/uce/request/SubscriptionTerminatedHelper.java b/src/java/com/android/ims/rcs/uce/request/SubscriptionTerminatedHelper.java
new file mode 100644
index 00000000..8ae25271
--- /dev/null
+++ b/src/java/com/android/ims/rcs/uce/request/SubscriptionTerminatedHelper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2021 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.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.RcsUceAdapter.ErrorCode;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.ims.rcs.uce.util.UceUtils;
+
+import java.util.Optional;
+
+/**
+ * The helper class to analyze the result of the callback onTerminated to determine whether the
+ * subscription request should be retried or not.
+ */
+public class SubscriptionTerminatedHelper {
+
+ private static final String LOG_TAG = UceUtils.getLogPrefix() + "SubscriptionTerminated";
+
+ // The terminated reasons defined in RFC 3265 3.2.4
+ private static final String REASON_DEACTIVATED = "deactivated";
+ private static final String REASON_PROBATION = "probation";
+ private static final String REASON_REJECTED = "rejected";
+ private static final String REASON_TIMEOUT = "timeout";
+ private static final String REASON_GIVEUP = "giveup";
+ private static final String REASON_NORESOURCE = "noresource";
+
+ /**
+ * The analysis result of the callback onTerminated.
+ */
+ static class TerminatedResult {
+ private final @ErrorCode Optional<Integer> mErrorCode;
+ private final long mRetryAfterMillis;
+
+ public TerminatedResult(@ErrorCode Optional<Integer> errorCode, long retryAfterMillis) {
+ mErrorCode = errorCode;
+ mRetryAfterMillis = retryAfterMillis;
+ }
+
+ /**
+ * @return the error code when the request is failed. Optional.empty if the request is
+ * successful.
+ */
+ public Optional<Integer> getErrorCode() {
+ return mErrorCode;
+ }
+
+ public long getRetryAfterMillis() {
+ return mRetryAfterMillis;
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TerminatedResult: ")
+ .append("errorCode=").append(mErrorCode)
+ .append(", retryAfterMillis=").append(mRetryAfterMillis);
+ return builder.toString();
+ }
+ }
+
+ /**
+ * According to the RFC 3265, Check the given reason to see whether clients should retry the
+ * subscribe request.
+ * <p>
+ * See RFC 3265 3.2.4 for the detail.
+ *
+ * @param reason The reason why the subscribe request is terminated. The reason is given by the
+ * network and it could be empty.
+ * @param retryAfterMillis How long should clients wait before retrying.
+ */
+ public static TerminatedResult getAnalysisResult(String reason, long retryAfterMillis) {
+ TerminatedResult result = null;
+ if (TextUtils.isEmpty(reason)) {
+ /*
+ * When the value of retryAfterMillis is larger then zero, the client should retry.
+ */
+ if (retryAfterMillis > 0L) {
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_GENERIC_FAILURE),
+ retryAfterMillis);
+ }
+ } else if (REASON_DEACTIVATED.equalsIgnoreCase(reason)) {
+ /*
+ * When the reason is "deactivated", clients should retry immediately.
+ */
+ long retry = getRequestRetryAfterMillis(retryAfterMillis);
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_GENERIC_FAILURE), retry);
+ } else if (REASON_PROBATION.equalsIgnoreCase(reason)) {
+ /*
+ * When the reason is "probation", it means that the subscription has been terminated,
+ * but the client should retry at some later time.
+ */
+ long retry = getRequestRetryAfterMillis(retryAfterMillis);
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_GENERIC_FAILURE), retry);
+ } else if (REASON_REJECTED.equalsIgnoreCase(reason)) {
+ /*
+ * When the reason is "rejected", it means that the subscription has been terminated
+ * due to chang in authorization policy. Clients should NOT retry.
+ */
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_NOT_AUTHORIZED), 0L);
+ } else if (REASON_TIMEOUT.equalsIgnoreCase(reason) && retryAfterMillis > 0L) {
+ /*
+ * The subscription has been terminated because it was not refreshed before it expired.
+ * The request completes successfully when the retryAfter is not set. Otherwise, the
+ * request should retry if the retryAfter is set.
+ */
+ long retry = getRequestRetryAfterMillis(retryAfterMillis);
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_REQUEST_TIMEOUT), retry);
+ } else if (REASON_GIVEUP.equalsIgnoreCase(reason)) {
+ /*
+ * The subscription has been terminated because the notifier could no obtain
+ * authorization in a timely fashion. Clients could retry the subscribe request.
+ */
+ long retry = getRequestRetryAfterMillis(retryAfterMillis);
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_NOT_AUTHORIZED), retry);
+ } else if (REASON_NORESOURCE.equalsIgnoreCase(reason)) {
+ /*
+ * The subscription has been terminated because the resource is no longer exists.
+ * Clients should NOT retry.
+ */
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_NOT_FOUND), 0L);
+ } else if (retryAfterMillis > 0L) {
+ /*
+ * Even if the reason is not listed above, clients should retry the request as long as
+ * the value of retry is non-zero.
+ */
+ long retry = getRequestRetryAfterMillis(retryAfterMillis);
+ result = new TerminatedResult(Optional.of(RcsUceAdapter.ERROR_GENERIC_FAILURE), retry);
+ }
+
+ // The request should be successful. when the terminated is not in the above cases
+ if (result == null) {
+ result = new TerminatedResult(Optional.empty(), 0L);
+ }
+
+ Log.d(LOG_TAG, "getAnalysisResult: reason=" + reason + ", retry=" + retryAfterMillis +
+ ", " + result);
+ return result;
+ }
+
+ /*
+ * Get the appropriated retryAfterMillis for the subscribe request.
+ */
+ private static long getRequestRetryAfterMillis(long retryAfterMillis) {
+ // Return the minimum retry after millis if the given retryAfterMillis is less than the
+ // minimum value.
+ long minRetryAfterMillis = UceUtils.getMinimumRequestRetryAfterMillis();
+ return (retryAfterMillis < minRetryAfterMillis) ? minRetryAfterMillis : retryAfterMillis;
+ }
+}
diff --git a/src/java/com/android/ims/rcs/uce/util/UceUtils.java b/src/java/com/android/ims/rcs/uce/util/UceUtils.java
index b499d23d..93bdbd52 100644
--- a/src/java/com/android/ims/rcs/uce/util/UceUtils.java
+++ b/src/java/com/android/ims/rcs/uce/util/UceUtils.java
@@ -46,6 +46,7 @@ public class UceUtils {
private static final long DEFAULT_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC =
TimeUnit.DAYS.toSeconds(30);
private static final long DEFAULT_REQUEST_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(20);
+ private static final long DEFAULT_MINIMUM_REQUEST_RETRY_AFTER_MS = TimeUnit.SECONDS.toMillis(3);
// The task ID of the UCE request
private static long TASK_ID = 0L;
@@ -341,4 +342,11 @@ public class UceUtils {
builder.append(",").append(exitStateTimeMillis); // exit state time
return builder.toString();
}
+
+ /**
+ * Get the minimum value of the capabilities request retry after.
+ */
+ public static long getMinimumRequestRetryAfterMillis() {
+ return DEFAULT_MINIMUM_REQUEST_RETRY_AFTER_MS;
+ }
}