diff options
author | James.cf Lin <jamescflin@google.com> | 2021-05-17 23:04:17 +0800 |
---|---|---|
committer | James.cf Lin <jamescflin@google.com> | 2021-05-19 07:34:46 +0800 |
commit | 2ecae17d5054c4a790146f278267a5290f17f690 (patch) | |
tree | 20bef81eefc1b48fc5e63b7360371094970feb7b | |
parent | 0a9ad5e0dfd4ebf144f180ad1598019d7d6ff1a3 (diff) | |
download | ims-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
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; + } } |