[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / google_apis / gcm / gcm_client_impl.cc
blob32254545aad54a0dba9882ec3438bb820679ff8f
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/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/mcs.pb.h"
25 #include "net/http/http_network_session.h"
26 #include "net/url_request/url_request_context.h"
27 #include "url/gurl.h"
29 namespace gcm {
31 namespace {
33 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
34 // retries.
35 // Note: In order to ensure a minimum of 20 seconds between server errors (for
36 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
37 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
38 // themselves.
39 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
40 // Number of initial errors (in sequence) to ignore before applying
41 // exponential back-off rules.
44 // Initial delay for exponential back-off in ms.
45 30 * 1000, // 30 seconds.
47 // Factor by which the waiting time will be multiplied.
50 // Fuzzing percentage. ex: 10% will spread requests randomly
51 // between 90%-100% of the calculated time.
52 0.33, // 33%.
54 // Maximum amount of time we are willing to delay our request in ms.
55 10 * 60 * 1000, // 10 minutes.
57 // Time to keep an entry from being discarded even when it
58 // has no significant state, -1 to never discard.
59 -1,
61 // Don't use initial delay unless the last request was an error.
62 false,
65 // Indicates a message type of the received message.
66 enum MessageType {
67 UNKNOWN, // Undetermined type.
68 DATA_MESSAGE, // Regular data message.
69 DELETED_MESSAGES, // Messages were deleted on the server.
70 SEND_ERROR, // Error sending a message.
73 enum OutgoingMessageTTLCategory {
74 TTL_ZERO,
75 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
76 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
78 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
79 TTL_MORE_THAN_ONE_WEEK,
80 TTL_MAXIMUM,
81 // NOTE: always keep this entry at the end. Add new TTL category only
82 // immediately above this line. Make sure to update the corresponding
83 // histogram enum accordingly.
84 TTL_CATEGORY_COUNT
87 const int kMaxRegistrationRetries = 5;
88 const char kMessageTypeDataMessage[] = "gcm";
89 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
90 const char kMessageTypeKey[] = "message_type";
91 const char kMessageTypeSendErrorKey[] = "send_error";
92 const char kSendErrorMessageIdKey[] = "google.message_id";
93 const char kSendMessageFromValue[] = "gcm@chrome.com";
94 const int64 kDefaultUserSerialNumber = 0LL;
96 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
97 switch (status) {
98 case MCSClient::QUEUED:
99 return GCMClient::SUCCESS;
100 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
101 return GCMClient::NETWORK_ERROR;
102 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
103 return GCMClient::NETWORK_ERROR;
104 case MCSClient::MESSAGE_TOO_LARGE:
105 return GCMClient::INVALID_PARAMETER;
106 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
107 return GCMClient::NETWORK_ERROR;
108 case MCSClient::TTL_EXCEEDED:
109 return GCMClient::NETWORK_ERROR;
110 case MCSClient::SENT:
111 default:
112 NOTREACHED();
113 break;
115 return GCMClientImpl::UNKNOWN_ERROR;
118 MessageType DecodeMessageType(const std::string& value) {
119 if (kMessageTypeDeletedMessagesKey == value)
120 return DELETED_MESSAGES;
121 if (kMessageTypeSendErrorKey == value)
122 return SEND_ERROR;
123 if (kMessageTypeDataMessage == value)
124 return DATA_MESSAGE;
125 return UNKNOWN;
128 void RecordOutgoingMessageToUMA(
129 const gcm::GCMClient::OutgoingMessage& message) {
130 OutgoingMessageTTLCategory ttl_category;
131 if (message.time_to_live == 0)
132 ttl_category = TTL_ZERO;
133 else if (message.time_to_live <= 60 )
134 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
135 else if (message.time_to_live <= 60 * 60)
136 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
137 else if (message.time_to_live <= 24 * 60 * 60)
138 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
139 else if (message.time_to_live <= 7 * 24 * 60 * 60)
140 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
141 else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
142 ttl_category = TTL_MORE_THAN_ONE_WEEK;
143 else
144 ttl_category = TTL_MAXIMUM;
146 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
147 ttl_category,
148 TTL_CATEGORY_COUNT);
151 } // namespace
153 GCMInternalsBuilder::GCMInternalsBuilder() {}
154 GCMInternalsBuilder::~GCMInternalsBuilder() {}
156 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
157 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
160 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
161 const std::string& version,
162 base::Clock* clock,
163 ConnectionFactory* connection_factory,
164 GCMStore* gcm_store,
165 GCMStatsRecorder* recorder) {
166 return make_scoped_ptr<MCSClient>(
167 new MCSClient(version,
168 clock,
169 connection_factory,
170 gcm_store,
171 recorder));
174 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
175 const std::vector<GURL>& endpoints,
176 const net::BackoffEntry::Policy& backoff_policy,
177 scoped_refptr<net::HttpNetworkSession> network_session,
178 net::NetLog* net_log,
179 GCMStatsRecorder* recorder) {
180 return make_scoped_ptr<ConnectionFactory>(
181 new ConnectionFactoryImpl(endpoints,
182 backoff_policy,
183 network_session,
184 net_log,
185 recorder));
188 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
189 : internals_builder_(internals_builder.Pass()),
190 state_(UNINITIALIZED),
191 clock_(internals_builder_->BuildClock()),
192 url_request_context_getter_(NULL),
193 pending_registration_requests_deleter_(&pending_registration_requests_),
194 pending_unregistration_requests_deleter_(
195 &pending_unregistration_requests_),
196 periodic_checkin_ptr_factory_(this),
197 weak_ptr_factory_(this) {
200 GCMClientImpl::~GCMClientImpl() {
203 void GCMClientImpl::Initialize(
204 const checkin_proto::ChromeBuildProto& chrome_build_proto,
205 const base::FilePath& path,
206 const std::vector<std::string>& account_ids,
207 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
208 const scoped_refptr<net::URLRequestContextGetter>&
209 url_request_context_getter,
210 scoped_ptr<Encryptor> encryptor,
211 GCMClient::Delegate* delegate) {
212 DCHECK_EQ(UNINITIALIZED, state_);
213 DCHECK(url_request_context_getter);
214 DCHECK(delegate);
216 url_request_context_getter_ = url_request_context_getter;
217 const net::HttpNetworkSession::Params* network_session_params =
218 url_request_context_getter_->GetURLRequestContext()->
219 GetNetworkSessionParams();
220 DCHECK(network_session_params);
221 network_session_ = new net::HttpNetworkSession(*network_session_params);
223 chrome_build_proto_.CopyFrom(chrome_build_proto);
224 account_ids_ = account_ids;
226 gcm_store_.reset(
227 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
229 delegate_ = delegate;
231 recorder_.SetDelegate(this);
233 state_ = INITIALIZED;
236 void GCMClientImpl::Start() {
237 DCHECK_EQ(INITIALIZED, state_);
239 // Once the loading is completed, the check-in will be initiated.
240 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
241 weak_ptr_factory_.GetWeakPtr()));
242 state_ = LOADING;
245 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
246 DCHECK_EQ(LOADING, state_);
248 if (!result->success) {
249 ResetState();
250 return;
253 registrations_ = result->registrations;
254 device_checkin_info_.android_id = result->device_android_id;
255 device_checkin_info_.secret = result->device_security_token;
256 last_checkin_time_ = result->last_checkin_time;
257 gservices_settings_.UpdateFromLoadResult(*result);
258 InitializeMCSClient(result.Pass());
260 if (device_checkin_info_.IsValid()) {
261 SchedulePeriodicCheckin();
262 OnReady();
263 return;
266 state_ = INITIAL_DEVICE_CHECKIN;
267 device_checkin_info_.Reset();
268 StartCheckin();
271 void GCMClientImpl::InitializeMCSClient(
272 scoped_ptr<GCMStore::LoadResult> result) {
273 std::vector<GURL> endpoints;
274 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
275 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
276 connection_factory_ = internals_builder_->BuildConnectionFactory(
277 endpoints,
278 kDefaultBackoffPolicy,
279 network_session_,
280 net_log_.net_log(),
281 &recorder_);
282 mcs_client_ = internals_builder_->BuildMCSClient(
283 chrome_build_proto_.chrome_version(),
284 clock_.get(),
285 connection_factory_.get(),
286 gcm_store_.get(),
287 &recorder_).Pass();
289 mcs_client_->Initialize(
290 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
291 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
292 weak_ptr_factory_.GetWeakPtr()),
293 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
294 weak_ptr_factory_.GetWeakPtr()),
295 result.Pass());
298 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
299 const CheckinInfo& checkin_info) {
300 DCHECK(!device_checkin_info_.IsValid());
302 device_checkin_info_.android_id = checkin_info.android_id;
303 device_checkin_info_.secret = checkin_info.secret;
304 gcm_store_->SetDeviceCredentials(
305 checkin_info.android_id, checkin_info.secret,
306 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
307 weak_ptr_factory_.GetWeakPtr()));
309 OnReady();
312 void GCMClientImpl::OnReady() {
313 state_ = READY;
314 StartMCSLogin();
316 delegate_->OnGCMReady();
319 void GCMClientImpl::StartMCSLogin() {
320 DCHECK_EQ(READY, state_);
321 DCHECK(device_checkin_info_.IsValid());
322 mcs_client_->Login(device_checkin_info_.android_id,
323 device_checkin_info_.secret);
326 void GCMClientImpl::ResetState() {
327 state_ = UNINITIALIZED;
328 // TODO(fgorski): reset all of the necessart objects and start over.
331 void GCMClientImpl::StartCheckin() {
332 // Make sure no checkin is in progress.
333 if (checkin_request_.get())
334 return;
336 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
337 device_checkin_info_.secret,
338 gservices_settings_.digest(),
339 account_ids_,
340 chrome_build_proto_);
341 checkin_request_.reset(
342 new CheckinRequest(gservices_settings_.GetCheckinURL(),
343 request_info,
344 kDefaultBackoffPolicy,
345 base::Bind(&GCMClientImpl::OnCheckinCompleted,
346 weak_ptr_factory_.GetWeakPtr()),
347 url_request_context_getter_,
348 &recorder_));
349 checkin_request_->Start();
352 void GCMClientImpl::OnCheckinCompleted(
353 const checkin_proto::AndroidCheckinResponse& checkin_response) {
354 checkin_request_.reset();
356 if (!checkin_response.has_android_id() ||
357 !checkin_response.has_security_token()) {
358 // TODO(fgorski): I don't think a retry here will help, we should probably
359 // start over. By checking in with (0, 0).
360 return;
363 CheckinInfo checkin_info;
364 checkin_info.android_id = checkin_response.android_id();
365 checkin_info.secret = checkin_response.security_token();
367 if (state_ == INITIAL_DEVICE_CHECKIN) {
368 OnFirstTimeDeviceCheckinCompleted(checkin_info);
369 } else {
370 // checkin_info is not expected to change after a periodic checkin as it
371 // would invalidate the registratoin IDs.
372 DCHECK_EQ(READY, state_);
373 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
374 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
377 if (device_checkin_info_.IsValid()) {
378 // First update G-services settings, as something might have changed.
379 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
380 gcm_store_->SetGServicesSettings(
381 gservices_settings_.settings_map(),
382 gservices_settings_.digest(),
383 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
384 weak_ptr_factory_.GetWeakPtr()));
387 last_checkin_time_ = clock_->Now();
388 gcm_store_->SetLastCheckinTime(
389 last_checkin_time_,
390 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
391 weak_ptr_factory_.GetWeakPtr()));
392 SchedulePeriodicCheckin();
396 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
397 DCHECK(success);
400 void GCMClientImpl::SchedulePeriodicCheckin() {
401 // Make sure no checkin is in progress.
402 if (checkin_request_.get())
403 return;
405 // There should be only one periodic checkin pending at a time. Removing
406 // pending periodic checkin to schedule a new one.
407 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
409 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
410 if (time_to_next_checkin < base::TimeDelta())
411 time_to_next_checkin = base::TimeDelta();
413 base::MessageLoop::current()->PostDelayedTask(
414 FROM_HERE,
415 base::Bind(&GCMClientImpl::StartCheckin,
416 periodic_checkin_ptr_factory_.GetWeakPtr()),
417 time_to_next_checkin);
420 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
421 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
422 clock_->Now();
425 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
426 // TODO(fgorski): This is one of the signals that store needs a rebuild.
427 DCHECK(success);
430 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
431 // TODO(fgorski): This is one of the signals that store needs a rebuild.
432 DCHECK(success);
435 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
436 // TODO(fgorski): This is one of the signals that store needs a rebuild.
437 DCHECK(success);
440 void GCMClientImpl::Stop() {
441 device_checkin_info_.Reset();
442 connection_factory_.reset();
443 mcs_client_.reset();
444 checkin_request_.reset();
445 pending_registration_requests_.clear();
446 state_ = INITIALIZED;
447 gcm_store_->Close();
450 void GCMClientImpl::CheckOut() {
451 Stop();
452 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
453 weak_ptr_factory_.GetWeakPtr()));
456 void GCMClientImpl::Register(const std::string& app_id,
457 const std::vector<std::string>& sender_ids) {
458 DCHECK_EQ(state_, READY);
460 // If the same sender ids is provided, return the cached registration ID
461 // directly.
462 RegistrationInfoMap::const_iterator registrations_iter =
463 registrations_.find(app_id);
464 if (registrations_iter != registrations_.end() &&
465 registrations_iter->second->sender_ids == sender_ids) {
466 delegate_->OnRegisterFinished(
467 app_id, registrations_iter->second->registration_id, SUCCESS);
468 return;
471 RegistrationRequest::RequestInfo request_info(
472 device_checkin_info_.android_id,
473 device_checkin_info_.secret,
474 app_id,
475 sender_ids);
476 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
478 RegistrationRequest* registration_request =
479 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
480 request_info,
481 kDefaultBackoffPolicy,
482 base::Bind(&GCMClientImpl::OnRegisterCompleted,
483 weak_ptr_factory_.GetWeakPtr(),
484 app_id,
485 sender_ids),
486 kMaxRegistrationRetries,
487 url_request_context_getter_,
488 &recorder_);
489 pending_registration_requests_[app_id] = registration_request;
490 registration_request->Start();
493 void GCMClientImpl::OnRegisterCompleted(
494 const std::string& app_id,
495 const std::vector<std::string>& sender_ids,
496 RegistrationRequest::Status status,
497 const std::string& registration_id) {
498 DCHECK(delegate_);
500 Result result;
501 PendingRegistrationRequests::iterator iter =
502 pending_registration_requests_.find(app_id);
503 if (iter == pending_registration_requests_.end())
504 result = UNKNOWN_ERROR;
505 else if (status == RegistrationRequest::INVALID_SENDER)
506 result = INVALID_PARAMETER;
507 else if (registration_id.empty())
508 result = SERVER_ERROR;
509 else
510 result = SUCCESS;
512 if (result == SUCCESS) {
513 // Cache it.
514 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
515 registration->sender_ids = sender_ids;
516 registration->registration_id = registration_id;
517 registrations_[app_id] = registration;
519 // Save it in the persistent store.
520 gcm_store_->AddRegistration(
521 app_id,
522 registration,
523 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
524 weak_ptr_factory_.GetWeakPtr()));
527 delegate_->OnRegisterFinished(
528 app_id, result == SUCCESS ? registration_id : std::string(), result);
530 if (iter != pending_registration_requests_.end()) {
531 delete iter->second;
532 pending_registration_requests_.erase(iter);
536 void GCMClientImpl::Unregister(const std::string& app_id) {
537 DCHECK_EQ(state_, READY);
538 if (pending_unregistration_requests_.count(app_id) == 1)
539 return;
541 // Remove from the cache and persistent store.
542 registrations_.erase(app_id);
543 gcm_store_->RemoveRegistration(
544 app_id,
545 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
546 weak_ptr_factory_.GetWeakPtr()));
548 UnregistrationRequest::RequestInfo request_info(
549 device_checkin_info_.android_id,
550 device_checkin_info_.secret,
551 app_id);
553 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
554 gservices_settings_.GetRegistrationURL(),
555 request_info,
556 kDefaultBackoffPolicy,
557 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
558 weak_ptr_factory_.GetWeakPtr(),
559 app_id),
560 url_request_context_getter_,
561 &recorder_);
562 pending_unregistration_requests_[app_id] = unregistration_request;
563 unregistration_request->Start();
566 void GCMClientImpl::OnUnregisterCompleted(
567 const std::string& app_id,
568 UnregistrationRequest::Status status) {
569 DVLOG(1) << "Unregister completed for app: " << app_id
570 << " with " << (status ? "success." : "failure.");
571 delegate_->OnUnregisterFinished(
572 app_id,
573 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
575 PendingUnregistrationRequests::iterator iter =
576 pending_unregistration_requests_.find(app_id);
577 if (iter == pending_unregistration_requests_.end())
578 return;
580 delete iter->second;
581 pending_unregistration_requests_.erase(iter);
584 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
585 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
586 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
589 void GCMClientImpl::Send(const std::string& app_id,
590 const std::string& receiver_id,
591 const OutgoingMessage& message) {
592 DCHECK_EQ(state_, READY);
594 RecordOutgoingMessageToUMA(message);
596 mcs_proto::DataMessageStanza stanza;
597 stanza.set_ttl(message.time_to_live);
598 stanza.set_sent(clock_->Now().ToInternalValue() /
599 base::Time::kMicrosecondsPerSecond);
600 stanza.set_id(message.id);
601 stanza.set_from(kSendMessageFromValue);
602 stanza.set_to(receiver_id);
603 stanza.set_category(app_id);
605 for (MessageData::const_iterator iter = message.data.begin();
606 iter != message.data.end();
607 ++iter) {
608 mcs_proto::AppData* app_data = stanza.add_app_data();
609 app_data->set_key(iter->first);
610 app_data->set_value(iter->second);
613 MCSMessage mcs_message(stanza);
614 DVLOG(1) << "MCS message size: " << mcs_message.size();
615 mcs_client_->SendMessage(mcs_message);
618 std::string GCMClientImpl::GetStateString() const {
619 switch(state_) {
620 case GCMClientImpl::INITIALIZED:
621 return "INITIALIZED";
622 case GCMClientImpl::UNINITIALIZED:
623 return "UNINITIALIZED";
624 case GCMClientImpl::LOADING:
625 return "LOADING";
626 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
627 return "INITIAL_DEVICE_CHECKIN";
628 case GCMClientImpl::READY:
629 return "READY";
630 default:
631 NOTREACHED();
632 return std::string();
636 void GCMClientImpl::SetRecording(bool recording) {
637 recorder_.SetRecording(recording);
640 void GCMClientImpl::ClearActivityLogs() {
641 recorder_.Clear();
644 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
645 GCMClient::GCMStatistics stats;
646 stats.gcm_client_created = true;
647 stats.is_recording = recorder_.is_recording();
648 stats.gcm_client_state = GetStateString();
649 stats.connection_client_created = mcs_client_.get() != NULL;
650 if (mcs_client_.get()) {
651 stats.connection_state = mcs_client_->GetStateString();
652 stats.send_queue_size = mcs_client_->GetSendQueueSize();
653 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
655 if (device_checkin_info_.android_id > 0)
656 stats.android_id = device_checkin_info_.android_id;
657 recorder_.CollectActivities(&stats.recorded_activities);
659 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
660 it != registrations_.end(); ++it) {
661 stats.registered_app_ids.push_back(it->first);
663 return stats;
666 void GCMClientImpl::OnActivityRecorded() {
667 delegate_->OnActivityRecorded();
670 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
671 switch (message.tag()) {
672 case kLoginResponseTag:
673 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
674 return;
675 case kDataMessageStanzaTag:
676 DVLOG(1) << "A downstream message received. Processing...";
677 HandleIncomingMessage(message);
678 return;
679 default:
680 NOTREACHED() << "Message with unexpected tag received by GCMClient";
681 return;
685 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
686 const std::string& app_id,
687 const std::string& message_id,
688 MCSClient::MessageSendStatus status) {
689 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
690 DCHECK(delegate_);
692 // TTL_EXCEEDED is singled out here, because it can happen long time after the
693 // message was sent. That is why it comes as |OnMessageSendError| event rather
694 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
695 // All other errors will be raised immediately, through asynchronous callback.
696 // It is expected that TTL_EXCEEDED will be issued for a message that was
697 // previously issued |OnSendFinished| with status SUCCESS.
698 // For now, we do not report that the message has been sent and acked
699 // successfully.
700 // TODO(jianli): Consider adding UMA for this status.
701 if (status == MCSClient::TTL_EXCEEDED) {
702 SendErrorDetails send_error_details;
703 send_error_details.message_id = message_id;
704 send_error_details.result = GCMClient::TTL_EXCEEDED;
705 delegate_->OnMessageSendError(app_id, send_error_details);
706 } else if (status != MCSClient::SENT) {
707 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
711 void GCMClientImpl::OnMCSError() {
712 // TODO(fgorski): For now it replaces the initialization method. Long term it
713 // should have an error or status passed in.
716 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
717 DCHECK(delegate_);
719 const mcs_proto::DataMessageStanza& data_message_stanza =
720 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
721 message.GetProtobuf());
722 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
724 // Copying all the data from the stanza to a MessageData object. When present,
725 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
726 // later.
727 MessageData message_data;
728 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
729 std::string key = data_message_stanza.app_data(i).key();
730 message_data[key] = data_message_stanza.app_data(i).value();
733 MessageType message_type = DATA_MESSAGE;
734 MessageData::iterator iter = message_data.find(kMessageTypeKey);
735 if (iter != message_data.end()) {
736 message_type = DecodeMessageType(iter->second);
737 message_data.erase(iter);
740 switch (message_type) {
741 case DATA_MESSAGE:
742 HandleIncomingDataMessage(data_message_stanza, message_data);
743 break;
744 case DELETED_MESSAGES:
745 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
746 data_message_stanza.from(),
747 data_message_stanza.ByteSize(),
748 true,
749 GCMStatsRecorder::DELETED_MESSAGES);
750 delegate_->OnMessagesDeleted(data_message_stanza.category());
751 break;
752 case SEND_ERROR:
753 HandleIncomingSendError(data_message_stanza, message_data);
754 break;
755 case UNKNOWN:
756 default: // Treat default the same as UNKNOWN.
757 DVLOG(1) << "Unknown message_type received. Message ignored. "
758 << "App ID: " << data_message_stanza.category() << ".";
759 break;
763 void GCMClientImpl::HandleIncomingDataMessage(
764 const mcs_proto::DataMessageStanza& data_message_stanza,
765 MessageData& message_data) {
766 std::string app_id = data_message_stanza.category();
768 // Drop the message when the app is not registered for the sender of the
769 // message.
770 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
771 bool not_registered =
772 iter == registrations_.end() ||
773 std::find(iter->second->sender_ids.begin(),
774 iter->second->sender_ids.end(),
775 data_message_stanza.from()) == iter->second->sender_ids.end();
776 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
777 data_message_stanza.ByteSize(), !not_registered,
778 GCMStatsRecorder::DATA_MESSAGE);
779 if (not_registered) {
780 return;
783 IncomingMessage incoming_message;
784 incoming_message.sender_id = data_message_stanza.from();
785 if (data_message_stanza.has_token())
786 incoming_message.collapse_key = data_message_stanza.token();
787 incoming_message.data = message_data;
788 delegate_->OnMessageReceived(app_id, incoming_message);
791 void GCMClientImpl::HandleIncomingSendError(
792 const mcs_proto::DataMessageStanza& data_message_stanza,
793 MessageData& message_data) {
794 SendErrorDetails send_error_details;
795 send_error_details.additional_data = message_data;
796 send_error_details.result = SERVER_ERROR;
798 MessageData::iterator iter =
799 send_error_details.additional_data.find(kSendErrorMessageIdKey);
800 if (iter != send_error_details.additional_data.end()) {
801 send_error_details.message_id = iter->second;
802 send_error_details.additional_data.erase(iter);
805 recorder_.RecordIncomingSendError(
806 data_message_stanza.category(),
807 data_message_stanza.to(),
808 data_message_stanza.id());
809 delegate_->OnMessageSendError(data_message_stanza.category(),
810 send_error_details);
813 } // namespace gcm