[GCM] Add more UMA to GCM
[chromium-blink-merge.git] / google_apis / gcm / gcm_client_impl.cc
blob00134f780c82ade7aa1cd38a3c7883dca48f24c6
1 // Copyright 2013 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 "google_apis/gcm/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/mcs_message.h"
18 #include "google_apis/gcm/base/mcs_util.h"
19 #include "google_apis/gcm/engine/checkin_request.h"
20 #include "google_apis/gcm/engine/connection_factory_impl.h"
21 #include "google_apis/gcm/engine/gcm_store_impl.h"
22 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
23 #include "google_apis/gcm/protocol/mcs.pb.h"
24 #include "net/http/http_network_session.h"
25 #include "net/url_request/url_request_context.h"
26 #include "url/gurl.h"
28 namespace gcm {
30 namespace {
32 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
33 // retries.
34 // Note: In order to ensure a minimum of 20 seconds between server errors (for
35 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
36 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
37 // themselves.
38 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
39 // Number of initial errors (in sequence) to ignore before applying
40 // exponential back-off rules.
43 // Initial delay for exponential back-off in ms.
44 30 * 1000, // 30 seconds.
46 // Factor by which the waiting time will be multiplied.
49 // Fuzzing percentage. ex: 10% will spread requests randomly
50 // between 90%-100% of the calculated time.
51 0.33, // 33%.
53 // Maximum amount of time we are willing to delay our request in ms.
54 10 * 60 * 1000, // 10 minutes.
56 // Time to keep an entry from being discarded even when it
57 // has no significant state, -1 to never discard.
58 -1,
60 // Don't use initial delay unless the last request was an error.
61 false,
64 // Indicates a message type of the received message.
65 enum MessageType {
66 UNKNOWN, // Undetermined type.
67 DATA_MESSAGE, // Regular data message.
68 DELETED_MESSAGES, // Messages were deleted on the server.
69 SEND_ERROR, // Error sending a message.
72 enum OutgoingMessageTTLCategory {
73 TTL_ZERO,
74 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
75 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
76 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
78 TTL_MORE_THAN_ONE_WEEK,
79 TTL_MAXIMUM,
80 // NOTE: always keep this entry at the end. Add new TTL category only
81 // immediately above this line. Make sure to update the corresponding
82 // histogram enum accordingly.
83 TTL_CATEGORY_COUNT
86 // MCS endpoints. SSL Key pinning is done automatically due to the *.google.com
87 // pinning rule.
88 // Note: modifying the endpoints will affect the ability to compare the
89 // GCM.CurrentEnpoint histogram across versions.
90 const char kMCSEndpointMain[] = "https://mtalk.google.com:5228";
91 const char kMCSEndpointFallback[] = "https://mtalk.google.com:443";
93 const int kMaxRegistrationRetries = 5;
94 const char kMessageTypeDataMessage[] = "gcm";
95 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
96 const char kMessageTypeKey[] = "message_type";
97 const char kMessageTypeSendErrorKey[] = "send_error";
98 const char kSendErrorMessageIdKey[] = "google.message_id";
99 const char kSendMessageFromValue[] = "gcm@chrome.com";
100 const int64 kDefaultUserSerialNumber = 0LL;
102 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
103 switch (status) {
104 case MCSClient::QUEUED:
105 return GCMClient::SUCCESS;
106 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
107 return GCMClient::NETWORK_ERROR;
108 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
109 return GCMClient::NETWORK_ERROR;
110 case MCSClient::MESSAGE_TOO_LARGE:
111 return GCMClient::INVALID_PARAMETER;
112 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
113 return GCMClient::NETWORK_ERROR;
114 case MCSClient::TTL_EXCEEDED:
115 return GCMClient::NETWORK_ERROR;
116 case MCSClient::SENT:
117 default:
118 NOTREACHED();
119 break;
121 return GCMClientImpl::UNKNOWN_ERROR;
124 MessageType DecodeMessageType(const std::string& value) {
125 if (kMessageTypeDeletedMessagesKey == value)
126 return DELETED_MESSAGES;
127 if (kMessageTypeSendErrorKey == value)
128 return SEND_ERROR;
129 if (kMessageTypeDataMessage == value)
130 return DATA_MESSAGE;
131 return UNKNOWN;
134 void RecordOutgoingMessageToUMA(
135 const gcm::GCMClient::OutgoingMessage& message) {
136 OutgoingMessageTTLCategory ttl_category;
137 if (message.time_to_live == 0)
138 ttl_category = TTL_ZERO;
139 else if (message.time_to_live <= 60 )
140 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
141 else if (message.time_to_live <= 60 * 60)
142 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
143 else if (message.time_to_live <= 24 * 60 * 60)
144 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
145 else if (message.time_to_live <= 7 * 24 * 60 * 60)
146 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
147 else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
148 ttl_category = TTL_MORE_THAN_ONE_WEEK;
149 else
150 ttl_category = TTL_MAXIMUM;
152 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
153 ttl_category,
154 TTL_CATEGORY_COUNT);
157 } // namespace
159 GCMInternalsBuilder::GCMInternalsBuilder() {}
160 GCMInternalsBuilder::~GCMInternalsBuilder() {}
162 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
163 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
166 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
167 const std::string& version,
168 base::Clock* clock,
169 ConnectionFactory* connection_factory,
170 GCMStore* gcm_store,
171 GCMStatsRecorder* recorder) {
172 return make_scoped_ptr<MCSClient>(
173 new MCSClient(version,
174 clock,
175 connection_factory,
176 gcm_store,
177 recorder));
180 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
181 const std::vector<GURL>& endpoints,
182 const net::BackoffEntry::Policy& backoff_policy,
183 scoped_refptr<net::HttpNetworkSession> network_session,
184 net::NetLog* net_log,
185 GCMStatsRecorder* recorder) {
186 return make_scoped_ptr<ConnectionFactory>(
187 new ConnectionFactoryImpl(endpoints,
188 backoff_policy,
189 network_session,
190 net_log,
191 recorder));
194 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
195 : internals_builder_(internals_builder.Pass()),
196 state_(UNINITIALIZED),
197 clock_(internals_builder_->BuildClock()),
198 url_request_context_getter_(NULL),
199 pending_registration_requests_deleter_(&pending_registration_requests_),
200 pending_unregistration_requests_deleter_(
201 &pending_unregistration_requests_),
202 periodic_checkin_ptr_factory_(this),
203 weak_ptr_factory_(this) {
206 GCMClientImpl::~GCMClientImpl() {
209 void GCMClientImpl::Initialize(
210 const checkin_proto::ChromeBuildProto& chrome_build_proto,
211 const base::FilePath& path,
212 const std::vector<std::string>& account_ids,
213 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
214 const scoped_refptr<net::URLRequestContextGetter>&
215 url_request_context_getter,
216 Delegate* delegate) {
217 DCHECK_EQ(UNINITIALIZED, state_);
218 DCHECK(url_request_context_getter);
219 DCHECK(delegate);
221 url_request_context_getter_ = url_request_context_getter;
222 const net::HttpNetworkSession::Params* network_session_params =
223 url_request_context_getter_->GetURLRequestContext()->
224 GetNetworkSessionParams();
225 DCHECK(network_session_params);
226 network_session_ = new net::HttpNetworkSession(*network_session_params);
228 chrome_build_proto_.CopyFrom(chrome_build_proto);
229 account_ids_ = account_ids;
231 gcm_store_.reset(new GCMStoreImpl(path, blocking_task_runner));
233 delegate_ = delegate;
235 state_ = INITIALIZED;
238 void GCMClientImpl::Load() {
239 DCHECK_EQ(INITIALIZED, state_);
241 // Once the loading is completed, the check-in will be initiated.
242 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
243 weak_ptr_factory_.GetWeakPtr()));
244 state_ = LOADING;
247 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
248 DCHECK_EQ(LOADING, state_);
250 if (!result->success) {
251 ResetState();
252 return;
255 registrations_ = result->registrations;
256 device_checkin_info_.android_id = result->device_android_id;
257 device_checkin_info_.secret = result->device_security_token;
258 last_checkin_time_ = result->last_checkin_time;
259 gservices_settings_.UpdateFromLoadResult(*result);
260 InitializeMCSClient(result.Pass());
262 if (device_checkin_info_.IsValid()) {
263 SchedulePeriodicCheckin();
264 OnReady();
265 return;
268 state_ = INITIAL_DEVICE_CHECKIN;
269 device_checkin_info_.Reset();
270 StartCheckin();
273 void GCMClientImpl::InitializeMCSClient(
274 scoped_ptr<GCMStore::LoadResult> result) {
275 std::vector<GURL> endpoints;
276 endpoints.push_back(GURL(kMCSEndpointMain));
277 endpoints.push_back(GURL(kMCSEndpointFallback));
278 connection_factory_ = internals_builder_->BuildConnectionFactory(
279 endpoints,
280 kDefaultBackoffPolicy,
281 network_session_,
282 net_log_.net_log(),
283 &recorder_);
284 mcs_client_ = internals_builder_->BuildMCSClient(
285 chrome_build_proto_.chrome_version(),
286 clock_.get(),
287 connection_factory_.get(),
288 gcm_store_.get(),
289 &recorder_).Pass();
291 mcs_client_->Initialize(
292 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
293 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
294 weak_ptr_factory_.GetWeakPtr()),
295 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
296 weak_ptr_factory_.GetWeakPtr()),
297 result.Pass());
300 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
301 const CheckinInfo& checkin_info) {
302 DCHECK(!device_checkin_info_.IsValid());
304 device_checkin_info_.android_id = checkin_info.android_id;
305 device_checkin_info_.secret = checkin_info.secret;
306 gcm_store_->SetDeviceCredentials(
307 checkin_info.android_id, checkin_info.secret,
308 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
309 weak_ptr_factory_.GetWeakPtr()));
311 OnReady();
314 void GCMClientImpl::OnReady() {
315 state_ = READY;
316 StartMCSLogin();
318 delegate_->OnGCMReady();
321 void GCMClientImpl::StartMCSLogin() {
322 DCHECK_EQ(READY, state_);
323 DCHECK(device_checkin_info_.IsValid());
324 mcs_client_->Login(device_checkin_info_.android_id,
325 device_checkin_info_.secret);
328 void GCMClientImpl::ResetState() {
329 state_ = UNINITIALIZED;
330 // TODO(fgorski): reset all of the necessart objects and start over.
333 void GCMClientImpl::StartCheckin() {
334 // Make sure no checkin is in progress.
335 if (checkin_request_.get())
336 return;
338 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
339 device_checkin_info_.secret,
340 gservices_settings_.digest(),
341 account_ids_,
342 chrome_build_proto_);
343 checkin_request_.reset(
344 new CheckinRequest(gservices_settings_.checkin_url(),
345 request_info,
346 kDefaultBackoffPolicy,
347 base::Bind(&GCMClientImpl::OnCheckinCompleted,
348 weak_ptr_factory_.GetWeakPtr()),
349 url_request_context_getter_,
350 &recorder_));
351 checkin_request_->Start();
354 void GCMClientImpl::OnCheckinCompleted(
355 const checkin_proto::AndroidCheckinResponse& checkin_response) {
356 checkin_request_.reset();
358 if (!checkin_response.has_android_id() ||
359 !checkin_response.has_security_token()) {
360 // TODO(fgorski): I don't think a retry here will help, we should probably
361 // start over. By checking in with (0, 0).
362 return;
365 CheckinInfo checkin_info;
366 checkin_info.android_id = checkin_response.android_id();
367 checkin_info.secret = checkin_response.security_token();
369 if (state_ == INITIAL_DEVICE_CHECKIN) {
370 OnFirstTimeDeviceCheckinCompleted(checkin_info);
371 } else {
372 // checkin_info is not expected to change after a periodic checkin as it
373 // would invalidate the registratoin IDs.
374 DCHECK_EQ(READY, state_);
375 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
376 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
379 if (device_checkin_info_.IsValid()) {
380 // First update G-services settings, as something might have changed.
381 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
382 gcm_store_->SetGServicesSettings(
383 gservices_settings_.GetSettingsMap(),
384 gservices_settings_.digest(),
385 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
386 weak_ptr_factory_.GetWeakPtr()));
389 last_checkin_time_ = clock_->Now();
390 gcm_store_->SetLastCheckinTime(
391 last_checkin_time_,
392 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
393 weak_ptr_factory_.GetWeakPtr()));
394 SchedulePeriodicCheckin();
398 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
399 DCHECK(success);
402 void GCMClientImpl::SchedulePeriodicCheckin() {
403 // Make sure no checkin is in progress.
404 if (checkin_request_.get())
405 return;
407 // There should be only one periodic checkin pending at a time. Removing
408 // pending periodic checkin to schedule a new one.
409 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
411 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
412 if (time_to_next_checkin < base::TimeDelta())
413 time_to_next_checkin = base::TimeDelta();
415 base::MessageLoop::current()->PostDelayedTask(
416 FROM_HERE,
417 base::Bind(&GCMClientImpl::StartCheckin,
418 periodic_checkin_ptr_factory_.GetWeakPtr()),
419 time_to_next_checkin);
422 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
423 return last_checkin_time_ + gservices_settings_.checkin_interval() -
424 clock_->Now();
427 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
428 // TODO(fgorski): This is one of the signals that store needs a rebuild.
429 DCHECK(success);
432 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
433 // TODO(fgorski): This is one of the signals that store needs a rebuild.
434 DCHECK(success);
437 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
438 // TODO(fgorski): This is one of the signals that store needs a rebuild.
439 DCHECK(success);
442 void GCMClientImpl::Stop() {
443 device_checkin_info_.Reset();
444 connection_factory_.reset();
445 mcs_client_.reset();
446 checkin_request_.reset();
447 pending_registration_requests_.clear();
448 state_ = INITIALIZED;
449 gcm_store_->Close();
452 void GCMClientImpl::CheckOut() {
453 Stop();
454 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
455 weak_ptr_factory_.GetWeakPtr()));
458 void GCMClientImpl::Register(const std::string& app_id,
459 const std::vector<std::string>& sender_ids) {
460 DCHECK_EQ(state_, READY);
462 // If the same sender ids is provided, return the cached registration ID
463 // directly.
464 RegistrationInfoMap::const_iterator registrations_iter =
465 registrations_.find(app_id);
466 if (registrations_iter != registrations_.end() &&
467 registrations_iter->second->sender_ids == sender_ids) {
468 delegate_->OnRegisterFinished(
469 app_id, registrations_iter->second->registration_id, SUCCESS);
470 return;
473 RegistrationRequest::RequestInfo request_info(
474 device_checkin_info_.android_id,
475 device_checkin_info_.secret,
476 app_id,
477 sender_ids);
478 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
480 RegistrationRequest* registration_request =
481 new RegistrationRequest(gservices_settings_.registration_url(),
482 request_info,
483 kDefaultBackoffPolicy,
484 base::Bind(&GCMClientImpl::OnRegisterCompleted,
485 weak_ptr_factory_.GetWeakPtr(),
486 app_id,
487 sender_ids),
488 kMaxRegistrationRetries,
489 url_request_context_getter_,
490 &recorder_);
491 pending_registration_requests_[app_id] = registration_request;
492 registration_request->Start();
495 void GCMClientImpl::OnRegisterCompleted(
496 const std::string& app_id,
497 const std::vector<std::string>& sender_ids,
498 RegistrationRequest::Status status,
499 const std::string& registration_id) {
500 DCHECK(delegate_);
502 Result result;
503 PendingRegistrationRequests::iterator iter =
504 pending_registration_requests_.find(app_id);
505 if (iter == pending_registration_requests_.end())
506 result = UNKNOWN_ERROR;
507 else if (status == RegistrationRequest::INVALID_SENDER)
508 result = INVALID_PARAMETER;
509 else if (registration_id.empty())
510 result = SERVER_ERROR;
511 else
512 result = SUCCESS;
514 if (result == SUCCESS) {
515 // Cache it.
516 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
517 registration->sender_ids = sender_ids;
518 registration->registration_id = registration_id;
519 registrations_[app_id] = registration;
521 // Save it in the persistent store.
522 gcm_store_->AddRegistration(
523 app_id,
524 registration,
525 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
526 weak_ptr_factory_.GetWeakPtr()));
529 delegate_->OnRegisterFinished(
530 app_id, result == SUCCESS ? registration_id : std::string(), result);
532 if (iter != pending_registration_requests_.end()) {
533 delete iter->second;
534 pending_registration_requests_.erase(iter);
538 void GCMClientImpl::Unregister(const std::string& app_id) {
539 DCHECK_EQ(state_, READY);
540 if (pending_unregistration_requests_.count(app_id) == 1)
541 return;
543 // Remove from the cache and persistent store.
544 registrations_.erase(app_id);
545 gcm_store_->RemoveRegistration(
546 app_id,
547 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
548 weak_ptr_factory_.GetWeakPtr()));
550 UnregistrationRequest::RequestInfo request_info(
551 device_checkin_info_.android_id,
552 device_checkin_info_.secret,
553 app_id);
555 UnregistrationRequest* unregistration_request =
556 new UnregistrationRequest(
557 gservices_settings_.registration_url(),
558 request_info,
559 kDefaultBackoffPolicy,
560 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
561 weak_ptr_factory_.GetWeakPtr(),
562 app_id),
563 url_request_context_getter_,
564 &recorder_);
565 pending_unregistration_requests_[app_id] = unregistration_request;
566 unregistration_request->Start();
569 void GCMClientImpl::OnUnregisterCompleted(
570 const std::string& app_id,
571 UnregistrationRequest::Status status) {
572 DVLOG(1) << "Unregister completed for app: " << app_id
573 << " with " << (status ? "success." : "failure.");
574 delegate_->OnUnregisterFinished(
575 app_id,
576 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
578 PendingUnregistrationRequests::iterator iter =
579 pending_unregistration_requests_.find(app_id);
580 if (iter == pending_unregistration_requests_.end())
581 return;
583 delete iter->second;
584 pending_unregistration_requests_.erase(iter);
587 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
588 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
589 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
592 void GCMClientImpl::Send(const std::string& app_id,
593 const std::string& receiver_id,
594 const OutgoingMessage& message) {
595 DCHECK_EQ(state_, READY);
597 RecordOutgoingMessageToUMA(message);
599 mcs_proto::DataMessageStanza stanza;
600 stanza.set_ttl(message.time_to_live);
601 stanza.set_sent(clock_->Now().ToInternalValue() /
602 base::Time::kMicrosecondsPerSecond);
603 stanza.set_id(message.id);
604 stanza.set_from(kSendMessageFromValue);
605 stanza.set_to(receiver_id);
606 stanza.set_category(app_id);
608 for (MessageData::const_iterator iter = message.data.begin();
609 iter != message.data.end();
610 ++iter) {
611 mcs_proto::AppData* app_data = stanza.add_app_data();
612 app_data->set_key(iter->first);
613 app_data->set_value(iter->second);
616 MCSMessage mcs_message(stanza);
617 DVLOG(1) << "MCS message size: " << mcs_message.size();
618 mcs_client_->SendMessage(mcs_message);
621 std::string GCMClientImpl::GetStateString() const {
622 switch(state_) {
623 case GCMClientImpl::INITIALIZED:
624 return "INITIALIZED";
625 case GCMClientImpl::UNINITIALIZED:
626 return "UNINITIALIZED";
627 case GCMClientImpl::LOADING:
628 return "LOADING";
629 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
630 return "INITIAL_DEVICE_CHECKIN";
631 case GCMClientImpl::READY:
632 return "READY";
633 default:
634 NOTREACHED();
635 return std::string();
639 void GCMClientImpl::SetRecording(bool recording) {
640 recorder_.SetRecording(recording);
643 void GCMClientImpl::ClearActivityLogs() {
644 recorder_.Clear();
647 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
648 GCMClient::GCMStatistics stats;
649 stats.gcm_client_created = true;
650 stats.is_recording = recorder_.is_recording();
651 stats.gcm_client_state = GetStateString();
652 stats.connection_client_created = mcs_client_.get() != NULL;
653 if (mcs_client_.get()) {
654 stats.connection_state = mcs_client_->GetStateString();
655 stats.send_queue_size = mcs_client_->GetSendQueueSize();
656 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
658 if (device_checkin_info_.android_id > 0)
659 stats.android_id = device_checkin_info_.android_id;
660 recorder_.CollectActivities(&stats.recorded_activities);
662 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
663 it != registrations_.end(); ++it) {
664 stats.registered_app_ids.push_back(it->first);
666 return stats;
669 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
670 switch (message.tag()) {
671 case kLoginResponseTag:
672 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
673 return;
674 case kDataMessageStanzaTag:
675 DVLOG(1) << "A downstream message received. Processing...";
676 HandleIncomingMessage(message);
677 return;
678 default:
679 NOTREACHED() << "Message with unexpected tag received by GCMClient";
680 return;
684 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
685 const std::string& app_id,
686 const std::string& message_id,
687 MCSClient::MessageSendStatus status) {
688 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
689 DCHECK(delegate_);
691 // TTL_EXCEEDED is singled out here, because it can happen long time after the
692 // message was sent. That is why it comes as |OnMessageSendError| event rather
693 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
694 // All other errors will be raised immediately, through asynchronous callback.
695 // It is expected that TTL_EXCEEDED will be issued for a message that was
696 // previously issued |OnSendFinished| with status SUCCESS.
697 // For now, we do not report that the message has been sent and acked
698 // successfully.
699 // TODO(jianli): Consider adding UMA for this status.
700 if (status == MCSClient::TTL_EXCEEDED) {
701 SendErrorDetails send_error_details;
702 send_error_details.message_id = message_id;
703 send_error_details.result = GCMClient::TTL_EXCEEDED;
704 delegate_->OnMessageSendError(app_id, send_error_details);
705 } else if (status != MCSClient::SENT) {
706 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
710 void GCMClientImpl::OnMCSError() {
711 // TODO(fgorski): For now it replaces the initialization method. Long term it
712 // should have an error or status passed in.
715 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
716 DCHECK(delegate_);
718 const mcs_proto::DataMessageStanza& data_message_stanza =
719 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
720 message.GetProtobuf());
721 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
723 // Copying all the data from the stanza to a MessageData object. When present,
724 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
725 // later.
726 MessageData message_data;
727 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
728 std::string key = data_message_stanza.app_data(i).key();
729 message_data[key] = data_message_stanza.app_data(i).value();
732 MessageType message_type = DATA_MESSAGE;
733 MessageData::iterator iter = message_data.find(kMessageTypeKey);
734 if (iter != message_data.end()) {
735 message_type = DecodeMessageType(iter->second);
736 message_data.erase(iter);
739 switch (message_type) {
740 case DATA_MESSAGE:
741 HandleIncomingDataMessage(data_message_stanza, message_data);
742 break;
743 case DELETED_MESSAGES:
744 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
745 data_message_stanza.from(),
746 data_message_stanza.ByteSize(),
747 true,
748 GCMStatsRecorder::DELETED_MESSAGES);
749 delegate_->OnMessagesDeleted(data_message_stanza.category());
750 break;
751 case SEND_ERROR:
752 HandleIncomingSendError(data_message_stanza, message_data);
753 break;
754 case UNKNOWN:
755 default: // Treat default the same as UNKNOWN.
756 DVLOG(1) << "Unknown message_type received. Message ignored. "
757 << "App ID: " << data_message_stanza.category() << ".";
758 break;
762 void GCMClientImpl::HandleIncomingDataMessage(
763 const mcs_proto::DataMessageStanza& data_message_stanza,
764 MessageData& message_data) {
765 std::string app_id = data_message_stanza.category();
767 // Drop the message when the app is not registered for the sender of the
768 // message.
769 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
770 bool not_registered =
771 iter == registrations_.end() ||
772 std::find(iter->second->sender_ids.begin(),
773 iter->second->sender_ids.end(),
774 data_message_stanza.from()) == iter->second->sender_ids.end();
775 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
776 data_message_stanza.ByteSize(), !not_registered,
777 GCMStatsRecorder::DATA_MESSAGE);
778 if (not_registered) {
779 return;
782 IncomingMessage incoming_message;
783 incoming_message.sender_id = data_message_stanza.from();
784 if (data_message_stanza.has_token())
785 incoming_message.collapse_key = data_message_stanza.token();
786 incoming_message.data = message_data;
787 delegate_->OnMessageReceived(app_id, incoming_message);
790 void GCMClientImpl::HandleIncomingSendError(
791 const mcs_proto::DataMessageStanza& data_message_stanza,
792 MessageData& message_data) {
793 SendErrorDetails send_error_details;
794 send_error_details.additional_data = message_data;
795 send_error_details.result = SERVER_ERROR;
797 MessageData::iterator iter =
798 send_error_details.additional_data.find(kSendErrorMessageIdKey);
799 if (iter != send_error_details.additional_data.end()) {
800 send_error_details.message_id = iter->second;
801 send_error_details.additional_data.erase(iter);
804 recorder_.RecordIncomingSendError(
805 data_message_stanza.category(),
806 data_message_stanza.to(),
807 data_message_stanza.id());
808 delegate_->OnMessageSendError(data_message_stanza.category(),
809 send_error_details);
812 } // namespace gcm