Fix bug in load time stats.
[chromium-blink-merge.git] / sync / notifier / chrome_invalidation_client.cc
blob6ec46595a0df5420ca9f347dc42252cc9779bf22
1 // Copyright (c) 2012 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 "sync/notifier/chrome_invalidation_client.h"
7 #include <string>
8 #include <vector>
10 #include "base/callback.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/tracked_objects.h"
14 #include "google/cacheinvalidation/include/invalidation-client.h"
15 #include "google/cacheinvalidation/include/types.h"
16 #include "google/cacheinvalidation/types.pb.h"
17 #include "jingle/notifier/listener/push_client.h"
18 #include "sync/notifier/invalidation_util.h"
19 #include "sync/notifier/registration_manager.h"
21 namespace {
23 const char kApplicationName[] = "chrome-sync";
25 } // namespace
27 namespace syncer {
29 ChromeInvalidationClient::Listener::~Listener() {}
31 ChromeInvalidationClient::ChromeInvalidationClient(
32 scoped_ptr<notifier::PushClient> push_client)
33 : push_client_(push_client.get()),
34 chrome_system_resources_(push_client.Pass(),
35 ALLOW_THIS_IN_INITIALIZER_LIST(this)),
36 listener_(NULL),
37 ticl_state_(DEFAULT_NOTIFICATION_ERROR),
38 push_client_state_(DEFAULT_NOTIFICATION_ERROR) {
39 DCHECK(CalledOnValidThread());
40 push_client_->AddObserver(this);
43 ChromeInvalidationClient::~ChromeInvalidationClient() {
44 DCHECK(CalledOnValidThread());
45 push_client_->RemoveObserver(this);
46 Stop();
47 DCHECK(!listener_);
50 void ChromeInvalidationClient::Start(
51 const CreateInvalidationClientCallback&
52 create_invalidation_client_callback,
53 const std::string& client_id, const std::string& client_info,
54 const std::string& state,
55 const InvalidationVersionMap& initial_max_invalidation_versions,
56 const WeakHandle<InvalidationStateTracker>& invalidation_state_tracker,
57 Listener* listener) {
58 DCHECK(CalledOnValidThread());
59 Stop();
61 chrome_system_resources_.set_platform(client_info);
62 chrome_system_resources_.Start();
64 // The Storage resource is implemented as a write-through cache. We populate
65 // it with the initial state on startup, so subsequent writes go to disk and
66 // update the in-memory cache, while reads just return the cached state.
67 chrome_system_resources_.storage()->SetInitialState(state);
69 max_invalidation_versions_ = initial_max_invalidation_versions;
70 if (max_invalidation_versions_.empty()) {
71 DVLOG(2) << "No initial max invalidation versions for any id";
72 } else {
73 for (InvalidationVersionMap::const_iterator it =
74 max_invalidation_versions_.begin();
75 it != max_invalidation_versions_.end(); ++it) {
76 DVLOG(2) << "Initial max invalidation version for "
77 << ObjectIdToString(it->first) << " is "
78 << it->second;
81 invalidation_state_tracker_ = invalidation_state_tracker;
82 DCHECK(invalidation_state_tracker_.IsInitialized());
84 DCHECK(!listener_);
85 DCHECK(listener);
86 listener_ = listener;
88 int client_type = ipc::invalidation::ClientType::CHROME_SYNC;
89 invalidation_client_.reset(
90 create_invalidation_client_callback.Run(
91 &chrome_system_resources_, client_type, client_id,
92 kApplicationName, this));
93 invalidation_client_->Start();
95 registration_manager_.reset(
96 new RegistrationManager(invalidation_client_.get()));
99 void ChromeInvalidationClient::UpdateCredentials(
100 const std::string& email, const std::string& token) {
101 DCHECK(CalledOnValidThread());
102 chrome_system_resources_.network()->UpdateCredentials(email, token);
105 void ChromeInvalidationClient::UpdateRegisteredIds(const ObjectIdSet& ids) {
106 DCHECK(CalledOnValidThread());
107 registered_ids_ = ids;
108 // |ticl_state_| can go to NO_NOTIFICATION_ERROR even without a
109 // working XMPP connection (as observed by us), so check it instead
110 // of GetState() (see http://crbug.com/139424).
111 if (ticl_state_ == NO_NOTIFICATION_ERROR && registration_manager_.get()) {
112 registration_manager_->UpdateRegisteredIds(registered_ids_);
114 // TODO(akalin): Clear invalidation versions for unregistered types.
117 void ChromeInvalidationClient::Ready(
118 invalidation::InvalidationClient* client) {
119 DCHECK(CalledOnValidThread());
120 DCHECK_EQ(client, invalidation_client_.get());
121 ticl_state_ = NO_NOTIFICATION_ERROR;
122 EmitStateChange();
123 registration_manager_->UpdateRegisteredIds(registered_ids_);
126 void ChromeInvalidationClient::Invalidate(
127 invalidation::InvalidationClient* client,
128 const invalidation::Invalidation& invalidation,
129 const invalidation::AckHandle& ack_handle) {
130 DCHECK(CalledOnValidThread());
131 DCHECK_EQ(client, invalidation_client_.get());
132 DVLOG(1) << "Invalidate: " << InvalidationToString(invalidation);
134 const invalidation::ObjectId& id = invalidation.object_id();
136 // The invalidation API spec allows for the possibility of redundant
137 // invalidations, so keep track of the max versions and drop
138 // invalidations with old versions.
140 // TODO(akalin): Now that we keep track of registered ids, we
141 // should drop invalidations for unregistered ids. We may also
142 // have to filter it at a higher level, as invalidations for
143 // newly-unregistered ids may already be in flight.
144 InvalidationVersionMap::const_iterator it =
145 max_invalidation_versions_.find(id);
146 if ((it != max_invalidation_versions_.end()) &&
147 (invalidation.version() <= it->second)) {
148 // Drop redundant invalidations.
149 client->Acknowledge(ack_handle);
150 return;
152 DVLOG(2) << "Setting max invalidation version for " << ObjectIdToString(id)
153 << " to " << invalidation.version();
154 max_invalidation_versions_[id] = invalidation.version();
155 invalidation_state_tracker_.Call(
156 FROM_HERE,
157 &InvalidationStateTracker::SetMaxVersion,
158 id, invalidation.version());
160 std::string payload;
161 // payload() CHECK()'s has_payload(), so we must check it ourselves first.
162 if (invalidation.has_payload())
163 payload = invalidation.payload();
165 ObjectIdPayloadMap id_payloads;
166 id_payloads[id] = payload;
167 EmitInvalidation(id_payloads);
168 // TODO(akalin): We should really acknowledge only after we get the
169 // updates from the sync server. (see http://crbug.com/78462).
170 client->Acknowledge(ack_handle);
173 void ChromeInvalidationClient::InvalidateUnknownVersion(
174 invalidation::InvalidationClient* client,
175 const invalidation::ObjectId& object_id,
176 const invalidation::AckHandle& ack_handle) {
177 DCHECK(CalledOnValidThread());
178 DCHECK_EQ(client, invalidation_client_.get());
179 DVLOG(1) << "InvalidateUnknownVersion";
181 ObjectIdPayloadMap id_payloads;
182 id_payloads[object_id] = std::string();
183 EmitInvalidation(id_payloads);
184 // TODO(akalin): We should really acknowledge only after we get the
185 // updates from the sync server. (see http://crbug.com/78462).
186 client->Acknowledge(ack_handle);
189 // This should behave as if we got an invalidation with version
190 // UNKNOWN_OBJECT_VERSION for all known data types.
191 void ChromeInvalidationClient::InvalidateAll(
192 invalidation::InvalidationClient* client,
193 const invalidation::AckHandle& ack_handle) {
194 DCHECK(CalledOnValidThread());
195 DCHECK_EQ(client, invalidation_client_.get());
196 DVLOG(1) << "InvalidateAll";
198 ObjectIdPayloadMap id_payloads;
199 for (ObjectIdSet::const_iterator it = registered_ids_.begin();
200 it != registered_ids_.end(); ++it) {
201 id_payloads[*it] = std::string();
203 EmitInvalidation(id_payloads);
204 // TODO(akalin): We should really acknowledge only after we get the
205 // updates from the sync server. (see http://crbug.com/76482).
206 client->Acknowledge(ack_handle);
209 void ChromeInvalidationClient::EmitInvalidation(
210 const ObjectIdPayloadMap& id_payloads) {
211 DCHECK(CalledOnValidThread());
212 listener_->OnInvalidate(id_payloads);
215 void ChromeInvalidationClient::InformRegistrationStatus(
216 invalidation::InvalidationClient* client,
217 const invalidation::ObjectId& object_id,
218 InvalidationListener::RegistrationState new_state) {
219 DCHECK(CalledOnValidThread());
220 DCHECK_EQ(client, invalidation_client_.get());
221 DVLOG(1) << "InformRegistrationStatus: "
222 << ObjectIdToString(object_id) << " " << new_state;
224 if (new_state != InvalidationListener::REGISTERED) {
225 // Let |registration_manager_| handle the registration backoff policy.
226 registration_manager_->MarkRegistrationLost(object_id);
230 void ChromeInvalidationClient::InformRegistrationFailure(
231 invalidation::InvalidationClient* client,
232 const invalidation::ObjectId& object_id,
233 bool is_transient,
234 const std::string& error_message) {
235 DCHECK(CalledOnValidThread());
236 DCHECK_EQ(client, invalidation_client_.get());
237 DVLOG(1) << "InformRegistrationFailure: "
238 << ObjectIdToString(object_id)
239 << "is_transient=" << is_transient
240 << ", message=" << error_message;
242 if (is_transient) {
243 // We don't care about |unknown_hint|; we let
244 // |registration_manager_| handle the registration backoff policy.
245 registration_manager_->MarkRegistrationLost(object_id);
246 } else {
247 // Non-transient failures are permanent, so block any future
248 // registration requests for |model_type|. (This happens if the
249 // server doesn't recognize the data type, which could happen for
250 // brand-new data types.)
251 registration_manager_->DisableId(object_id);
255 void ChromeInvalidationClient::ReissueRegistrations(
256 invalidation::InvalidationClient* client,
257 const std::string& prefix,
258 int prefix_length) {
259 DCHECK(CalledOnValidThread());
260 DCHECK_EQ(client, invalidation_client_.get());
261 DVLOG(1) << "AllRegistrationsLost";
262 registration_manager_->MarkAllRegistrationsLost();
265 void ChromeInvalidationClient::InformError(
266 invalidation::InvalidationClient* client,
267 const invalidation::ErrorInfo& error_info) {
268 DCHECK(CalledOnValidThread());
269 DCHECK_EQ(client, invalidation_client_.get());
270 LOG(ERROR) << "Ticl error " << error_info.error_reason() << ": "
271 << error_info.error_message()
272 << " (transient = " << error_info.is_transient() << ")";
273 if (error_info.error_reason() == invalidation::ErrorReason::AUTH_FAILURE) {
274 ticl_state_ = NOTIFICATION_CREDENTIALS_REJECTED;
275 } else {
276 ticl_state_ = TRANSIENT_NOTIFICATION_ERROR;
278 EmitStateChange();
281 void ChromeInvalidationClient::WriteState(const std::string& state) {
282 DCHECK(CalledOnValidThread());
283 DVLOG(1) << "WriteState";
284 invalidation_state_tracker_.Call(
285 FROM_HERE, &InvalidationStateTracker::SetInvalidationState, state);
288 void ChromeInvalidationClient::StopForTest() {
289 DCHECK(CalledOnValidThread());
290 Stop();
293 void ChromeInvalidationClient::Stop() {
294 DCHECK(CalledOnValidThread());
295 if (!invalidation_client_.get()) {
296 return;
299 registration_manager_.reset();
300 chrome_system_resources_.Stop();
301 invalidation_client_->Stop();
303 invalidation_client_.reset();
304 listener_ = NULL;
306 invalidation_state_tracker_.Reset();
307 max_invalidation_versions_.clear();
308 ticl_state_ = DEFAULT_NOTIFICATION_ERROR;
309 push_client_state_ = DEFAULT_NOTIFICATION_ERROR;
312 NotificationsDisabledReason ChromeInvalidationClient::GetState() const {
313 DCHECK(CalledOnValidThread());
314 if (ticl_state_ == NOTIFICATION_CREDENTIALS_REJECTED ||
315 push_client_state_ == NOTIFICATION_CREDENTIALS_REJECTED) {
316 // If either the ticl or the push client rejected our credentials,
317 // return NOTIFICATION_CREDENTIALS_REJECTED.
318 return NOTIFICATION_CREDENTIALS_REJECTED;
320 if (ticl_state_ == NO_NOTIFICATION_ERROR &&
321 push_client_state_ == NO_NOTIFICATION_ERROR) {
322 // If the ticl is ready and the push client notifications are
323 // enabled, return NO_NOTIFICATION_ERROR.
324 return NO_NOTIFICATION_ERROR;
326 // Otherwise, we have a transient error.
327 return TRANSIENT_NOTIFICATION_ERROR;
330 void ChromeInvalidationClient::EmitStateChange() {
331 DCHECK(CalledOnValidThread());
332 if (GetState() == NO_NOTIFICATION_ERROR) {
333 listener_->OnNotificationsEnabled();
334 } else {
335 listener_->OnNotificationsDisabled(GetState());
339 void ChromeInvalidationClient::OnNotificationsEnabled() {
340 DCHECK(CalledOnValidThread());
341 push_client_state_ = NO_NOTIFICATION_ERROR;
342 EmitStateChange();
345 void ChromeInvalidationClient::OnNotificationsDisabled(
346 notifier::NotificationsDisabledReason reason) {
347 DCHECK(CalledOnValidThread());
348 push_client_state_ = FromNotifierReason(reason);
349 EmitStateChange();
352 void ChromeInvalidationClient::OnIncomingNotification(
353 const notifier::Notification& notification) {
354 DCHECK(CalledOnValidThread());
355 // Do nothing, since this is already handled by |invalidation_client_|.
358 } // namespace syncer