Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blob3b4e96426a247b757d7799a1c3c4e8e2b60fe568
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.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/default_clock.h"
17 #include "google_apis/gcm/base/encryptor.h"
18 #include "google_apis/gcm/base/mcs_message.h"
19 #include "google_apis/gcm/base/mcs_util.h"
20 #include "google_apis/gcm/engine/checkin_request.h"
21 #include "google_apis/gcm/engine/connection_factory_impl.h"
22 #include "google_apis/gcm/engine/gcm_store_impl.h"
23 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
24 #include "google_apis/gcm/protocol/checkin.pb.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "net/http/http_network_session.h"
27 #include "net/http/http_transaction_factory.h"
28 #include "net/url_request/url_request_context.h"
29 #include "url/gurl.h"
31 namespace gcm {
33 namespace {
35 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
36 // retries.
37 // Note: In order to ensure a minimum of 20 seconds between server errors (for
38 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
39 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
40 // themselves.
41 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
42 // Number of initial errors (in sequence) to ignore before applying
43 // exponential back-off rules.
46 // Initial delay for exponential back-off in ms.
47 30 * 1000, // 30 seconds.
49 // Factor by which the waiting time will be multiplied.
52 // Fuzzing percentage. ex: 10% will spread requests randomly
53 // between 90%-100% of the calculated time.
54 0.33, // 33%.
56 // Maximum amount of time we are willing to delay our request in ms.
57 10 * 60 * 1000, // 10 minutes.
59 // Time to keep an entry from being discarded even when it
60 // has no significant state, -1 to never discard.
61 -1,
63 // Don't use initial delay unless the last request was an error.
64 false,
67 // Indicates a message type of the received message.
68 enum MessageType {
69 UNKNOWN, // Undetermined type.
70 DATA_MESSAGE, // Regular data message.
71 DELETED_MESSAGES, // Messages were deleted on the server.
72 SEND_ERROR, // Error sending a message.
75 enum OutgoingMessageTTLCategory {
76 TTL_ZERO,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
78 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
79 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
80 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
81 TTL_MORE_THAN_ONE_WEEK,
82 TTL_MAXIMUM,
83 // NOTE: always keep this entry at the end. Add new TTL category only
84 // immediately above this line. Make sure to update the corresponding
85 // histogram enum accordingly.
86 TTL_CATEGORY_COUNT
89 const int kMaxRegistrationRetries = 5;
90 const char kMessageTypeDataMessage[] = "gcm";
91 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
92 const char kMessageTypeKey[] = "message_type";
93 const char kMessageTypeSendErrorKey[] = "send_error";
94 const char kSendErrorMessageIdKey[] = "google.message_id";
95 const char kSendMessageFromValue[] = "gcm@chrome.com";
96 const int64 kDefaultUserSerialNumber = 0LL;
98 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
99 switch (status) {
100 case MCSClient::QUEUED:
101 return GCMClient::SUCCESS;
102 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
103 return GCMClient::NETWORK_ERROR;
104 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
105 return GCMClient::NETWORK_ERROR;
106 case MCSClient::MESSAGE_TOO_LARGE:
107 return GCMClient::INVALID_PARAMETER;
108 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
109 return GCMClient::NETWORK_ERROR;
110 case MCSClient::TTL_EXCEEDED:
111 return GCMClient::NETWORK_ERROR;
112 case MCSClient::SENT:
113 default:
114 NOTREACHED();
115 break;
117 return GCMClientImpl::UNKNOWN_ERROR;
120 void ToCheckinProtoVersion(
121 const GCMClient::ChromeBuildInfo& chrome_build_info,
122 checkin_proto::ChromeBuildProto* android_build_info) {
123 checkin_proto::ChromeBuildProto_Platform platform;
124 switch (chrome_build_info.platform) {
125 case GCMClient::PLATFORM_WIN:
126 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
127 break;
128 case GCMClient::PLATFORM_MAC:
129 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
130 break;
131 case GCMClient::PLATFORM_LINUX:
132 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
133 break;
134 case GCMClient::PLATFORM_IOS:
135 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
136 break;
137 case GCMClient::PLATFORM_ANDROID:
138 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
139 break;
140 case GCMClient::PLATFORM_CROS:
141 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
142 break;
143 case GCMClient::PLATFORM_UNKNOWN:
144 // For unknown platform, return as LINUX.
145 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
146 break;
147 default:
148 NOTREACHED();
149 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
150 break;
152 android_build_info->set_platform(platform);
154 checkin_proto::ChromeBuildProto_Channel channel;
155 switch (chrome_build_info.channel) {
156 case GCMClient::CHANNEL_STABLE:
157 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
158 break;
159 case GCMClient::CHANNEL_BETA:
160 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
161 break;
162 case GCMClient::CHANNEL_DEV:
163 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
164 break;
165 case GCMClient::CHANNEL_CANARY:
166 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
167 break;
168 case GCMClient::CHANNEL_UNKNOWN:
169 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
170 break;
171 default:
172 NOTREACHED();
173 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
174 break;
176 android_build_info->set_channel(channel);
178 android_build_info->set_chrome_version(chrome_build_info.version);
181 MessageType DecodeMessageType(const std::string& value) {
182 if (kMessageTypeDeletedMessagesKey == value)
183 return DELETED_MESSAGES;
184 if (kMessageTypeSendErrorKey == value)
185 return SEND_ERROR;
186 if (kMessageTypeDataMessage == value)
187 return DATA_MESSAGE;
188 return UNKNOWN;
191 void RecordOutgoingMessageToUMA(
192 const gcm::GCMClient::OutgoingMessage& message) {
193 OutgoingMessageTTLCategory ttl_category;
194 if (message.time_to_live == 0)
195 ttl_category = TTL_ZERO;
196 else if (message.time_to_live <= 60 )
197 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
198 else if (message.time_to_live <= 60 * 60)
199 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
200 else if (message.time_to_live <= 24 * 60 * 60)
201 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
202 else if (message.time_to_live <= 7 * 24 * 60 * 60)
203 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
204 else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
205 ttl_category = TTL_MORE_THAN_ONE_WEEK;
206 else
207 ttl_category = TTL_MAXIMUM;
209 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
210 ttl_category,
211 TTL_CATEGORY_COUNT);
214 } // namespace
216 GCMInternalsBuilder::GCMInternalsBuilder() {}
217 GCMInternalsBuilder::~GCMInternalsBuilder() {}
219 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
220 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
223 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
224 const std::string& version,
225 base::Clock* clock,
226 ConnectionFactory* connection_factory,
227 GCMStore* gcm_store,
228 GCMStatsRecorder* recorder) {
229 return make_scoped_ptr<MCSClient>(
230 new MCSClient(version,
231 clock,
232 connection_factory,
233 gcm_store,
234 recorder));
237 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
238 const std::vector<GURL>& endpoints,
239 const net::BackoffEntry::Policy& backoff_policy,
240 const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
241 const scoped_refptr<net::HttpNetworkSession>& http_network_session,
242 net::NetLog* net_log,
243 GCMStatsRecorder* recorder) {
244 return make_scoped_ptr<ConnectionFactory>(
245 new ConnectionFactoryImpl(endpoints,
246 backoff_policy,
247 gcm_network_session,
248 http_network_session,
249 net_log,
250 recorder));
253 GCMClientImpl::CheckinInfo::CheckinInfo()
254 : android_id(0), secret(0), accounts_set(false) {
257 GCMClientImpl::CheckinInfo::~CheckinInfo() {
260 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
261 last_checkin_accounts.clear();
262 for (std::map<std::string, std::string>::iterator iter =
263 account_tokens.begin();
264 iter != account_tokens.end();
265 ++iter) {
266 last_checkin_accounts.insert(iter->first);
270 void GCMClientImpl::CheckinInfo::Reset() {
271 android_id = 0;
272 secret = 0;
273 accounts_set = false;
274 account_tokens.clear();
275 last_checkin_accounts.clear();
278 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
279 : internals_builder_(internals_builder.Pass()),
280 state_(UNINITIALIZED),
281 delegate_(NULL),
282 clock_(internals_builder_->BuildClock()),
283 url_request_context_getter_(NULL),
284 pending_registration_requests_deleter_(&pending_registration_requests_),
285 pending_unregistration_requests_deleter_(
286 &pending_unregistration_requests_),
287 periodic_checkin_ptr_factory_(this),
288 weak_ptr_factory_(this) {
291 GCMClientImpl::~GCMClientImpl() {
294 void GCMClientImpl::Initialize(
295 const ChromeBuildInfo& chrome_build_info,
296 const base::FilePath& path,
297 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
298 const scoped_refptr<net::URLRequestContextGetter>&
299 url_request_context_getter,
300 scoped_ptr<Encryptor> encryptor,
301 GCMClient::Delegate* delegate) {
302 DCHECK_EQ(UNINITIALIZED, state_);
303 DCHECK(url_request_context_getter.get());
304 DCHECK(delegate);
306 url_request_context_getter_ = url_request_context_getter;
307 const net::HttpNetworkSession::Params* network_session_params =
308 url_request_context_getter_->GetURLRequestContext()->
309 GetNetworkSessionParams();
310 DCHECK(network_session_params);
311 network_session_ = new net::HttpNetworkSession(*network_session_params);
313 chrome_build_info_ = chrome_build_info;
315 gcm_store_.reset(
316 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
318 delegate_ = delegate;
320 recorder_.SetDelegate(this);
322 state_ = INITIALIZED;
325 void GCMClientImpl::Start() {
326 DCHECK_EQ(INITIALIZED, state_);
328 // Once the loading is completed, the check-in will be initiated.
329 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
330 weak_ptr_factory_.GetWeakPtr()));
331 state_ = LOADING;
334 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
335 DCHECK_EQ(LOADING, state_);
337 if (!result->success) {
338 ResetState();
339 return;
342 registrations_ = result->registrations;
343 device_checkin_info_.android_id = result->device_android_id;
344 device_checkin_info_.secret = result->device_security_token;
345 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
346 // A case where there were previously no accounts reported with checkin is
347 // considered to be the same as when the list of accounts is empty. It enables
348 // scheduling a periodic checkin for devices with no signed in users
349 // immediately after restart, while keeping |accounts_set == false| delays the
350 // checkin until the list of accounts is set explicitly.
351 if (result->last_checkin_accounts.size() == 0)
352 device_checkin_info_.accounts_set = true;
353 last_checkin_time_ = result->last_checkin_time;
354 gservices_settings_.UpdateFromLoadResult(*result);
355 InitializeMCSClient(result.Pass());
357 if (device_checkin_info_.IsValid()) {
358 SchedulePeriodicCheckin();
359 OnReady();
360 return;
363 state_ = INITIAL_DEVICE_CHECKIN;
364 device_checkin_info_.Reset();
365 StartCheckin();
368 void GCMClientImpl::InitializeMCSClient(
369 scoped_ptr<GCMStore::LoadResult> result) {
370 std::vector<GURL> endpoints;
371 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
372 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
373 connection_factory_ = internals_builder_->BuildConnectionFactory(
374 endpoints,
375 kDefaultBackoffPolicy,
376 network_session_,
377 url_request_context_getter_->GetURLRequestContext()
378 ->http_transaction_factory()
379 ->GetSession(),
380 net_log_.net_log(),
381 &recorder_);
382 connection_factory_->SetConnectionListener(this);
383 mcs_client_ = internals_builder_->BuildMCSClient(
384 chrome_build_info_.version,
385 clock_.get(),
386 connection_factory_.get(),
387 gcm_store_.get(),
388 &recorder_).Pass();
390 mcs_client_->Initialize(
391 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
392 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
393 weak_ptr_factory_.GetWeakPtr()),
394 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
395 weak_ptr_factory_.GetWeakPtr()),
396 result.Pass());
399 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
400 const CheckinInfo& checkin_info) {
401 DCHECK(!device_checkin_info_.IsValid());
403 device_checkin_info_.android_id = checkin_info.android_id;
404 device_checkin_info_.secret = checkin_info.secret;
405 // If accounts were not set by now, we can consider them set (to empty list)
406 // to make sure periodic checkins get scheduled after initial checkin.
407 device_checkin_info_.accounts_set = true;
408 gcm_store_->SetDeviceCredentials(
409 checkin_info.android_id, checkin_info.secret,
410 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
411 weak_ptr_factory_.GetWeakPtr()));
413 OnReady();
416 void GCMClientImpl::OnReady() {
417 state_ = READY;
418 StartMCSLogin();
420 delegate_->OnGCMReady();
423 void GCMClientImpl::StartMCSLogin() {
424 DCHECK_EQ(READY, state_);
425 DCHECK(device_checkin_info_.IsValid());
426 mcs_client_->Login(device_checkin_info_.android_id,
427 device_checkin_info_.secret);
430 void GCMClientImpl::ResetState() {
431 state_ = UNINITIALIZED;
432 // TODO(fgorski): reset all of the necessart objects and start over.
435 void GCMClientImpl::SetAccountsForCheckin(
436 const std::map<std::string, std::string>& account_tokens) {
437 bool accounts_set_before = device_checkin_info_.accounts_set;
438 device_checkin_info_.account_tokens = account_tokens;
439 device_checkin_info_.accounts_set = true;
441 DVLOG(1) << "Set account called with: " << account_tokens.size()
442 << " accounts.";
444 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
445 return;
447 bool account_removed = false;
448 for (std::set<std::string>::iterator iter =
449 device_checkin_info_.last_checkin_accounts.begin();
450 iter != device_checkin_info_.last_checkin_accounts.end();
451 ++iter) {
452 if (account_tokens.find(*iter) == account_tokens.end())
453 account_removed = true;
456 // Checkin will be forced when any of the accounts was removed during the
457 // current Chrome session or if there has been an account removed between the
458 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
459 // We only force checkin when user signs out. When there is a new account
460 // signed in, the periodic checkin will take care of adding the association in
461 // reasonable time.
462 if (account_removed) {
463 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
464 checkin_request_.reset();
465 StartCheckin();
466 } else if (!accounts_set_before) {
467 SchedulePeriodicCheckin();
468 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
472 void GCMClientImpl::UpdateAccountMapping(
473 const AccountMapping& account_mapping) {
474 gcm_store_->AddAccountMapping(account_mapping,
475 base::Bind(&GCMClientImpl::DefaultStoreCallback,
476 weak_ptr_factory_.GetWeakPtr()));
479 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
480 gcm_store_->RemoveAccountMapping(
481 account_id,
482 base::Bind(&GCMClientImpl::DefaultStoreCallback,
483 weak_ptr_factory_.GetWeakPtr()));
486 void GCMClientImpl::StartCheckin() {
487 // Make sure no checkin is in progress.
488 if (checkin_request_.get())
489 return;
491 checkin_proto::ChromeBuildProto chrome_build_proto;
492 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
493 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
494 device_checkin_info_.secret,
495 device_checkin_info_.account_tokens,
496 gservices_settings_.digest(),
497 chrome_build_proto);
498 checkin_request_.reset(
499 new CheckinRequest(gservices_settings_.GetCheckinURL(),
500 request_info,
501 kDefaultBackoffPolicy,
502 base::Bind(&GCMClientImpl::OnCheckinCompleted,
503 weak_ptr_factory_.GetWeakPtr()),
504 url_request_context_getter_.get(),
505 &recorder_));
506 // Taking a snapshot of the accounts count here, as there might be an asynch
507 // update of the account tokens while checkin is in progress.
508 device_checkin_info_.SnapshotCheckinAccounts();
509 checkin_request_->Start();
512 void GCMClientImpl::OnCheckinCompleted(
513 const checkin_proto::AndroidCheckinResponse& checkin_response) {
514 checkin_request_.reset();
516 if (!checkin_response.has_android_id() ||
517 !checkin_response.has_security_token()) {
518 // TODO(fgorski): I don't think a retry here will help, we should probably
519 // start over. By checking in with (0, 0).
520 return;
523 CheckinInfo checkin_info;
524 checkin_info.android_id = checkin_response.android_id();
525 checkin_info.secret = checkin_response.security_token();
527 if (state_ == INITIAL_DEVICE_CHECKIN) {
528 OnFirstTimeDeviceCheckinCompleted(checkin_info);
529 } else {
530 // checkin_info is not expected to change after a periodic checkin as it
531 // would invalidate the registratoin IDs.
532 DCHECK_EQ(READY, state_);
533 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
534 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
537 if (device_checkin_info_.IsValid()) {
538 // First update G-services settings, as something might have changed.
539 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
540 gcm_store_->SetGServicesSettings(
541 gservices_settings_.settings_map(),
542 gservices_settings_.digest(),
543 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
544 weak_ptr_factory_.GetWeakPtr()));
547 last_checkin_time_ = clock_->Now();
548 gcm_store_->SetLastCheckinInfo(
549 last_checkin_time_,
550 device_checkin_info_.last_checkin_accounts,
551 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
552 weak_ptr_factory_.GetWeakPtr()));
553 SchedulePeriodicCheckin();
557 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
558 DCHECK(success);
561 void GCMClientImpl::SchedulePeriodicCheckin() {
562 // Make sure no checkin is in progress.
563 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
564 return;
566 // There should be only one periodic checkin pending at a time. Removing
567 // pending periodic checkin to schedule a new one.
568 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
570 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
571 if (time_to_next_checkin < base::TimeDelta())
572 time_to_next_checkin = base::TimeDelta();
574 base::MessageLoop::current()->PostDelayedTask(
575 FROM_HERE,
576 base::Bind(&GCMClientImpl::StartCheckin,
577 periodic_checkin_ptr_factory_.GetWeakPtr()),
578 time_to_next_checkin);
581 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
582 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
583 clock_->Now();
586 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
587 // TODO(fgorski): This is one of the signals that store needs a rebuild.
588 DCHECK(success);
591 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
592 // TODO(fgorski): This is one of the signals that store needs a rebuild.
593 DCHECK(success);
596 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
597 // TODO(fgorski): This is one of the signals that store needs a rebuild.
598 DCHECK(success);
601 void GCMClientImpl::DefaultStoreCallback(bool success) {
602 DCHECK(success);
605 void GCMClientImpl::Stop() {
606 weak_ptr_factory_.InvalidateWeakPtrs();
607 device_checkin_info_.Reset();
608 connection_factory_.reset();
609 delegate_->OnDisconnected();
610 mcs_client_.reset();
611 checkin_request_.reset();
612 pending_registration_requests_.clear();
613 state_ = INITIALIZED;
614 gcm_store_->Close();
617 void GCMClientImpl::CheckOut() {
618 Stop();
619 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
620 weak_ptr_factory_.GetWeakPtr()));
623 void GCMClientImpl::Register(const std::string& app_id,
624 const std::vector<std::string>& sender_ids) {
625 DCHECK_EQ(state_, READY);
627 // If the same sender ids is provided, return the cached registration ID
628 // directly.
629 RegistrationInfoMap::const_iterator registrations_iter =
630 registrations_.find(app_id);
631 if (registrations_iter != registrations_.end() &&
632 registrations_iter->second->sender_ids == sender_ids) {
633 delegate_->OnRegisterFinished(
634 app_id, registrations_iter->second->registration_id, SUCCESS);
635 return;
638 RegistrationRequest::RequestInfo request_info(
639 device_checkin_info_.android_id,
640 device_checkin_info_.secret,
641 app_id,
642 sender_ids);
643 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
645 RegistrationRequest* registration_request =
646 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
647 request_info,
648 kDefaultBackoffPolicy,
649 base::Bind(&GCMClientImpl::OnRegisterCompleted,
650 weak_ptr_factory_.GetWeakPtr(),
651 app_id,
652 sender_ids),
653 kMaxRegistrationRetries,
654 url_request_context_getter_,
655 &recorder_);
656 pending_registration_requests_[app_id] = registration_request;
657 registration_request->Start();
660 void GCMClientImpl::OnRegisterCompleted(
661 const std::string& app_id,
662 const std::vector<std::string>& sender_ids,
663 RegistrationRequest::Status status,
664 const std::string& registration_id) {
665 DCHECK(delegate_);
667 Result result;
668 PendingRegistrationRequests::iterator iter =
669 pending_registration_requests_.find(app_id);
670 if (iter == pending_registration_requests_.end())
671 result = UNKNOWN_ERROR;
672 else if (status == RegistrationRequest::INVALID_SENDER)
673 result = INVALID_PARAMETER;
674 else if (registration_id.empty())
675 result = SERVER_ERROR;
676 else
677 result = SUCCESS;
679 if (result == SUCCESS) {
680 // Cache it.
681 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
682 registration->sender_ids = sender_ids;
683 registration->registration_id = registration_id;
684 registrations_[app_id] = registration;
686 // Save it in the persistent store.
687 gcm_store_->AddRegistration(
688 app_id,
689 registration,
690 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
691 weak_ptr_factory_.GetWeakPtr()));
694 delegate_->OnRegisterFinished(
695 app_id, result == SUCCESS ? registration_id : std::string(), result);
697 if (iter != pending_registration_requests_.end()) {
698 delete iter->second;
699 pending_registration_requests_.erase(iter);
703 void GCMClientImpl::Unregister(const std::string& app_id) {
704 DCHECK_EQ(state_, READY);
705 if (pending_unregistration_requests_.count(app_id) == 1)
706 return;
708 // Remove from the cache and persistent store.
709 registrations_.erase(app_id);
710 gcm_store_->RemoveRegistration(
711 app_id,
712 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
713 weak_ptr_factory_.GetWeakPtr()));
715 UnregistrationRequest::RequestInfo request_info(
716 device_checkin_info_.android_id,
717 device_checkin_info_.secret,
718 app_id);
720 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
721 gservices_settings_.GetRegistrationURL(),
722 request_info,
723 kDefaultBackoffPolicy,
724 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
725 weak_ptr_factory_.GetWeakPtr(),
726 app_id),
727 url_request_context_getter_,
728 &recorder_);
729 pending_unregistration_requests_[app_id] = unregistration_request;
730 unregistration_request->Start();
733 void GCMClientImpl::OnUnregisterCompleted(
734 const std::string& app_id,
735 UnregistrationRequest::Status status) {
736 DVLOG(1) << "Unregister completed for app: " << app_id
737 << " with " << (status ? "success." : "failure.");
738 delegate_->OnUnregisterFinished(
739 app_id,
740 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
742 PendingUnregistrationRequests::iterator iter =
743 pending_unregistration_requests_.find(app_id);
744 if (iter == pending_unregistration_requests_.end())
745 return;
747 delete iter->second;
748 pending_unregistration_requests_.erase(iter);
751 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
752 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
753 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
756 void GCMClientImpl::Send(const std::string& app_id,
757 const std::string& receiver_id,
758 const OutgoingMessage& message) {
759 DCHECK_EQ(state_, READY);
761 RecordOutgoingMessageToUMA(message);
763 mcs_proto::DataMessageStanza stanza;
764 stanza.set_ttl(message.time_to_live);
765 stanza.set_sent(clock_->Now().ToInternalValue() /
766 base::Time::kMicrosecondsPerSecond);
767 stanza.set_id(message.id);
768 stanza.set_from(kSendMessageFromValue);
769 stanza.set_to(receiver_id);
770 stanza.set_category(app_id);
772 for (MessageData::const_iterator iter = message.data.begin();
773 iter != message.data.end();
774 ++iter) {
775 mcs_proto::AppData* app_data = stanza.add_app_data();
776 app_data->set_key(iter->first);
777 app_data->set_value(iter->second);
780 MCSMessage mcs_message(stanza);
781 DVLOG(1) << "MCS message size: " << mcs_message.size();
782 mcs_client_->SendMessage(mcs_message);
785 std::string GCMClientImpl::GetStateString() const {
786 switch(state_) {
787 case GCMClientImpl::INITIALIZED:
788 return "INITIALIZED";
789 case GCMClientImpl::UNINITIALIZED:
790 return "UNINITIALIZED";
791 case GCMClientImpl::LOADING:
792 return "LOADING";
793 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
794 return "INITIAL_DEVICE_CHECKIN";
795 case GCMClientImpl::READY:
796 return "READY";
797 default:
798 NOTREACHED();
799 return std::string();
803 void GCMClientImpl::SetRecording(bool recording) {
804 recorder_.SetRecording(recording);
807 void GCMClientImpl::ClearActivityLogs() {
808 recorder_.Clear();
811 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
812 GCMClient::GCMStatistics stats;
813 stats.gcm_client_created = true;
814 stats.is_recording = recorder_.is_recording();
815 stats.gcm_client_state = GetStateString();
816 stats.connection_client_created = mcs_client_.get() != NULL;
817 if (connection_factory_.get())
818 stats.connection_state = connection_factory_->GetConnectionStateString();
819 if (mcs_client_.get()) {
820 stats.send_queue_size = mcs_client_->GetSendQueueSize();
821 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
823 if (device_checkin_info_.android_id > 0)
824 stats.android_id = device_checkin_info_.android_id;
825 recorder_.CollectActivities(&stats.recorded_activities);
827 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
828 it != registrations_.end(); ++it) {
829 stats.registered_app_ids.push_back(it->first);
831 return stats;
834 void GCMClientImpl::OnActivityRecorded() {
835 delegate_->OnActivityRecorded();
838 void GCMClientImpl::OnConnected(const GURL& current_server,
839 const net::IPEndPoint& ip_endpoint) {
840 // TODO(gcm): expose current server in debug page.
841 delegate_->OnActivityRecorded();
842 delegate_->OnConnected(ip_endpoint);
845 void GCMClientImpl::OnDisconnected() {
846 delegate_->OnActivityRecorded();
847 delegate_->OnDisconnected();
850 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
851 switch (message.tag()) {
852 case kLoginResponseTag:
853 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
854 return;
855 case kDataMessageStanzaTag:
856 DVLOG(1) << "A downstream message received. Processing...";
857 HandleIncomingMessage(message);
858 return;
859 default:
860 NOTREACHED() << "Message with unexpected tag received by GCMClient";
861 return;
865 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
866 const std::string& app_id,
867 const std::string& message_id,
868 MCSClient::MessageSendStatus status) {
869 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
870 DCHECK(delegate_);
872 // TTL_EXCEEDED is singled out here, because it can happen long time after the
873 // message was sent. That is why it comes as |OnMessageSendError| event rather
874 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
875 // All other errors will be raised immediately, through asynchronous callback.
876 // It is expected that TTL_EXCEEDED will be issued for a message that was
877 // previously issued |OnSendFinished| with status SUCCESS.
878 // TODO(jianli): Consider adding UMA for this status.
879 if (status == MCSClient::TTL_EXCEEDED) {
880 SendErrorDetails send_error_details;
881 send_error_details.message_id = message_id;
882 send_error_details.result = GCMClient::TTL_EXCEEDED;
883 delegate_->OnMessageSendError(app_id, send_error_details);
884 } else if (status == MCSClient::SENT) {
885 delegate_->OnSendAcknowledged(app_id, message_id);
886 } else {
887 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
891 void GCMClientImpl::OnMCSError() {
892 // TODO(fgorski): For now it replaces the initialization method. Long term it
893 // should have an error or status passed in.
896 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
897 DCHECK(delegate_);
899 const mcs_proto::DataMessageStanza& data_message_stanza =
900 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
901 message.GetProtobuf());
902 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
904 // Copying all the data from the stanza to a MessageData object. When present,
905 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
906 // later.
907 MessageData message_data;
908 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
909 std::string key = data_message_stanza.app_data(i).key();
910 message_data[key] = data_message_stanza.app_data(i).value();
913 MessageType message_type = DATA_MESSAGE;
914 MessageData::iterator iter = message_data.find(kMessageTypeKey);
915 if (iter != message_data.end()) {
916 message_type = DecodeMessageType(iter->second);
917 message_data.erase(iter);
920 switch (message_type) {
921 case DATA_MESSAGE:
922 HandleIncomingDataMessage(data_message_stanza, message_data);
923 break;
924 case DELETED_MESSAGES:
925 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
926 data_message_stanza.from(),
927 data_message_stanza.ByteSize(),
928 true,
929 GCMStatsRecorder::DELETED_MESSAGES);
930 delegate_->OnMessagesDeleted(data_message_stanza.category());
931 break;
932 case SEND_ERROR:
933 HandleIncomingSendError(data_message_stanza, message_data);
934 break;
935 case UNKNOWN:
936 default: // Treat default the same as UNKNOWN.
937 DVLOG(1) << "Unknown message_type received. Message ignored. "
938 << "App ID: " << data_message_stanza.category() << ".";
939 break;
943 void GCMClientImpl::HandleIncomingDataMessage(
944 const mcs_proto::DataMessageStanza& data_message_stanza,
945 MessageData& message_data) {
946 std::string app_id = data_message_stanza.category();
948 // Drop the message when the app is not registered for the sender of the
949 // message.
950 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
951 bool not_registered =
952 iter == registrations_.end() ||
953 std::find(iter->second->sender_ids.begin(),
954 iter->second->sender_ids.end(),
955 data_message_stanza.from()) == iter->second->sender_ids.end();
956 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
957 data_message_stanza.ByteSize(), !not_registered,
958 GCMStatsRecorder::DATA_MESSAGE);
959 if (not_registered) {
960 return;
963 IncomingMessage incoming_message;
964 incoming_message.sender_id = data_message_stanza.from();
965 if (data_message_stanza.has_token())
966 incoming_message.collapse_key = data_message_stanza.token();
967 incoming_message.data = message_data;
968 delegate_->OnMessageReceived(app_id, incoming_message);
971 void GCMClientImpl::HandleIncomingSendError(
972 const mcs_proto::DataMessageStanza& data_message_stanza,
973 MessageData& message_data) {
974 SendErrorDetails send_error_details;
975 send_error_details.additional_data = message_data;
976 send_error_details.result = SERVER_ERROR;
978 MessageData::iterator iter =
979 send_error_details.additional_data.find(kSendErrorMessageIdKey);
980 if (iter != send_error_details.additional_data.end()) {
981 send_error_details.message_id = iter->second;
982 send_error_details.additional_data.erase(iter);
985 recorder_.RecordIncomingSendError(
986 data_message_stanza.category(),
987 data_message_stanza.to(),
988 data_message_stanza.id());
989 delegate_->OnMessageSendError(data_message_stanza.category(),
990 send_error_details);
993 } // namespace gcm