Add ability for NetLogLogger to gather data from more than just NetLog
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blob1c9f64fbe447498e27d1ca4a8c47b77a210adb1b
1 // Copyright 2014 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/gcm_driver/gcm_client_impl.h"
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/default_clock.h"
18 #include "base/timer/timer.h"
19 #include "components/gcm_driver/gcm_account_mapper.h"
20 #include "components/gcm_driver/gcm_backoff_policy.h"
21 #include "google_apis/gcm/base/encryptor.h"
22 #include "google_apis/gcm/base/mcs_message.h"
23 #include "google_apis/gcm/base/mcs_util.h"
24 #include "google_apis/gcm/engine/checkin_request.h"
25 #include "google_apis/gcm/engine/connection_factory_impl.h"
26 #include "google_apis/gcm/engine/gcm_store_impl.h"
27 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
28 #include "google_apis/gcm/protocol/checkin.pb.h"
29 #include "google_apis/gcm/protocol/mcs.pb.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_transaction_factory.h"
32 #include "net/url_request/url_request_context.h"
33 #include "url/gurl.h"
35 namespace gcm {
37 namespace {
39 // Indicates a message type of the received message.
40 enum MessageType {
41 UNKNOWN, // Undetermined type.
42 DATA_MESSAGE, // Regular data message.
43 DELETED_MESSAGES, // Messages were deleted on the server.
44 SEND_ERROR, // Error sending a message.
47 enum OutgoingMessageTTLCategory {
48 TTL_ZERO,
49 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
50 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
51 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
52 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
53 TTL_MORE_THAN_ONE_WEEK,
54 TTL_MAXIMUM,
55 // NOTE: always keep this entry at the end. Add new TTL category only
56 // immediately above this line. Make sure to update the corresponding
57 // histogram enum accordingly.
58 TTL_CATEGORY_COUNT
61 enum ResetStoreError {
62 DESTROYING_STORE_FAILED,
63 INFINITE_STORE_RESET,
64 // NOTE: always keep this entry at the end. Add new value only immediately
65 // above this line. Make sure to update the corresponding histogram enum
66 // accordingly.
67 RESET_STORE_ERROR_COUNT
70 const int kMaxRegistrationRetries = 5;
71 const char kMessageTypeDataMessage[] = "gcm";
72 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
73 const char kMessageTypeKey[] = "message_type";
74 const char kMessageTypeSendErrorKey[] = "send_error";
75 const char kSendErrorMessageIdKey[] = "google.message_id";
76 const char kSendMessageFromValue[] = "gcm@chrome.com";
77 const int64 kDefaultUserSerialNumber = 0LL;
79 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
80 switch (status) {
81 case MCSClient::QUEUED:
82 return GCMClient::SUCCESS;
83 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
84 return GCMClient::NETWORK_ERROR;
85 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
86 return GCMClient::NETWORK_ERROR;
87 case MCSClient::MESSAGE_TOO_LARGE:
88 return GCMClient::INVALID_PARAMETER;
89 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
90 return GCMClient::NETWORK_ERROR;
91 case MCSClient::TTL_EXCEEDED:
92 return GCMClient::NETWORK_ERROR;
93 case MCSClient::SENT:
94 default:
95 NOTREACHED();
96 break;
98 return GCMClientImpl::UNKNOWN_ERROR;
101 void ToCheckinProtoVersion(
102 const GCMClient::ChromeBuildInfo& chrome_build_info,
103 checkin_proto::ChromeBuildProto* android_build_info) {
104 checkin_proto::ChromeBuildProto_Platform platform;
105 switch (chrome_build_info.platform) {
106 case GCMClient::PLATFORM_WIN:
107 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
108 break;
109 case GCMClient::PLATFORM_MAC:
110 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
111 break;
112 case GCMClient::PLATFORM_LINUX:
113 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
114 break;
115 case GCMClient::PLATFORM_IOS:
116 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
117 break;
118 case GCMClient::PLATFORM_ANDROID:
119 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
120 break;
121 case GCMClient::PLATFORM_CROS:
122 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
123 break;
124 case GCMClient::PLATFORM_UNKNOWN:
125 // For unknown platform, return as LINUX.
126 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
127 break;
128 default:
129 NOTREACHED();
130 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
131 break;
133 android_build_info->set_platform(platform);
135 checkin_proto::ChromeBuildProto_Channel channel;
136 switch (chrome_build_info.channel) {
137 case GCMClient::CHANNEL_STABLE:
138 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
139 break;
140 case GCMClient::CHANNEL_BETA:
141 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
142 break;
143 case GCMClient::CHANNEL_DEV:
144 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
145 break;
146 case GCMClient::CHANNEL_CANARY:
147 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
148 break;
149 case GCMClient::CHANNEL_UNKNOWN:
150 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
151 break;
152 default:
153 NOTREACHED();
154 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
155 break;
157 android_build_info->set_channel(channel);
159 android_build_info->set_chrome_version(chrome_build_info.version);
162 MessageType DecodeMessageType(const std::string& value) {
163 if (kMessageTypeDeletedMessagesKey == value)
164 return DELETED_MESSAGES;
165 if (kMessageTypeSendErrorKey == value)
166 return SEND_ERROR;
167 if (kMessageTypeDataMessage == value)
168 return DATA_MESSAGE;
169 return UNKNOWN;
172 void RecordOutgoingMessageToUMA(
173 const gcm::GCMClient::OutgoingMessage& message) {
174 OutgoingMessageTTLCategory ttl_category;
175 if (message.time_to_live == 0)
176 ttl_category = TTL_ZERO;
177 else if (message.time_to_live <= 60 )
178 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
179 else if (message.time_to_live <= 60 * 60)
180 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
181 else if (message.time_to_live <= 24 * 60 * 60)
182 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
183 else
184 ttl_category = TTL_MAXIMUM;
186 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
187 ttl_category,
188 TTL_CATEGORY_COUNT);
191 void RecordResetStoreErrorToUMA(ResetStoreError error) {
192 UMA_HISTOGRAM_ENUMERATION("GCM.ResetStore", error, RESET_STORE_ERROR_COUNT);
195 } // namespace
197 GCMInternalsBuilder::GCMInternalsBuilder() {}
198 GCMInternalsBuilder::~GCMInternalsBuilder() {}
200 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
201 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
204 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
205 const std::string& version,
206 base::Clock* clock,
207 ConnectionFactory* connection_factory,
208 GCMStore* gcm_store,
209 GCMStatsRecorder* recorder) {
210 return scoped_ptr<MCSClient>(new MCSClient(
211 version, clock, connection_factory, gcm_store, recorder));
214 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
215 const std::vector<GURL>& endpoints,
216 const net::BackoffEntry::Policy& backoff_policy,
217 const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
218 const scoped_refptr<net::HttpNetworkSession>& http_network_session,
219 net::NetLog* net_log,
220 GCMStatsRecorder* recorder) {
221 return make_scoped_ptr<ConnectionFactory>(
222 new ConnectionFactoryImpl(endpoints,
223 backoff_policy,
224 gcm_network_session,
225 http_network_session,
226 net_log,
227 recorder));
230 GCMClientImpl::CheckinInfo::CheckinInfo()
231 : android_id(0), secret(0), accounts_set(false) {
234 GCMClientImpl::CheckinInfo::~CheckinInfo() {
237 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
238 last_checkin_accounts.clear();
239 for (std::map<std::string, std::string>::iterator iter =
240 account_tokens.begin();
241 iter != account_tokens.end();
242 ++iter) {
243 last_checkin_accounts.insert(iter->first);
247 void GCMClientImpl::CheckinInfo::Reset() {
248 android_id = 0;
249 secret = 0;
250 accounts_set = false;
251 account_tokens.clear();
252 last_checkin_accounts.clear();
255 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
256 : internals_builder_(internals_builder.Pass()),
257 state_(UNINITIALIZED),
258 delegate_(NULL),
259 start_mode_(DELAYED_START),
260 clock_(internals_builder_->BuildClock()),
261 gcm_store_reset_(false),
262 url_request_context_getter_(NULL),
263 pending_registration_requests_deleter_(&pending_registration_requests_),
264 pending_unregistration_requests_deleter_(
265 &pending_unregistration_requests_),
266 periodic_checkin_ptr_factory_(this),
267 weak_ptr_factory_(this) {
270 GCMClientImpl::~GCMClientImpl() {
273 void GCMClientImpl::Initialize(
274 const ChromeBuildInfo& chrome_build_info,
275 const base::FilePath& path,
276 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
277 const scoped_refptr<net::URLRequestContextGetter>&
278 url_request_context_getter,
279 scoped_ptr<Encryptor> encryptor,
280 GCMClient::Delegate* delegate) {
281 DCHECK_EQ(UNINITIALIZED, state_);
282 DCHECK(url_request_context_getter.get());
283 DCHECK(delegate);
285 url_request_context_getter_ = url_request_context_getter;
286 const net::HttpNetworkSession::Params* network_session_params =
287 url_request_context_getter_->GetURLRequestContext()->
288 GetNetworkSessionParams();
289 DCHECK(network_session_params);
290 network_session_ = new net::HttpNetworkSession(*network_session_params);
292 chrome_build_info_ = chrome_build_info;
294 gcm_store_.reset(
295 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
297 delegate_ = delegate;
299 recorder_.SetDelegate(this);
301 state_ = INITIALIZED;
304 void GCMClientImpl::Start(StartMode start_mode) {
305 DCHECK_NE(UNINITIALIZED, state_);
307 if (state_ == LOADED) {
308 // Start the GCM if not yet.
309 if (start_mode == IMMEDIATE_START)
310 StartGCM();
311 return;
314 // The delay start behavior will be abandoned when Start has been called
315 // once with IMMEDIATE_START behavior.
316 if (start_mode == IMMEDIATE_START)
317 start_mode_ = IMMEDIATE_START;
319 // Bail out if the loading is not started or completed.
320 if (state_ != INITIALIZED)
321 return;
323 // Once the loading is completed, the check-in will be initiated.
324 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
325 weak_ptr_factory_.GetWeakPtr()));
326 state_ = LOADING;
329 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
330 DCHECK_EQ(LOADING, state_);
332 if (!result->success) {
333 ResetStore();
334 return;
336 gcm_store_reset_ = false;
338 registrations_ = result->registrations;
339 device_checkin_info_.android_id = result->device_android_id;
340 device_checkin_info_.secret = result->device_security_token;
341 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
342 // A case where there were previously no accounts reported with checkin is
343 // considered to be the same as when the list of accounts is empty. It enables
344 // scheduling a periodic checkin for devices with no signed in users
345 // immediately after restart, while keeping |accounts_set == false| delays the
346 // checkin until the list of accounts is set explicitly.
347 if (result->last_checkin_accounts.size() == 0)
348 device_checkin_info_.accounts_set = true;
349 last_checkin_time_ = result->last_checkin_time;
350 gservices_settings_.UpdateFromLoadResult(*result);
351 load_result_ = result.Pass();
352 state_ = LOADED;
354 // Don't initiate the GCM connection when GCM is in delayed start mode and
355 // not any standalone app has registered GCM yet.
356 if (start_mode_ == DELAYED_START && !HasStandaloneRegisteredApp())
357 return;
359 StartGCM();
362 void GCMClientImpl::StartGCM() {
363 // Taking over the value of account_mappings before passing the ownership of
364 // load result to InitializeMCSClient.
365 std::vector<AccountMapping> account_mappings;
366 account_mappings.swap(load_result_->account_mappings);
367 base::Time last_token_fetch_time = load_result_->last_token_fetch_time;
369 InitializeMCSClient();
371 if (device_checkin_info_.IsValid()) {
372 SchedulePeriodicCheckin();
373 OnReady(account_mappings, last_token_fetch_time);
374 return;
377 state_ = INITIAL_DEVICE_CHECKIN;
378 device_checkin_info_.Reset();
379 StartCheckin();
382 void GCMClientImpl::InitializeMCSClient() {
383 std::vector<GURL> endpoints;
384 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
385 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
386 connection_factory_ = internals_builder_->BuildConnectionFactory(
387 endpoints,
388 GetGCMBackoffPolicy(),
389 network_session_,
390 url_request_context_getter_->GetURLRequestContext()
391 ->http_transaction_factory()
392 ->GetSession(),
393 net_log_.net_log(),
394 &recorder_);
395 connection_factory_->SetConnectionListener(this);
396 mcs_client_ = internals_builder_->BuildMCSClient(
397 chrome_build_info_.version,
398 clock_.get(),
399 connection_factory_.get(),
400 gcm_store_.get(),
401 &recorder_).Pass();
403 mcs_client_->Initialize(
404 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
405 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
406 weak_ptr_factory_.GetWeakPtr()),
407 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
408 weak_ptr_factory_.GetWeakPtr()),
409 load_result_.Pass());
412 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
413 const CheckinInfo& checkin_info) {
414 DCHECK(!device_checkin_info_.IsValid());
416 device_checkin_info_.android_id = checkin_info.android_id;
417 device_checkin_info_.secret = checkin_info.secret;
418 // If accounts were not set by now, we can consider them set (to empty list)
419 // to make sure periodic checkins get scheduled after initial checkin.
420 device_checkin_info_.accounts_set = true;
421 gcm_store_->SetDeviceCredentials(
422 checkin_info.android_id, checkin_info.secret,
423 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
424 weak_ptr_factory_.GetWeakPtr()));
426 OnReady(std::vector<AccountMapping>(), base::Time());
429 void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
430 const base::Time& last_token_fetch_time) {
431 state_ = READY;
432 StartMCSLogin();
434 delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
437 void GCMClientImpl::StartMCSLogin() {
438 DCHECK_EQ(READY, state_);
439 DCHECK(device_checkin_info_.IsValid());
440 mcs_client_->Login(device_checkin_info_.android_id,
441 device_checkin_info_.secret);
444 void GCMClientImpl::ResetStore() {
445 DCHECK_EQ(LOADING, state_);
447 // If already being reset, don't do it again. We want to prevent from
448 // resetting and loading from the store again and again.
449 if (gcm_store_reset_) {
450 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET);
451 state_ = UNINITIALIZED;
452 return;
454 gcm_store_reset_ = true;
456 // Destroy the GCM store to start over.
457 gcm_store_->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback,
458 weak_ptr_factory_.GetWeakPtr()));
461 void GCMClientImpl::SetAccountTokens(
462 const std::vector<AccountTokenInfo>& account_tokens) {
463 device_checkin_info_.account_tokens.clear();
464 for (std::vector<AccountTokenInfo>::const_iterator iter =
465 account_tokens.begin();
466 iter != account_tokens.end();
467 ++iter) {
468 device_checkin_info_.account_tokens[iter->email] = iter->access_token;
471 bool accounts_set_before = device_checkin_info_.accounts_set;
472 device_checkin_info_.accounts_set = true;
474 DVLOG(1) << "Set account called with: " << account_tokens.size()
475 << " accounts.";
477 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
478 return;
480 bool account_removed = false;
481 for (std::set<std::string>::iterator iter =
482 device_checkin_info_.last_checkin_accounts.begin();
483 iter != device_checkin_info_.last_checkin_accounts.end();
484 ++iter) {
485 if (device_checkin_info_.account_tokens.find(*iter) ==
486 device_checkin_info_.account_tokens.end()) {
487 account_removed = true;
491 // Checkin will be forced when any of the accounts was removed during the
492 // current Chrome session or if there has been an account removed between the
493 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
494 // We only force checkin when user signs out. When there is a new account
495 // signed in, the periodic checkin will take care of adding the association in
496 // reasonable time.
497 if (account_removed) {
498 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
499 checkin_request_.reset();
500 StartCheckin();
501 } else if (!accounts_set_before) {
502 SchedulePeriodicCheckin();
503 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
507 void GCMClientImpl::UpdateAccountMapping(
508 const AccountMapping& account_mapping) {
509 gcm_store_->AddAccountMapping(account_mapping,
510 base::Bind(&GCMClientImpl::DefaultStoreCallback,
511 weak_ptr_factory_.GetWeakPtr()));
514 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
515 gcm_store_->RemoveAccountMapping(
516 account_id,
517 base::Bind(&GCMClientImpl::DefaultStoreCallback,
518 weak_ptr_factory_.GetWeakPtr()));
521 void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
522 gcm_store_->SetLastTokenFetchTime(
523 time,
524 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
525 weak_ptr_factory_.GetWeakPtr()));
528 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
529 DCHECK(mcs_client_);
530 mcs_client_->UpdateHeartbeatTimer(timer.Pass());
533 void GCMClientImpl::StartCheckin() {
534 // Make sure no checkin is in progress.
535 if (checkin_request_.get())
536 return;
538 checkin_proto::ChromeBuildProto chrome_build_proto;
539 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
540 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
541 device_checkin_info_.secret,
542 device_checkin_info_.account_tokens,
543 gservices_settings_.digest(),
544 chrome_build_proto);
545 checkin_request_.reset(
546 new CheckinRequest(gservices_settings_.GetCheckinURL(),
547 request_info,
548 GetGCMBackoffPolicy(),
549 base::Bind(&GCMClientImpl::OnCheckinCompleted,
550 weak_ptr_factory_.GetWeakPtr()),
551 url_request_context_getter_.get(),
552 &recorder_));
553 // Taking a snapshot of the accounts count here, as there might be an asynch
554 // update of the account tokens while checkin is in progress.
555 device_checkin_info_.SnapshotCheckinAccounts();
556 checkin_request_->Start();
559 void GCMClientImpl::OnCheckinCompleted(
560 const checkin_proto::AndroidCheckinResponse& checkin_response) {
561 checkin_request_.reset();
563 if (!checkin_response.has_android_id() ||
564 !checkin_response.has_security_token()) {
565 // TODO(fgorski): I don't think a retry here will help, we should probably
566 // start over. By checking in with (0, 0).
567 return;
570 CheckinInfo checkin_info;
571 checkin_info.android_id = checkin_response.android_id();
572 checkin_info.secret = checkin_response.security_token();
574 if (state_ == INITIAL_DEVICE_CHECKIN) {
575 OnFirstTimeDeviceCheckinCompleted(checkin_info);
576 } else {
577 // checkin_info is not expected to change after a periodic checkin as it
578 // would invalidate the registratoin IDs.
579 DCHECK_EQ(READY, state_);
580 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
581 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
584 if (device_checkin_info_.IsValid()) {
585 // First update G-services settings, as something might have changed.
586 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
587 gcm_store_->SetGServicesSettings(
588 gservices_settings_.settings_map(),
589 gservices_settings_.digest(),
590 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
591 weak_ptr_factory_.GetWeakPtr()));
594 last_checkin_time_ = clock_->Now();
595 gcm_store_->SetLastCheckinInfo(
596 last_checkin_time_,
597 device_checkin_info_.last_checkin_accounts,
598 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
599 weak_ptr_factory_.GetWeakPtr()));
600 SchedulePeriodicCheckin();
604 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
605 DCHECK(success);
608 void GCMClientImpl::SchedulePeriodicCheckin() {
609 // Make sure no checkin is in progress.
610 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
611 return;
613 // There should be only one periodic checkin pending at a time. Removing
614 // pending periodic checkin to schedule a new one.
615 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
617 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
618 if (time_to_next_checkin < base::TimeDelta())
619 time_to_next_checkin = base::TimeDelta();
621 base::MessageLoop::current()->PostDelayedTask(
622 FROM_HERE,
623 base::Bind(&GCMClientImpl::StartCheckin,
624 periodic_checkin_ptr_factory_.GetWeakPtr()),
625 time_to_next_checkin);
628 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
629 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
630 clock_->Now();
633 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
634 // TODO(fgorski): This is one of the signals that store needs a rebuild.
635 DCHECK(success);
638 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
639 // TODO(fgorski): This is one of the signals that store needs a rebuild.
640 DCHECK(success);
643 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
644 // TODO(fgorski): This is one of the signals that store needs a rebuild.
645 DCHECK(success);
648 void GCMClientImpl::DefaultStoreCallback(bool success) {
649 DCHECK(success);
652 void GCMClientImpl::IgnoreWriteResultCallback(bool success) {
653 // TODO(fgorski): Ignoring the write result for now to make sure
654 // sync_intergration_tests are not broken.
657 void GCMClientImpl::ResetStoreCallback(bool success) {
658 if (!success) {
659 LOG(ERROR) << "Failed to reset GCM store";
660 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
661 state_ = UNINITIALIZED;
662 return;
665 state_ = INITIALIZED;
666 Start(start_mode_);
669 void GCMClientImpl::Stop() {
670 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
671 // Shutdown.
672 DVLOG(1) << "Stopping the GCM Client";
673 weak_ptr_factory_.InvalidateWeakPtrs();
674 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
675 device_checkin_info_.Reset();
676 connection_factory_.reset();
677 delegate_->OnDisconnected();
678 mcs_client_.reset();
679 checkin_request_.reset();
680 // Delete all of the pending registration and unregistration requests.
681 STLDeleteValues(&pending_registration_requests_);
682 STLDeleteValues(&pending_unregistration_requests_);
683 state_ = INITIALIZED;
684 gcm_store_->Close();
687 void GCMClientImpl::Register(const std::string& app_id,
688 const std::vector<std::string>& sender_ids) {
689 DCHECK_EQ(state_, READY);
691 // If the same sender ids is provided, return the cached registration ID
692 // directly.
693 RegistrationInfoMap::const_iterator registrations_iter =
694 registrations_.find(app_id);
695 if (registrations_iter != registrations_.end() &&
696 registrations_iter->second->sender_ids == sender_ids) {
697 delegate_->OnRegisterFinished(
698 app_id, registrations_iter->second->registration_id, SUCCESS);
699 return;
702 RegistrationRequest::RequestInfo request_info(
703 device_checkin_info_.android_id,
704 device_checkin_info_.secret,
705 app_id,
706 sender_ids);
707 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
709 RegistrationRequest* registration_request =
710 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
711 request_info,
712 GetGCMBackoffPolicy(),
713 base::Bind(&GCMClientImpl::OnRegisterCompleted,
714 weak_ptr_factory_.GetWeakPtr(),
715 app_id,
716 sender_ids),
717 kMaxRegistrationRetries,
718 url_request_context_getter_,
719 &recorder_);
720 pending_registration_requests_[app_id] = registration_request;
721 registration_request->Start();
724 void GCMClientImpl::OnRegisterCompleted(
725 const std::string& app_id,
726 const std::vector<std::string>& sender_ids,
727 RegistrationRequest::Status status,
728 const std::string& registration_id) {
729 DCHECK(delegate_);
731 Result result;
732 PendingRegistrationRequests::iterator iter =
733 pending_registration_requests_.find(app_id);
734 if (iter == pending_registration_requests_.end())
735 result = UNKNOWN_ERROR;
736 else if (status == RegistrationRequest::INVALID_SENDER)
737 result = INVALID_PARAMETER;
738 else if (registration_id.empty())
739 result = SERVER_ERROR;
740 else
741 result = SUCCESS;
743 if (result == SUCCESS) {
744 // Cache it.
745 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
746 registration->sender_ids = sender_ids;
747 registration->registration_id = registration_id;
748 registrations_[app_id] = registration;
750 // Save it in the persistent store.
751 gcm_store_->AddRegistration(
752 app_id,
753 registration,
754 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
755 weak_ptr_factory_.GetWeakPtr()));
758 delegate_->OnRegisterFinished(
759 app_id, result == SUCCESS ? registration_id : std::string(), result);
761 if (iter != pending_registration_requests_.end()) {
762 delete iter->second;
763 pending_registration_requests_.erase(iter);
767 void GCMClientImpl::Unregister(const std::string& app_id) {
768 DCHECK_EQ(state_, READY);
769 if (pending_unregistration_requests_.count(app_id) == 1)
770 return;
772 // Remove from the cache and persistent store.
773 registrations_.erase(app_id);
774 gcm_store_->RemoveRegistration(
775 app_id,
776 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
777 weak_ptr_factory_.GetWeakPtr()));
779 UnregistrationRequest::RequestInfo request_info(
780 device_checkin_info_.android_id,
781 device_checkin_info_.secret,
782 app_id);
784 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
785 gservices_settings_.GetRegistrationURL(),
786 request_info,
787 GetGCMBackoffPolicy(),
788 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
789 weak_ptr_factory_.GetWeakPtr(),
790 app_id),
791 url_request_context_getter_,
792 &recorder_);
793 pending_unregistration_requests_[app_id] = unregistration_request;
794 unregistration_request->Start();
797 void GCMClientImpl::OnUnregisterCompleted(
798 const std::string& app_id,
799 UnregistrationRequest::Status status) {
800 DVLOG(1) << "Unregister completed for app: " << app_id
801 << " with " << (status ? "success." : "failure.");
802 delegate_->OnUnregisterFinished(
803 app_id,
804 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
806 PendingUnregistrationRequests::iterator iter =
807 pending_unregistration_requests_.find(app_id);
808 if (iter == pending_unregistration_requests_.end())
809 return;
811 delete iter->second;
812 pending_unregistration_requests_.erase(iter);
815 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
816 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
817 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
820 void GCMClientImpl::Send(const std::string& app_id,
821 const std::string& receiver_id,
822 const OutgoingMessage& message) {
823 DCHECK_EQ(state_, READY);
825 RecordOutgoingMessageToUMA(message);
827 mcs_proto::DataMessageStanza stanza;
828 stanza.set_ttl(message.time_to_live);
829 stanza.set_sent(clock_->Now().ToInternalValue() /
830 base::Time::kMicrosecondsPerSecond);
831 stanza.set_id(message.id);
832 stanza.set_from(kSendMessageFromValue);
833 stanza.set_to(receiver_id);
834 stanza.set_category(app_id);
836 for (MessageData::const_iterator iter = message.data.begin();
837 iter != message.data.end();
838 ++iter) {
839 mcs_proto::AppData* app_data = stanza.add_app_data();
840 app_data->set_key(iter->first);
841 app_data->set_value(iter->second);
844 MCSMessage mcs_message(stanza);
845 DVLOG(1) << "MCS message size: " << mcs_message.size();
846 mcs_client_->SendMessage(mcs_message);
849 std::string GCMClientImpl::GetStateString() const {
850 switch(state_) {
851 case GCMClientImpl::INITIALIZED:
852 return "INITIALIZED";
853 case GCMClientImpl::UNINITIALIZED:
854 return "UNINITIALIZED";
855 case GCMClientImpl::LOADING:
856 return "LOADING";
857 case GCMClientImpl::LOADED:
858 return "LOADED";
859 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
860 return "INITIAL_DEVICE_CHECKIN";
861 case GCMClientImpl::READY:
862 return "READY";
863 default:
864 NOTREACHED();
865 return std::string();
869 void GCMClientImpl::SetRecording(bool recording) {
870 recorder_.SetRecording(recording);
873 void GCMClientImpl::ClearActivityLogs() {
874 recorder_.Clear();
877 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
878 GCMClient::GCMStatistics stats;
879 stats.gcm_client_created = true;
880 stats.is_recording = recorder_.is_recording();
881 stats.gcm_client_state = GetStateString();
882 stats.connection_client_created = mcs_client_.get() != NULL;
883 if (connection_factory_.get())
884 stats.connection_state = connection_factory_->GetConnectionStateString();
885 if (mcs_client_.get()) {
886 stats.send_queue_size = mcs_client_->GetSendQueueSize();
887 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
889 if (device_checkin_info_.android_id > 0)
890 stats.android_id = device_checkin_info_.android_id;
891 recorder_.CollectActivities(&stats.recorded_activities);
893 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
894 it != registrations_.end(); ++it) {
895 stats.registered_app_ids.push_back(it->first);
897 return stats;
900 void GCMClientImpl::OnActivityRecorded() {
901 delegate_->OnActivityRecorded();
904 void GCMClientImpl::OnConnected(const GURL& current_server,
905 const net::IPEndPoint& ip_endpoint) {
906 // TODO(gcm): expose current server in debug page.
907 delegate_->OnActivityRecorded();
908 delegate_->OnConnected(ip_endpoint);
911 void GCMClientImpl::OnDisconnected() {
912 delegate_->OnActivityRecorded();
913 delegate_->OnDisconnected();
916 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
917 switch (message.tag()) {
918 case kLoginResponseTag:
919 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
920 return;
921 case kDataMessageStanzaTag:
922 DVLOG(1) << "A downstream message received. Processing...";
923 HandleIncomingMessage(message);
924 return;
925 default:
926 NOTREACHED() << "Message with unexpected tag received by GCMClient";
927 return;
931 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
932 const std::string& app_id,
933 const std::string& message_id,
934 MCSClient::MessageSendStatus status) {
935 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
936 DCHECK(delegate_);
938 // TTL_EXCEEDED is singled out here, because it can happen long time after the
939 // message was sent. That is why it comes as |OnMessageSendError| event rather
940 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
941 // All other errors will be raised immediately, through asynchronous callback.
942 // It is expected that TTL_EXCEEDED will be issued for a message that was
943 // previously issued |OnSendFinished| with status SUCCESS.
944 // TODO(jianli): Consider adding UMA for this status.
945 if (status == MCSClient::TTL_EXCEEDED) {
946 SendErrorDetails send_error_details;
947 send_error_details.message_id = message_id;
948 send_error_details.result = GCMClient::TTL_EXCEEDED;
949 delegate_->OnMessageSendError(app_id, send_error_details);
950 } else if (status == MCSClient::SENT) {
951 delegate_->OnSendAcknowledged(app_id, message_id);
952 } else {
953 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
957 void GCMClientImpl::OnMCSError() {
958 // TODO(fgorski): For now it replaces the initialization method. Long term it
959 // should have an error or status passed in.
962 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
963 DCHECK(delegate_);
965 const mcs_proto::DataMessageStanza& data_message_stanza =
966 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
967 message.GetProtobuf());
968 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
970 // Copying all the data from the stanza to a MessageData object. When present,
971 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
972 // later.
973 MessageData message_data;
974 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
975 std::string key = data_message_stanza.app_data(i).key();
976 message_data[key] = data_message_stanza.app_data(i).value();
979 MessageType message_type = DATA_MESSAGE;
980 MessageData::iterator iter = message_data.find(kMessageTypeKey);
981 if (iter != message_data.end()) {
982 message_type = DecodeMessageType(iter->second);
983 message_data.erase(iter);
986 switch (message_type) {
987 case DATA_MESSAGE:
988 HandleIncomingDataMessage(data_message_stanza, message_data);
989 break;
990 case DELETED_MESSAGES:
991 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
992 data_message_stanza.from(),
993 data_message_stanza.ByteSize(),
994 true,
995 GCMStatsRecorder::DELETED_MESSAGES);
996 delegate_->OnMessagesDeleted(data_message_stanza.category());
997 break;
998 case SEND_ERROR:
999 HandleIncomingSendError(data_message_stanza, message_data);
1000 break;
1001 case UNKNOWN:
1002 default: // Treat default the same as UNKNOWN.
1003 DVLOG(1) << "Unknown message_type received. Message ignored. "
1004 << "App ID: " << data_message_stanza.category() << ".";
1005 break;
1009 void GCMClientImpl::HandleIncomingDataMessage(
1010 const mcs_proto::DataMessageStanza& data_message_stanza,
1011 MessageData& message_data) {
1012 std::string app_id = data_message_stanza.category();
1014 // Drop the message when the app is not registered for the sender of the
1015 // message.
1016 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
1017 bool not_registered =
1018 iter == registrations_.end() ||
1019 std::find(iter->second->sender_ids.begin(),
1020 iter->second->sender_ids.end(),
1021 data_message_stanza.from()) == iter->second->sender_ids.end();
1022 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
1023 data_message_stanza.ByteSize(), !not_registered,
1024 GCMStatsRecorder::DATA_MESSAGE);
1025 if (not_registered) {
1026 return;
1029 IncomingMessage incoming_message;
1030 incoming_message.sender_id = data_message_stanza.from();
1031 if (data_message_stanza.has_token())
1032 incoming_message.collapse_key = data_message_stanza.token();
1033 incoming_message.data = message_data;
1034 delegate_->OnMessageReceived(app_id, incoming_message);
1037 void GCMClientImpl::HandleIncomingSendError(
1038 const mcs_proto::DataMessageStanza& data_message_stanza,
1039 MessageData& message_data) {
1040 SendErrorDetails send_error_details;
1041 send_error_details.additional_data = message_data;
1042 send_error_details.result = SERVER_ERROR;
1044 MessageData::iterator iter =
1045 send_error_details.additional_data.find(kSendErrorMessageIdKey);
1046 if (iter != send_error_details.additional_data.end()) {
1047 send_error_details.message_id = iter->second;
1048 send_error_details.additional_data.erase(iter);
1051 recorder_.RecordIncomingSendError(
1052 data_message_stanza.category(),
1053 data_message_stanza.to(),
1054 data_message_stanza.id());
1055 delegate_->OnMessageSendError(data_message_stanza.category(),
1056 send_error_details);
1059 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1060 if (registrations_.empty())
1061 return false;
1062 // Note that account mapper is not counted as a standalone app since it is
1063 // automatically started when other app uses GCM.
1064 return registrations_.size() > 1 ||
1065 !registrations_.count(kGCMAccountMapperAppId);
1068 } // namespace gcm