1// Copyright (c) 2010 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.
4
5#include "chrome/browser/sync/notifier/registration_manager.h"
6
7#include <algorithm>
8#include <cstddef>
9#include <string>
10
11#include "base/rand_util.h"
12#include "chrome/browser/sync/notifier/invalidation_util.h"
13#include "chrome/browser/sync/syncable/model_type.h"
14
15namespace sync_notifier {
16
17RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
18
19RegistrationManager::RegistrationStatus::RegistrationStatus()
20    : model_type(syncable::UNSPECIFIED),
21      registration_manager(NULL),
22      state(invalidation::RegistrationState_UNREGISTERED) {}
23
24RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
25
26void RegistrationManager::RegistrationStatus::DoRegister() {
27  DCHECK_NE(model_type, syncable::UNSPECIFIED);
28  DCHECK(registration_manager);
29  // We might be called explicitly, so stop the timer manually and
30  // reset the delay.
31  registration_timer.Stop();
32  delay = base::TimeDelta();
33  registration_manager->DoRegisterType(model_type);
34  DCHECK(!last_registration_request.is_null());
35}
36
37const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
38const int RegistrationManager::kRegistrationDelayExponent = 2;
39const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
40const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
41// 1 hour.
42const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
43
44RegistrationManager::RegistrationManager(
45    invalidation::InvalidationClient* invalidation_client)
46    : invalidation_client_(invalidation_client) {
47  DCHECK(invalidation_client_);
48  // Initialize statuses.
49  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
50       i < syncable::MODEL_TYPE_COUNT; ++i) {
51    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
52    RegistrationStatus* status = &registration_statuses_[model_type];
53    status->model_type = model_type;
54    status->registration_manager = this;
55  }
56}
57
58RegistrationManager::~RegistrationManager() {
59  DCHECK(non_thread_safe_.CalledOnValidThread());
60}
61
62void RegistrationManager::SetRegisteredTypes(
63    const syncable::ModelTypeSet& types) {
64  DCHECK(non_thread_safe_.CalledOnValidThread());
65
66  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
67       i < syncable::MODEL_TYPE_COUNT; ++i) {
68    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
69    if (types.count(model_type) > 0) {
70      if (!IsTypeRegistered(model_type)) {
71        TryRegisterType(model_type, false /* is_retry */);
72      }
73    } else {
74      if (IsTypeRegistered(model_type)) {
75        UnregisterType(model_type);
76      }
77    }
78  }
79}
80
81void RegistrationManager::MarkRegistrationLost(
82    syncable::ModelType model_type) {
83  DCHECK(non_thread_safe_.CalledOnValidThread());
84  registration_statuses_[model_type].state =
85      invalidation::RegistrationState_UNREGISTERED;
86  TryRegisterType(model_type, true /* is_retry */);
87}
88
89void RegistrationManager::MarkAllRegistrationsLost() {
90  DCHECK(non_thread_safe_.CalledOnValidThread());
91  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
92       i < syncable::MODEL_TYPE_COUNT; ++i) {
93    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
94    if (IsTypeRegistered(model_type)) {
95      MarkRegistrationLost(model_type);
96    }
97  }
98}
99
100syncable::ModelTypeSet RegistrationManager::GetRegisteredTypes() const {
101  DCHECK(non_thread_safe_.CalledOnValidThread());
102  syncable::ModelTypeSet registered_types;
103  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
104       i < syncable::MODEL_TYPE_COUNT; ++i) {
105    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
106    if (IsTypeRegistered(model_type)) {
107      registered_types.insert(model_type);
108    }
109  }
110  return registered_types;
111}
112
113RegistrationManager::PendingRegistrationMap
114    RegistrationManager::GetPendingRegistrations() const {
115  DCHECK(non_thread_safe_.CalledOnValidThread());
116  PendingRegistrationMap pending_registrations;
117  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
118       i < syncable::MODEL_TYPE_COUNT; ++i) {
119    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
120    const RegistrationStatus& status = registration_statuses_[model_type];
121    if (status.registration_timer.IsRunning()) {
122      pending_registrations[model_type].last_registration_request =
123          status.last_registration_request;
124      pending_registrations[model_type].registration_attempt =
125          status.last_registration_attempt;
126      pending_registrations[model_type].delay = status.delay;
127      pending_registrations[model_type].actual_delay =
128          status.registration_timer.GetCurrentDelay();
129    }
130  }
131  return pending_registrations;
132}
133
134void RegistrationManager::FirePendingRegistrationsForTest() {
135  DCHECK(non_thread_safe_.CalledOnValidThread());
136  for (int i = syncable::FIRST_REAL_MODEL_TYPE;
137       i < syncable::MODEL_TYPE_COUNT; ++i) {
138    syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
139    RegistrationStatus* status = &registration_statuses_[model_type];
140    if (status->registration_timer.IsRunning()) {
141      status->DoRegister();
142    }
143  }
144}
145
146// static
147double RegistrationManager::CalculateBackoff(
148    double retry_interval,
149    double initial_retry_interval,
150    double min_retry_interval,
151    double max_retry_interval,
152    double backoff_exponent,
153    double jitter,
154    double max_jitter) {
155  // scaled_jitter lies in [-max_jitter, max_jitter].
156  double scaled_jitter = jitter * max_jitter;
157  double new_retry_interval =
158      (retry_interval == 0.0) ?
159      (initial_retry_interval * (1.0 + scaled_jitter)) :
160      (retry_interval * (backoff_exponent + scaled_jitter));
161  return std::max(min_retry_interval,
162                  std::min(max_retry_interval, new_retry_interval));
163}
164
165double RegistrationManager::GetJitter() {
166  // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
167  // barely.
168  //
169  // TODO(akalin): Fix the bias.
170  return 2.0 * base::RandDouble() - 1.0;
171}
172
173void RegistrationManager::TryRegisterType(syncable::ModelType model_type,
174                                          bool is_retry) {
175  DCHECK(non_thread_safe_.CalledOnValidThread());
176  RegistrationStatus* status = &registration_statuses_[model_type];
177  status->last_registration_attempt = base::Time::Now();
178  if (is_retry) {
179    // If we're a retry, we must have tried at least once before.
180    DCHECK(!status->last_registration_request.is_null());
181    // delay = max(0, (now - last request) + next_delay)
182    status->delay =
183        (status->last_registration_request -
184         status->last_registration_attempt) +
185        status->next_delay;
186    base::TimeDelta delay =
187        (status->delay <= base::TimeDelta()) ?
188        base::TimeDelta() : status->delay;
189    VLOG(2) << "Registering "
190            << syncable::ModelTypeToString(model_type) << " in "
191            << delay.InMilliseconds() << " ms";
192    status->registration_timer.Stop();
193    status->registration_timer.Start(
194        delay, status, &RegistrationManager::RegistrationStatus::DoRegister);
195    double next_delay_seconds =
196        CalculateBackoff(static_cast<double>(status->next_delay.InSeconds()),
197                         kInitialRegistrationDelaySeconds,
198                         kMinRegistrationDelaySeconds,
199                         kMaxRegistrationDelaySeconds,
200                         kRegistrationDelayExponent,
201                         GetJitter(),
202                         kRegistrationDelayMaxJitter);
203    status->next_delay =
204        base::TimeDelta::FromSeconds(static_cast<int64>(next_delay_seconds));
205    VLOG(2) << "New next delay for "
206            << syncable::ModelTypeToString(model_type) << " is "
207            << status->next_delay.InSeconds() << " seconds";
208  } else {
209    VLOG(2) << "Not a retry -- registering "
210            << syncable::ModelTypeToString(model_type) << " immediately";
211    status->delay = base::TimeDelta();
212    status->next_delay = base::TimeDelta();
213    status->DoRegister();
214  }
215}
216
217void RegistrationManager::DoRegisterType(syncable::ModelType model_type) {
218  DCHECK(non_thread_safe_.CalledOnValidThread());
219  invalidation::ObjectId object_id;
220  if (!RealModelTypeToObjectId(model_type, &object_id)) {
221    LOG(DFATAL) << "Invalid model type: " << model_type;
222    return;
223  }
224  invalidation_client_->Register(object_id);
225  RegistrationStatus* status = &registration_statuses_[model_type];
226  status->state = invalidation::RegistrationState_REGISTERED;
227  status->last_registration_request = base::Time::Now();
228}
229
230void RegistrationManager::UnregisterType(syncable::ModelType model_type) {
231  DCHECK(non_thread_safe_.CalledOnValidThread());
232  invalidation::ObjectId object_id;
233  if (!RealModelTypeToObjectId(model_type, &object_id)) {
234    LOG(DFATAL) << "Invalid model type: " << model_type;
235    return;
236  }
237  invalidation_client_->Unregister(object_id);
238  RegistrationStatus* status = &registration_statuses_[model_type];
239  status->state = invalidation::RegistrationState_UNREGISTERED;
240}
241
242bool RegistrationManager::IsTypeRegistered(
243    syncable::ModelType model_type) const {
244  DCHECK(non_thread_safe_.CalledOnValidThread());
245  return registration_statuses_[model_type].state ==
246      invalidation::RegistrationState_REGISTERED;
247}
248
249}  // namespace sync_notifier
250