Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a620435 authored by James.cf Lin's avatar James.cf Lin
Browse files

[RCS UCE] Handle the onNetworkResonse callback with the Reason header.

Bug: 176920715
Test: atest ImsServiceTest RcsUceAdapterTest
Change-Id: Ia0cc04a8e55d10c93c0ab68a20e457682a3bab8f
parent 5f74ec24
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -173,12 +173,6 @@ public class PublishProcessor {
            return false;
        }

        // Check if the publishing request has reached the maximum number of retries.
        if (mProcessorState.isReachMaximumRetries()) {
            logd("isPublishAllowed: It has reached maximum number of retries");
            return false;
        }

        // Skip this request and re-send the request with the delay time if the publish request
        // executes too frequently.
        if (!mProcessorState.isCurrentTimeAllowed()) {
@@ -252,7 +246,7 @@ public class PublishProcessor {
        mLocalLog.log("Receive command error code=" + requestResponse.getCmdErrorCode());
        logd("onCommandError: " + requestResponse.toString());

        if (requestResponse.needRetry()) {
        if (!mProcessorState.isReachMaximumRetries() && requestResponse.needRetry()) {
            // Increase the retry count
            mProcessorState.increaseRetryCount();

@@ -293,7 +287,7 @@ public class PublishProcessor {
        mLocalLog.log("Receive network response code=" + requestResponse.getNetworkRespSipCode());
        logd("onNetworkResponse: " + requestResponse.toString());

        if (requestResponse.needRetry()) {
        if (!mProcessorState.isReachMaximumRetries() && requestResponse.needRetry()) {
            // Increase the retry count
            mProcessorState.increaseRetryCount();

+94 −16
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControl
import com.android.ims.rcs.uce.util.NetworkSipCode;

import java.time.Instant;
import java.util.Optional;

/**
 * Receiving the result callback of the publish request.
@@ -35,9 +36,11 @@ public class PublishRequestResponse {
    private volatile boolean mNeedRetry;
    private volatile PublishControllerCallback mPublishCtrlCallback;

    private int mCmdErrorCode;
    private int mNetworkRespSipCode;
    private String mNetworkRespReason;
    private Optional<Integer> mCmdErrorCode;
    private Optional<Integer> mNetworkRespSipCode;
    private Optional<String> mReasonPhrase;
    private Optional<Integer> mReasonHeaderCause;
    private Optional<String> mReasonHeaderText;

    // The timestamp when receive the response from the network.
    private Instant mResponseTimestamp;
@@ -45,6 +48,11 @@ public class PublishRequestResponse {
    public PublishRequestResponse(PublishControllerCallback publishCtrlCallback, long taskId) {
        mTaskId = taskId;
        mPublishCtrlCallback = publishCtrlCallback;
        mCmdErrorCode = Optional.empty();
        mNetworkRespSipCode = Optional.empty();
        mReasonPhrase = Optional.empty();
        mReasonHeaderCause = Optional.empty();
        mReasonHeaderText = Optional.empty();
    }

    // The result callback of the publish capability request.
@@ -58,6 +66,13 @@ public class PublishRequestResponse {
        public void onNetworkResponse(int code, String reason) {
            PublishRequestResponse.this.onNetworkResponse(code, reason);
        }

        @Override
        public void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause,
                String reasonHeaderText) {
            PublishRequestResponse.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause,
                    reasonHeaderText);
        }
    };

    public IPublishResponseCallback getResponseCallback() {
@@ -68,14 +83,44 @@ public class PublishRequestResponse {
        return mTaskId;
    }

    public int getCmdErrorCode() {
    /**
     * Retrieve the command error code which received from the network.
     */
    public Optional<Integer> getCmdErrorCode() {
        return mCmdErrorCode;
    }

    public int getNetworkRespSipCode() {
    /**
     * Retrieve the network response sip code which received from the network.
     */
    public Optional<Integer> getNetworkRespSipCode() {
        return mNetworkRespSipCode;
    }

    /**
     * Retrieve the reason phrase of the network response which received from the network.
     */
    public Optional<String> getReasonPhrase() {
        return mReasonPhrase;
    }

    /**
     * Retrieve the reason header from the network response.
     */
    public Optional<Integer> getReasonHeaderCause() {
        return mReasonHeaderCause;
    }

    /**
     * Retrieve the description of the reason header.
     */
    public Optional<String> getReasonHeaderText() {
        return mReasonHeaderText;
    }

    /**
     * Get the timestamp of receiving the network response callback.
     */
    public @Nullable Instant getResponseTimestamp() {
        return mResponseTimestamp;
    }
@@ -86,7 +131,7 @@ public class PublishRequestResponse {

    private void onCommandError(int errorCode) {
        mResponseTimestamp = Instant.now();
        mCmdErrorCode = errorCode;
        mCmdErrorCode = Optional.of(errorCode);
        updateRetryFlagByCommandError();

        PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
@@ -97,8 +142,23 @@ public class PublishRequestResponse {

    private void onNetworkResponse(int sipCode, String reason) {
        mResponseTimestamp = Instant.now();
        mNetworkRespSipCode = sipCode;
        mNetworkRespReason = reason;
        mNetworkRespSipCode = Optional.of(sipCode);
        mReasonPhrase = Optional.ofNullable(reason);
        updateRetryFlagByNetworkResponse();

        PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
        if (ctrlCallback != null) {
            ctrlCallback.onRequestNetworkResp(this);
        }
    }

    private void onNetworkResponse(int sipCode, String reasonPhrase, int reasonHeaderCause,
            String reasonHeaderText) {
        mResponseTimestamp = Instant.now();
        mNetworkRespSipCode = Optional.of(sipCode);
        mReasonPhrase = Optional.ofNullable(reasonPhrase);
        mReasonHeaderCause = Optional.of(reasonHeaderCause);
        mReasonHeaderText = Optional.ofNullable(reasonHeaderText);
        updateRetryFlagByNetworkResponse();

        PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
@@ -108,7 +168,7 @@ public class PublishRequestResponse {
    }

    private void updateRetryFlagByCommandError() {
        switch(mCmdErrorCode) {
        switch(getCmdErrorCode().orElse(-1)) {
            case RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT:
            case RcsCapabilityExchangeImplBase.COMMAND_CODE_INSUFFICIENT_MEMORY:
            case RcsCapabilityExchangeImplBase.COMMAND_CODE_LOST_NETWORK_CONNECTION:
@@ -119,7 +179,9 @@ public class PublishRequestResponse {
    }

    private void updateRetryFlagByNetworkResponse() {
        switch (mNetworkRespSipCode) {
        int networkRespSipCode = getReasonHeaderCause().orElseGet(
                () -> getNetworkRespSipCode().orElse(-1));
        switch (networkRespSipCode) {
            case NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT:
            case NetworkSipCode.SIP_CODE_INTERVAL_TOO_BRIEF:
            case NetworkSipCode.SIP_CODE_TEMPORARILY_UNAVAILABLE:
@@ -138,7 +200,13 @@ public class PublishRequestResponse {
     * Check whether the publishing request is successful.
     */
    public boolean isRequestSuccess() {
        return (mNetworkRespSipCode == NetworkSipCode.SIP_CODE_OK) ? true : false;
        final int sipCodeOk = NetworkSipCode.SIP_CODE_OK;
        if (getNetworkRespSipCode().filter(c -> c == sipCodeOk).isPresent() &&
                (!getReasonHeaderCause().isPresent()
                        || getReasonHeaderCause().filter(c -> c == sipCodeOk).isPresent())) {
            return true;
        }
        return false;
    }

    /**
@@ -152,7 +220,8 @@ public class PublishRequestResponse {
     * Convert the command error code to the publish state
     */
    public int getPublishStateByCmdErrorCode() {
        if (RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT == mCmdErrorCode) {
        if (getCmdErrorCode().orElse(-1) ==
                RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT) {
            return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
        }
        return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
@@ -162,7 +231,14 @@ public class PublishRequestResponse {
     * Convert the network sip code to the publish state
     */
    public int getPublishStateByNetworkResponse() {
        switch (mNetworkRespSipCode) {
        int respSipCode;
        if (getReasonHeaderCause().isPresent()) {
            respSipCode = getReasonHeaderCause().get();
        } else {
            respSipCode = getNetworkRespSipCode().orElse(-1);
        }

        switch (respSipCode) {
            case NetworkSipCode.SIP_CODE_OK:
                return RcsUceAdapter.PUBLISH_STATE_OK;
            case NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT:
@@ -179,9 +255,11 @@ public class PublishRequestResponse {
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("taskId=").append(mTaskId)
                .append(", CmdErrorCode=").append(mCmdErrorCode)
                .append(", NetworkResponse=").append(mNetworkRespSipCode)
                .append(", NetworkResponseReason=").append(mNetworkRespReason)
                .append(", CmdErrorCode=").append(getCmdErrorCode().orElse(-1))
                .append(", NetworkRespSipCode=").append(getNetworkRespSipCode().orElse(-1))
                .append(", ReasonPhrase=").append(getReasonPhrase().orElse(""))
                .append(", ReasonHeaderCause=").append(getReasonHeaderCause().orElse(-1))
                .append(", ReasonHeaderText=").append(getReasonHeaderText().orElse(""))
                .append(", ResponseTimestamp=").append(mResponseTimestamp)
                .append(", isRequestSuccess=").append(isRequestSuccess())
                .append(", needRetry=").append(mNeedRetry);
+111 −36
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
@@ -43,24 +44,30 @@ public class CapabilityRequestResponse {
    private static final String LOG_TAG = UceUtils.getLogPrefix() + "CapRequestResponse";

    // The command error code of the request. It is assigned by the callback "onCommandError"
    private @CommandCode Integer mCommandError;
    private @CommandCode Optional<Integer> mCommandError;

    // The SIP code of the network response. It is assigned by the callback "onNetworkResponse"
    private int mNetworkResponseCode;
    private Optional<Integer> mNetworkRespSipCode;

    // The reason of the network response. It is assigned by the callback "onNetworkResponse"
    private String mNetworkResponseReason;
    private Optional<String> mReasonPhrase;

    // The response sip code from the reason header
    private Optional<Integer> mReasonHeaderCause;

    // The phrase from the reason header
    private Optional<String> mReasonHeaderText;

    // The reason why the this request was terminated. This value is assigned by the callback
    // "onTerminated"
    private String mTerminatedReason;
    private Optional<String> mTerminatedReason;

    // How long after this request can be retried. This value is assigned by the callback
    // "onTerminated"
    private long mRetryAfterMillis = 0L;
    private Optional<Long> mRetryAfterMillis;

    // The error code of this request.
    private @RcsUceAdapter.ErrorCode Integer mErrorCode;
    private @RcsUceAdapter.ErrorCode Optional<Integer> mErrorCode;

    // The list of the terminated resource. This is assigned by the callback "onResourceTerminated"
    private final List<RcsContactUceCapability> mTerminatedResource;
@@ -73,6 +80,13 @@ public class CapabilityRequestResponse {
    public IRcsUceControllerCallback mCapabilitiesCallback;

    public CapabilityRequestResponse() {
        mCommandError = Optional.empty();
        mNetworkRespSipCode = Optional.empty();
        mReasonPhrase = Optional.empty();
        mReasonHeaderCause = Optional.empty();
        mReasonHeaderText = Optional.empty();
        mTerminatedReason = Optional.empty();
        mRetryAfterMillis = Optional.of(0L);
        mTerminatedResource = new ArrayList<>();
        mUpdatedCapabilityList = new ArrayList<>();
    }
@@ -88,13 +102,13 @@ public class CapabilityRequestResponse {
     * Set the error code when the request is failed.
     */
    public void setErrorCode(@RcsUceAdapter.ErrorCode int errorCode) {
        mErrorCode = errorCode;
        mErrorCode = Optional.of(errorCode);
    }

    /**
     * Get the error code of this request.
     */
    public int getErrorCode() {
    public Optional<Integer> getErrorCode() {
        return mErrorCode;
    }

@@ -102,37 +116,85 @@ public class CapabilityRequestResponse {
     * Set the command error code.
     */
    public void setCommandError(@CommandCode int commandError) {
        mCommandError = commandError;
        mCommandError = Optional.of(commandError);
    }

    /**
     * Set the network response of this request which is sent by the network.
     */
    public void setNetworkResponseCode(int sipCode, String reason) {
        mNetworkResponseCode = sipCode;
        mNetworkResponseReason = reason;
        mNetworkRespSipCode = Optional.of(sipCode);
        mReasonPhrase = Optional.ofNullable(reason);
    }

    /**
     * Set the network response of this request which is sent by the network.
     */
    public void setNetworkResponseCode(int sipCode, String reasonPhrase,
            int reasonHeaderCause, String reasonHeaderText) {
        mNetworkRespSipCode = Optional.of(sipCode);
        mReasonPhrase = Optional.ofNullable(reasonPhrase);
        mReasonHeaderCause = Optional.of(reasonHeaderCause);
        mReasonHeaderText = Optional.ofNullable(reasonHeaderText);
    }

    /**
     * Get the sip code of the network response.
     */
    public int getNetworkResponseCode() {
        return mNetworkResponseCode;
    public Optional<Integer> getNetworkRespSipCode() {
        return mNetworkRespSipCode;
    }

    /**
     * Get the reason of the network response.
     */
    public String getNetworkResponseReason() {
        return mNetworkResponseReason;
    public Optional<String> getReasonPhrase() {
        return mReasonPhrase;
    }

    /**
     * Get the response sip code from the reason header.
     */
    public Optional<Integer> getReasonHeaderCause() {
        return mReasonHeaderCause;
    }

    /**
     * Get the response phrae from the reason header.
     */
    public Optional<String> getReasonHeaderText() {
        return mReasonHeaderText;
    }

    /**
     * Check if the network response is success.
     * @return true if the network response code is OK.
     * Check if the network response is OK.
     * @return true if the network response code is OK and the Reason header cause
     * is either not present or OK.
     */
    public boolean isNetworkResponseOK() {
        return (mNetworkResponseCode == NetworkSipCode.SIP_CODE_OK) ? true : false;
        final int sipCodeOk = NetworkSipCode.SIP_CODE_OK;
        if (getNetworkRespSipCode().filter(c -> c == sipCodeOk).isPresent() &&
                (!getReasonHeaderCause().isPresent()
                        || getReasonHeaderCause().filter(c -> c == sipCodeOk).isPresent())) {
            return true;
        }
        return false;
    }

    /**
     * Check if the request is forbidden.
     * @return true if the Reason header sip code is 403(Forbidden) or the response sip code is 403.
     */
    public boolean isRequestForbidden() {
        // Check the Reason header sip code if the Reason header is present, otherwise check the
        // response sip code.
        if (getReasonHeaderCause().isPresent()) {
            return getReasonHeaderCause()
                    .filter(c -> c == NetworkSipCode.SIP_CODE_FORBIDDEN).isPresent();
        } else {
            return getNetworkRespSipCode()
            .filter(c -> c == NetworkSipCode.SIP_CODE_FORBIDDEN).isPresent();
        }
    }

    /**
@@ -141,15 +203,15 @@ public class CapabilityRequestResponse {
     * @param retryAfterMillis How long to wait before retry this request.
     */
    public void setRequestTerminated(String reason, long retryAfterMillis) {
        mTerminatedReason = reason;
        mRetryAfterMillis = retryAfterMillis;
        mTerminatedReason = Optional.ofNullable(reason);
        mRetryAfterMillis = Optional.of(retryAfterMillis);
    }

    /**
     * Retrieve the retryAfterMillis
     * @return Return the retryAfterMillis, 0L if the value is not present.
     */
    public long getRetryAfterMillis() {
        return mRetryAfterMillis;
        return mRetryAfterMillis.orElse(0L);
    }

    /**
@@ -170,8 +232,10 @@ public class CapabilityRequestResponse {
        }
    }

    // Remove the given capabilities from the UpdatedCapabilityList when these capabilities have
    // updated to the requester.
    /**
     * Remove the given capabilities from the UpdatedCapabilityList when these capabilities have
     * updated to the requester.
     */
    private void removeCapabilities(List<RcsContactUceCapability> capabilityList) {
        synchronized (mUpdatedCapabilityList) {
            mUpdatedCapabilityList.removeAll(capabilityList);
@@ -215,9 +279,8 @@ public class CapabilityRequestResponse {
     */
    public boolean triggerCachedCapabilitiesCallback(
            List<RcsContactUceCapability> cachedCapabilities) {

        if (cachedCapabilities == null || cachedCapabilities.isEmpty()) {
        // Return if there's no cached capabilities.
        if (cachedCapabilities == null || cachedCapabilities.isEmpty()) {
            return true;
        }

@@ -284,7 +347,7 @@ public class CapabilityRequestResponse {
     */
    public void triggerErrorCallback() {
        try {
            mCapabilitiesCallback.onError(mErrorCode, mRetryAfterMillis);
            mCapabilitiesCallback.onError(mErrorCode.get(), mRetryAfterMillis.orElse(0L));
        } catch (RemoteException e) {
            Log.w(LOG_TAG, "triggerErrorCallback exception: " + e);
        }
@@ -331,15 +394,25 @@ public class CapabilityRequestResponse {
    /**
     * Convert the SIP error code which sent by ImsService to the capability error code.
     */
    public static int convertSipErrorToCapabilityError(int sipError, String sipReason) {
    public int getCapabilityErrorFromSipError() {
        int sipError;
        String respReason;
        // Check the sip code in the Reason header first if the Reason Header is present.
        if (mReasonHeaderCause.isPresent()) {
            sipError = mReasonHeaderCause.get();
            respReason = mReasonHeaderText.orElse("");
        } else {
            sipError = mNetworkRespSipCode.orElse(-1);
            respReason = mReasonPhrase.orElse("");
        }
        int uceError;
        switch (sipError) {
            case NetworkSipCode.SIP_CODE_FORBIDDEN:   // 403
                if (NetworkSipCode.SIP_NOT_REGISTERED.equalsIgnoreCase(sipReason)) {
                if (NetworkSipCode.SIP_NOT_REGISTERED.equalsIgnoreCase(respReason)) {
                    // Not registered with IMS. Device shall register to IMS.
                    uceError = RcsUceAdapter.ERROR_NOT_REGISTERED;
                } else if (NetworkSipCode.SIP_NOT_AUTHORIZED_FOR_PRESENCE.equalsIgnoreCase(
                        sipReason)) {
                        respReason)) {
                    // Not provisioned for EAB. Device shall not retry.
                    uceError = RcsUceAdapter.ERROR_NOT_AUTHORIZED;
                } else {
@@ -369,12 +442,14 @@ public class CapabilityRequestResponse {
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        return builder.append("ErrorCode=").append(mErrorCode)
                .append(", CommandErrorCode=").append(mCommandError)
                .append(", NetworkResponseCode=").append(mNetworkResponseCode)
                .append(", NetworkResponseReason=").append(mNetworkResponseReason)
                .append(", RetryAfter=").append(mRetryAfterMillis)
                .append(", TerminatedReason=").append(mTerminatedReason)
        return builder.append("ErrorCode=").append(mErrorCode.orElse(-1))
                .append(", CommandErrorCode=").append(mCommandError.orElse(-1))
                .append(", NetworkResponseCode=").append(mNetworkRespSipCode.orElse(-1))
                .append(", NetworkResponseReason=").append(mReasonPhrase.orElse(""))
                .append(", ReasonHeaderCause=").append(mReasonHeaderCause.orElse(-1))
                .append(", ReasonHeaderText=").append(mReasonHeaderText.orElse(""))
                .append(", TerminatedReason=").append(mTerminatedReason.orElse(""))
                .append(", RetryAfterMillis=").append(mRetryAfterMillis.orElse(0L))
                .toString();
    }
}
+29 −6
Original line number Diff line number Diff line
@@ -52,6 +52,12 @@ public class SubscribeRequest extends UceRequest {
                    SubscribeRequest.this.onNetworkResponse(code, reason);
                }
                @Override
                public void onNetworkRespHeader(int code, String reasonPhrase,
                        int reasonHeaderCause, String reasonHeaderText) {
                    SubscribeRequest.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause,
                            reasonHeaderText);
                }
                @Override
                public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) {
                    SubscribeRequest.this.onCapabilitiesUpdate(pidfXmls);
                }
@@ -141,8 +147,28 @@ public class SubscribeRequest extends UceRequest {
        // Set the capability error code and notify RequestManager if the SIP code is not success.
        // Otherwise, waiting for the onNotifyCapabilitiesUpdate callback.
        if (!mRequestResponse.isNetworkResponseOK()) {
            int capErrorCode = CapabilityRequestResponse.convertSipErrorToCapabilityError(
                    sipCode, reason);
            int capErrorCode = mRequestResponse.getCapabilityErrorFromSipError();
            mRequestResponse.setErrorCode(capErrorCode);
            mRequestManagerCallback.onRequestFailed(mTaskId);
        }
    }

    // Handle the network response callback which is triggered by ISubscribeResponseCallback.
    private void onNetworkResponse(int sipCode, String reasonPhrase,
        int reasonHeaderCause, String reasonHeaderText) {
        logd("onNetworkResponse: code=" + sipCode + ", reasonPhrase=" + reasonPhrase +
                ", reasonHeaderCause=" + reasonHeaderCause +
                ", reasonHeaderText=" + reasonHeaderText);
        if (mIsFinished) {
            return;
        }
        mRequestResponse.setNetworkResponseCode(sipCode, reasonPhrase, reasonHeaderCause,
                reasonHeaderText);

        // Set the capability error code and notify RequestManager if the SIP code is not success.
        // Otherwise, waiting for the onNotifyCapabilitiesUpdate callback.
        if (!mRequestResponse.isNetworkResponseOK()) {
            int capErrorCode = mRequestResponse.getCapabilityErrorFromSipError();
            mRequestResponse.setErrorCode(capErrorCode);
            mRequestManagerCallback.onRequestFailed(mTaskId);
        }
@@ -213,10 +239,7 @@ public class SubscribeRequest extends UceRequest {
        } else {
            // This request is failed. Store the retryAfter info and notify UceRequestManager.
            mRequestResponse.setRequestTerminated(reason, retryAfterMillis);
            int networkCode = mRequestResponse.getNetworkResponseCode();
            String networkReason = mRequestResponse.getNetworkResponseReason();
            int capErrorCode = CapabilityRequestResponse.convertSipErrorToCapabilityError(
                    networkCode, networkReason);
            int capErrorCode = mRequestResponse.getCapabilityErrorFromSipError();
            mRequestResponse.setErrorCode(capErrorCode);
            mRequestManagerCallback.onRequestFailed(mTaskId);
        }
+2 −2
Original line number Diff line number Diff line
@@ -294,8 +294,8 @@ public abstract class UceRequest {
    }

    private void checkRequestForbidden() {
        if (mRequestResponse.getNetworkResponseCode() == NetworkSipCode.SIP_CODE_FORBIDDEN) {
            int errorCode = mRequestResponse.getErrorCode();
        if (mRequestResponse.isRequestForbidden()) {
            int errorCode = mRequestResponse.getErrorCode().orElse(RcsUceAdapter.ERROR_FORBIDDEN);
            long retryAfter = mRequestResponse.getRetryAfterMillis();
            mRequestManagerCallback.onRequestForbidden(true, errorCode, retryAfter);
        }
Loading