Commit c5e6b555 authored by wutao's avatar wutao Committed by Commit Bot

ambient: Add max retries to refresh token

Current implementation will not return callback until succeed. This
patch adds a limit to the max number of retries. This patch also changes
the backoff logic to use backoff entry.

Bug: b/169250512
Test: added new tests.
Change-Id: Id421a152dc7fe6ce3f365086d16c6e611b4fc001
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2427082
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810840}
parent f7f0b873
...@@ -14,14 +14,22 @@ namespace ash { ...@@ -14,14 +14,22 @@ namespace ash {
namespace { namespace {
constexpr base::TimeDelta kMinTokenRefreshDelay = constexpr int kMaxRetries = 3;
base::TimeDelta::FromMilliseconds(1000);
constexpr base::TimeDelta kMaxTokenRefreshDelay = constexpr net::BackoffEntry::Policy kRetryBackoffPolicy = {
base::TimeDelta::FromMilliseconds(60 * 1000); 0, // Number of initial errors to ignore.
1000, // Initial delay in ms.
2.0, // Factor by which the waiting time will be multiplied.
0.2, // Fuzzing percentage.
60 * 1000, // Maximum delay in ms.
-1, // Never discard the entry.
true, // Use initial delay.
};
} // namespace } // namespace
AmbientAccessTokenController::AmbientAccessTokenController() = default; AmbientAccessTokenController::AmbientAccessTokenController()
: refresh_token_retry_backoff_(&kRetryBackoffPolicy) {}
AmbientAccessTokenController::~AmbientAccessTokenController() = default; AmbientAccessTokenController::~AmbientAccessTokenController() = default;
...@@ -75,11 +83,17 @@ void AmbientAccessTokenController::AccessTokenRefreshed( ...@@ -75,11 +83,17 @@ void AmbientAccessTokenController::AccessTokenRefreshed(
has_pending_request_ = false; has_pending_request_ = false;
if (gaia_id.empty() || access_token.empty()) { if (gaia_id.empty() || access_token.empty()) {
RetryRefreshAccessToken(); refresh_token_retry_backoff_.InformOfRequest(/*succeeded=*/false);
if (refresh_token_retry_backoff_.failure_count() <= kMaxRetries)
RetryRefreshAccessToken();
else
NotifyAccessTokenRefreshed();
return; return;
} }
VLOG(1) << "Access token fetched."; VLOG(1) << "Access token fetched.";
refresh_token_retry_backoff_.Reset();
gaia_id_ = gaia_id; gaia_id_ = gaia_id;
access_token_ = access_token; access_token_ = access_token;
expiration_time_ = expiration_time; expiration_time_ = expiration_time;
...@@ -88,14 +102,7 @@ void AmbientAccessTokenController::AccessTokenRefreshed( ...@@ -88,14 +102,7 @@ void AmbientAccessTokenController::AccessTokenRefreshed(
void AmbientAccessTokenController::RetryRefreshAccessToken() { void AmbientAccessTokenController::RetryRefreshAccessToken() {
base::TimeDelta backoff_delay = base::TimeDelta backoff_delay =
std::min(kMinTokenRefreshDelay * refresh_token_retry_backoff_.GetTimeUntilRelease();
(1 << (token_refresh_error_backoff_factor - 1)),
kMaxTokenRefreshDelay) +
base::RandDouble() * kMinTokenRefreshDelay;
if (backoff_delay < kMaxTokenRefreshDelay)
++token_refresh_error_backoff_factor;
token_refresh_timer_.Start( token_refresh_timer_.Start(
FROM_HERE, backoff_delay, FROM_HERE, backoff_delay,
base::BindOnce(&AmbientAccessTokenController::RefreshAccessToken, base::BindOnce(&AmbientAccessTokenController::RefreshAccessToken,
...@@ -118,4 +125,8 @@ void AmbientAccessTokenController::SetTokenUsageBufferForTesting( ...@@ -118,4 +125,8 @@ void AmbientAccessTokenController::SetTokenUsageBufferForTesting(
token_usage_time_buffer_ = time; token_usage_time_buffer_ = time;
} }
base::TimeDelta AmbientAccessTokenController::GetTimeUntilReleaseForTesting() {
return refresh_token_retry_backoff_.GetTimeUntilRelease();
}
} // namespace ash } // namespace ash
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "net/base/backoff_entry.h"
namespace ash { namespace ash {
...@@ -54,6 +55,8 @@ class ASH_EXPORT AmbientAccessTokenController { ...@@ -54,6 +55,8 @@ class ASH_EXPORT AmbientAccessTokenController {
void SetTokenUsageBufferForTesting(base::TimeDelta time); void SetTokenUsageBufferForTesting(base::TimeDelta time);
base::TimeDelta GetTimeUntilReleaseForTesting();
std::string gaia_id_; std::string gaia_id_;
std::string access_token_; std::string access_token_;
...@@ -67,7 +70,8 @@ class ASH_EXPORT AmbientAccessTokenController { ...@@ -67,7 +70,8 @@ class ASH_EXPORT AmbientAccessTokenController {
base::TimeDelta token_usage_time_buffer_ = kTokenUsageTimeBuffer; base::TimeDelta token_usage_time_buffer_ = kTokenUsageTimeBuffer;
base::OneShotTimer token_refresh_timer_; base::OneShotTimer token_refresh_timer_;
int token_refresh_error_backoff_factor = 1;
net::BackoffEntry refresh_token_retry_backoff_;
std::vector<AccessTokenCallback> callbacks_; std::vector<AccessTokenCallback> callbacks_;
......
...@@ -215,7 +215,7 @@ TEST_F(AmbientControllerTest, ShouldReturnEmptyAccessToken) { ...@@ -215,7 +215,7 @@ TEST_F(AmbientControllerTest, ShouldReturnEmptyAccessToken) {
CloseAmbientScreen(); CloseAmbientScreen();
} }
TEST_F(AmbientControllerTest, ShouldRefreshAccessTokenAfterFailure) { TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenAfterFailure) {
EXPECT_FALSE(IsAccessTokenRequestPending()); EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token. // Lock the screen will request a token.
...@@ -225,16 +225,72 @@ TEST_F(AmbientControllerTest, ShouldRefreshAccessTokenAfterFailure) { ...@@ -225,16 +225,72 @@ TEST_F(AmbientControllerTest, ShouldRefreshAccessTokenAfterFailure) {
EXPECT_FALSE(IsAccessTokenRequestPending()); EXPECT_FALSE(IsAccessTokenRequestPending());
// Token request automatically retry. // Token request automatically retry.
// The failure delay has jitter so fast forward a bit more. task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
constexpr base::TimeDelta kMaxTokenRefreshDelay =
base::TimeDelta::FromSeconds(60);
task_environment()->FastForwardBy(kMaxTokenRefreshDelay * 2);
EXPECT_TRUE(IsAccessTokenRequestPending()); EXPECT_TRUE(IsAccessTokenRequestPending());
// Clean up. // Clean up.
CloseAmbientScreen(); CloseAmbientScreen();
} }
TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenWithBackoffPolicy) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
base::TimeDelta delay1 = GetRefreshTokenDelay();
task_environment()->FastForwardBy(delay1 * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
base::TimeDelta delay2 = GetRefreshTokenDelay();
EXPECT_GT(delay2, delay1);
task_environment()->FastForwardBy(delay2 * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
// Clean up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenOnlyThreeTimes) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 1st retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 2nd retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 3rd retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Will not retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_FALSE(IsAccessTokenRequestPending());
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, TEST_F(AmbientControllerTest,
CheckAcquireAndReleaseWakeLockWhenBatteryIsCharging) { CheckAcquireAndReleaseWakeLockWhenBatteryIsCharging) {
// Simulate a device being connected to a charger initially. // Simulate a device being connected to a charger initially.
......
...@@ -264,6 +264,10 @@ bool AmbientAshTestBase::IsAccessTokenRequestPending() const { ...@@ -264,6 +264,10 @@ bool AmbientAshTestBase::IsAccessTokenRequestPending() const {
return ambient_client_->IsAccessTokenRequestPending(); return ambient_client_->IsAccessTokenRequestPending();
} }
base::TimeDelta AmbientAshTestBase::GetRefreshTokenDelay() {
return token_controller()->GetTimeUntilReleaseForTesting();
}
AmbientController* AmbientAshTestBase::ambient_controller() { AmbientController* AmbientAshTestBase::ambient_controller() {
return Shell::Get()->ambient_controller(); return Shell::Get()->ambient_controller();
} }
......
...@@ -96,6 +96,8 @@ class AmbientAshTestBase : public AshTestBase { ...@@ -96,6 +96,8 @@ class AmbientAshTestBase : public AshTestBase {
bool IsAccessTokenRequestPending() const; bool IsAccessTokenRequestPending() const;
base::TimeDelta GetRefreshTokenDelay();
AmbientBackgroundImageView* GetAmbientBackgroundImageView(); AmbientBackgroundImageView* GetAmbientBackgroundImageView();
// Returns the media string view for displaying ongoing media info. // Returns the media string view for displaying ongoing media info.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment