Chromium Code Reviews| Index: blimp/client/android/java/src/org/chromium/blimp/auth/RetryingTokenSource.java |
| diff --git a/blimp/client/android/java/src/org/chromium/blimp/auth/RetryingTokenSource.java b/blimp/client/android/java/src/org/chromium/blimp/auth/RetryingTokenSource.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..cae7e4b7e910646dc997be2229b985be4b38b480 |
| --- /dev/null |
| +++ b/blimp/client/android/java/src/org/chromium/blimp/auth/RetryingTokenSource.java |
| @@ -0,0 +1,126 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.blimp.auth; |
| + |
| +import android.content.Intent; |
| +import android.os.Handler; |
| +import android.os.Message; |
| + |
| +import org.chromium.base.VisibleForTesting; |
| + |
| +/** |
| + * Wraps an existing {@link TokenSource} and adds exponential fallback retry support to it. The |
| + * underlying {@link TokenSource} will be queried and all calls will be proxied except transient |
| + * failures, which will cause a retry after an exponentially increasing delay. |
| + */ |
| +public class RetryingTokenSource extends Handler implements TokenSource, TokenSource.Callback { |
| + private static final int MSG_QUERY_TOKEN = 1; |
| + private static final long MIN_BACKOFF_DELAY_MS = 500; |
| + |
| + /** The maximum number of times to attempt connection before failing. */ |
| + @VisibleForTesting |
| + public static final int MAX_NUMBER_OF_RETRIES = 8; |
| + |
| + private final TokenSource mTokenSource; |
| + |
| + private TokenSource.Callback mCallback; |
| + private long mAttemptNumber; |
| + |
| + /** |
| + * Creates a {@link RetryingTokenSource} that proxies most {@link TokenSource} communication to |
| + * {@code tokenSource}. |
| + * @param tokenSource A {@link TokenSource} that does the actual underlying token management. |
| + */ |
| + public RetryingTokenSource(TokenSource tokenSource) { |
| + mTokenSource = tokenSource; |
| + mTokenSource.setCallback(this); |
| + } |
| + |
| + // TokenSource implementation. |
| + @Override |
| + public void destroy() { |
| + mTokenSource.destroy(); |
| + removeMessages(MSG_QUERY_TOKEN); |
| + } |
| + |
| + @Override |
| + public void setCallback(TokenSource.Callback callback) { |
| + mCallback = callback; |
| + } |
| + |
| + @Override |
| + public void getToken() { |
| + // Reset all exponential backoff states. |
|
nyquist
2015/09/29 23:38:40
Do we need any thread safety for the public method
David Trainor- moved to gerrit
2015/10/05 15:19:07
Done.
|
| + removeMessages(MSG_QUERY_TOKEN); |
| + mAttemptNumber = 0; |
| + |
| + // Start the TokenSource#getToken() exponential backoff calls. |
| + getTokenWithBackoff(); |
| + } |
| + |
| + @Override |
| + public boolean isRetrievingToken() { |
| + return mTokenSource.isRetrievingToken() || hasMessages(MSG_QUERY_TOKEN); |
| + } |
| + |
| + @Override |
| + public int tokenIsInvalid(String token) { |
| + return mTokenSource.tokenIsInvalid(token); |
| + } |
| + |
| + @Override |
| + public void onAccountSelected(Intent data) { |
| + mTokenSource.onAccountSelected(data); |
| + } |
| + |
| + // TokenSource.Callback implementation. |
| + @Override |
| + public void onTokenReceived(String token) { |
| + mCallback.onTokenReceived(token); |
| + } |
| + |
| + @Override |
| + public void onTokenUnavailable(boolean isTransient) { |
| + if (isTransient && mAttemptNumber < MAX_NUMBER_OF_RETRIES) { |
| + getTokenWithBackoff(); |
| + } else { |
| + mCallback.onTokenUnavailable(false); |
| + } |
| + } |
| + |
| + @Override |
| + public void onNeedsAccountToBeSelected(Intent intent) { |
| + mCallback.onNeedsAccountToBeSelected(intent); |
| + } |
| + |
| + // Handler overrides. |
| + @Override |
| + public void handleMessage(Message msg) { |
| + if (msg.what != MSG_QUERY_TOKEN) return; |
| + mTokenSource.getToken(); |
| + } |
| + |
| + /** |
| + * @param delay The suggested time (in ms) to wait before attempting to query for the token |
| + * again. |
| + * @return The actual time (in ms) to wait before attempting to query for a token again. |
| + */ |
| + @VisibleForTesting |
| + protected long finalizeRetryDelay(long delay) { |
| + return delay; |
| + } |
| + |
| + private void getTokenWithBackoff() { |
|
nyquist
2015/09/29 23:38:40
Could we make this a bit randomized? See https://c
David Trainor- moved to gerrit
2015/10/05 15:19:07
Done.
|
| + long delayMs = 0; |
| + |
| + // For the first attempt, don't delay. |
| + if (mAttemptNumber > 0) { |
| + delayMs = (long) Math.pow(2, (mAttemptNumber - 1)) * MIN_BACKOFF_DELAY_MS; |
| + } |
| + |
| + sendEmptyMessageDelayed(MSG_QUERY_TOKEN, finalizeRetryDelay(delayMs)); |
| + mAttemptNumber++; |
| + } |
| +} |