1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h"
11 #include "base/bind.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/rand_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "components/proximity_auth/logging/logging.h"
17 namespace proximity_auth
{
21 // Returns a human readable string given a |time_delta|.
22 std::string
TimeDeltaToString(const base::TimeDelta
& time_delta
) {
23 if (time_delta
.InDays() > 0)
24 return base::StringPrintf("%d days", time_delta
.InDays());
26 if (time_delta
.InHours() > 0)
27 return base::StringPrintf("%d hours", time_delta
.InHours());
29 if (time_delta
.InMinutes() > 0)
30 return base::StringPrintf("%d minutes", time_delta
.InMinutes());
32 return base::StringPrintf("%d seconds",
33 base::saturated_cast
<int>(time_delta
.InSeconds()));
38 SyncSchedulerImpl::SyncSchedulerImpl(Delegate
* delegate
,
39 base::TimeDelta refresh_period
,
40 base::TimeDelta base_recovery_period
,
41 double max_jitter_ratio
,
42 const std::string
& scheduler_name
)
43 : delegate_(delegate
),
44 refresh_period_(refresh_period
),
45 base_recovery_period_(base_recovery_period
),
46 max_jitter_ratio_(max_jitter_ratio
),
47 scheduler_name_(scheduler_name
),
48 strategy_(Strategy::PERIODIC_REFRESH
),
49 sync_state_(SyncState::NOT_STARTED
),
51 weak_ptr_factory_(this) {
54 SyncSchedulerImpl::~SyncSchedulerImpl() {
57 void SyncSchedulerImpl::Start(
58 const base::TimeDelta
& elapsed_time_since_last_sync
,
61 sync_state_
= SyncState::WAITING_FOR_REFRESH
;
62 // We reset the failure backoff when the scheduler is started again, as the
63 // configuration that caused the previous attempts to fail most likely won't
64 // be present after a restart.
65 if (strategy_
== Strategy::AGGRESSIVE_RECOVERY
)
68 // To take into account the time waited when the system is powered off, we
69 // subtract the time elapsed with a normal sync period to the initial time
71 base::TimeDelta sync_delta
=
72 GetJitteredPeriod() - elapsed_time_since_last_sync
;
74 // The elapsed time may be negative if the system clock is changed. In this
75 // case, we immediately schedule a sync.
76 base::TimeDelta zero_delta
= base::TimeDelta::FromSeconds(0);
77 if (elapsed_time_since_last_sync
< zero_delta
|| sync_delta
< zero_delta
)
78 sync_delta
= zero_delta
;
80 ScheduleNextSync(sync_delta
);
83 void SyncSchedulerImpl::ForceSync() {
87 base::TimeDelta
SyncSchedulerImpl::GetTimeToNextSync() const {
89 return base::TimeDelta::FromSeconds(0);
90 return timer_
->GetCurrentDelay();
93 SyncScheduler::Strategy
SyncSchedulerImpl::GetStrategy() const {
97 SyncScheduler::SyncState
SyncSchedulerImpl::GetSyncState() const {
101 void SyncSchedulerImpl::OnTimerFired() {
103 if (strategy_
== Strategy::PERIODIC_REFRESH
) {
104 PA_LOG(INFO
) << "Timer fired for periodic refresh, making request...";
105 sync_state_
= SyncState::SYNC_IN_PROGRESS
;
106 } else if (strategy_
== Strategy::AGGRESSIVE_RECOVERY
) {
107 PA_LOG(INFO
) << "Timer fired for aggressive recovery, making request...";
108 sync_state_
= SyncState::SYNC_IN_PROGRESS
;
114 delegate_
->OnSyncRequested(
115 make_scoped_ptr(new SyncRequest(weak_ptr_factory_
.GetWeakPtr())));
118 scoped_ptr
<base::Timer
> SyncSchedulerImpl::CreateTimer() {
119 bool retain_user_task
= false;
120 bool is_repeating
= false;
121 return make_scoped_ptr(new base::Timer(retain_user_task
, is_repeating
));
124 void SyncSchedulerImpl::ScheduleNextSync(const base::TimeDelta
& sync_delta
) {
125 if (sync_state_
!= SyncState::WAITING_FOR_REFRESH
) {
126 PA_LOG(ERROR
) << "Unexpected state when scheduling next sync: sync_state="
127 << static_cast<int>(sync_state_
);
131 bool is_aggressive_recovery
= (strategy_
== Strategy::AGGRESSIVE_RECOVERY
);
132 PA_LOG(INFO
) << "Scheduling next sync for " << scheduler_name_
<< ":\n"
133 << " Strategy: " << (is_aggressive_recovery
134 ? "Aggressive Recovery"
135 : "Periodic Refresh") << "\n"
136 << " Time Delta: " << TimeDeltaToString(sync_delta
)
137 << (is_aggressive_recovery
138 ? base::StringPrintf(
139 "\n Previous Failures: %d",
140 base::saturated_cast
<int>(failure_count_
))
143 timer_
= CreateTimer();
144 timer_
->Start(FROM_HERE
, sync_delta
,
145 base::Bind(&SyncSchedulerImpl::OnTimerFired
,
146 weak_ptr_factory_
.GetWeakPtr()));
149 void SyncSchedulerImpl::OnSyncCompleted(bool success
) {
150 if (sync_state_
!= SyncState::SYNC_IN_PROGRESS
) {
151 PA_LOG(ERROR
) << "Unexpected state when sync completed: sync_state="
152 << static_cast<int>(sync_state_
)
153 << ", strategy_=" << static_cast<int>(strategy_
);
156 sync_state_
= SyncState::WAITING_FOR_REFRESH
;
159 strategy_
= Strategy::PERIODIC_REFRESH
;
162 strategy_
= Strategy::AGGRESSIVE_RECOVERY
;
166 ScheduleNextSync(GetJitteredPeriod());
169 base::TimeDelta
SyncSchedulerImpl::GetJitteredPeriod() {
170 double jitter
= 2 * max_jitter_ratio_
* (base::RandDouble() - 0.5);
171 base::TimeDelta period
= GetPeriod();
172 base::TimeDelta jittered_time_delta
= period
+ (period
* jitter
);
173 if (jittered_time_delta
.InMilliseconds() < 0)
174 jittered_time_delta
= base::TimeDelta::FromMilliseconds(0);
175 return jittered_time_delta
;
178 base::TimeDelta
SyncSchedulerImpl::GetPeriod() {
179 if (strategy_
== Strategy::PERIODIC_REFRESH
) {
180 return refresh_period_
;
181 } else if (strategy_
== Strategy::AGGRESSIVE_RECOVERY
&& failure_count_
> 0) {
182 // The backoff for each consecutive failure is exponentially doubled until
183 // it is equal to the normal refresh period.
184 // Note: |backoff_factor| may evaulate to INF if |failure_count_| is large,
185 // but multiplication operations for TimeDelta objects are saturated.
186 double backoff_factor
= pow(2, failure_count_
- 1);
187 base::TimeDelta backoff_period
= base_recovery_period_
* backoff_factor
;
188 return backoff_period
< refresh_period_
? backoff_period
: refresh_period_
;
190 PA_LOG(ERROR
) << "Error getting period for strategy: "
191 << static_cast<int>(strategy_
);
192 return base::TimeDelta();
196 } // namespace proximity_auth