[GCM] Updating GServicesSettings to use GURLs and TimeDelta
[chromium-blink-merge.git] / google_apis / gcm / gcm_client_impl.cc
blob13b7eb874a5b6684ac0a1d6cb3cedbe28f1d1c01
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/engine/gservices_settings.h"
23 #include "google_apis/gcm/engine/mcs_client.h"
24 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "net/http/http_network_session.h"
27 #include "net/url_request/url_request_context.h"
28 #include "url/gurl.h"
30 namespace gcm {
32 namespace {
34 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
35 // retries.
36 // Note: In order to ensure a minimum of 20 seconds between server errors (for
37 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
38 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
39 // themselves.
40 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
41 // Number of initial errors (in sequence) to ignore before applying
42 // exponential back-off rules.
45 // Initial delay for exponential back-off in ms.
46 30 * 1000, // 30 seconds.
48 // Factor by which the waiting time will be multiplied.
51 // Fuzzing percentage. ex: 10% will spread requests randomly
52 // between 90%-100% of the calculated time.
53 0.33, // 33%.
55 // Maximum amount of time we are willing to delay our request in ms.
56 10 * 60 * 1000, // 10 minutes.
58 // Time to keep an entry from being discarded even when it
59 // has no significant state, -1 to never discard.
60 -1,
62 // Don't use initial delay unless the last request was an error.
63 false,
66 // Indicates a message type of the received message.
67 enum MessageType {
68 UNKNOWN, // Undetermined type.
69 DATA_MESSAGE, // Regular data message.
70 DELETED_MESSAGES, // Messages were deleted on the server.
71 SEND_ERROR, // Error sending a message.
74 // MCS endpoints. SSL Key pinning is done automatically due to the *.google.com
75 // pinning rule.
76 // Note: modifying the endpoints will affect the ability to compare the
77 // GCM.CurrentEnpoint histogram across versions.
78 const char kMCSEndpointMain[] = "https://mtalk.google.com:5228";
79 const char kMCSEndpointFallback[] = "https://mtalk.google.com:443";
81 const int kMaxRegistrationRetries = 5;
82 const char kMessageTypeDataMessage[] = "gcm";
83 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
84 const char kMessageTypeKey[] = "message_type";
85 const char kMessageTypeSendErrorKey[] = "send_error";
86 const char kSendErrorMessageIdKey[] = "google.message_id";
87 const char kSendMessageFromValue[] = "gcm@chrome.com";
88 const int64 kDefaultUserSerialNumber = 0LL;
90 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
91 switch (status) {
92 case MCSClient::QUEUED:
93 return GCMClient::SUCCESS;
94 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
95 return GCMClient::NETWORK_ERROR;
96 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
97 return GCMClient::NETWORK_ERROR;
98 case MCSClient::MESSAGE_TOO_LARGE:
99 return GCMClient::INVALID_PARAMETER;
100 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
101 return GCMClient::NETWORK_ERROR;
102 case MCSClient::TTL_EXCEEDED:
103 return GCMClient::NETWORK_ERROR;
104 case MCSClient::SENT:
105 default:
106 NOTREACHED();
107 break;
109 return GCMClientImpl::UNKNOWN_ERROR;
112 MessageType DecodeMessageType(const std::string& value) {
113 if (kMessageTypeDeletedMessagesKey == value)
114 return DELETED_MESSAGES;
115 if (kMessageTypeSendErrorKey == value)
116 return SEND_ERROR;
117 if (kMessageTypeDataMessage == value)
118 return DATA_MESSAGE;
119 return UNKNOWN;
122 } // namespace
124 GCMInternalsBuilder::GCMInternalsBuilder() {}
125 GCMInternalsBuilder::~GCMInternalsBuilder() {}
127 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
128 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
131 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
132 const std::string& version,
133 base::Clock* clock,
134 ConnectionFactory* connection_factory,
135 GCMStore* gcm_store,
136 GCMStatsRecorder* recorder) {
137 return make_scoped_ptr<MCSClient>(
138 new MCSClient(version,
139 clock,
140 connection_factory,
141 gcm_store,
142 recorder));
145 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
146 const std::vector<GURL>& endpoints,
147 const net::BackoffEntry::Policy& backoff_policy,
148 scoped_refptr<net::HttpNetworkSession> network_session,
149 net::NetLog* net_log,
150 GCMStatsRecorder* recorder) {
151 return make_scoped_ptr<ConnectionFactory>(
152 new ConnectionFactoryImpl(endpoints,
153 backoff_policy,
154 network_session,
155 net_log,
156 recorder));
159 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
160 : internals_builder_(internals_builder.Pass()),
161 state_(UNINITIALIZED),
162 clock_(internals_builder_->BuildClock()),
163 url_request_context_getter_(NULL),
164 pending_registration_requests_deleter_(&pending_registration_requests_),
165 pending_unregistration_requests_deleter_(
166 &pending_unregistration_requests_),
167 periodic_checkin_ptr_factory_(this),
168 weak_ptr_factory_(this) {
171 GCMClientImpl::~GCMClientImpl() {
174 void GCMClientImpl::Initialize(
175 const checkin_proto::ChromeBuildProto& chrome_build_proto,
176 const base::FilePath& path,
177 const std::vector<std::string>& account_ids,
178 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
179 const scoped_refptr<net::URLRequestContextGetter>&
180 url_request_context_getter,
181 Delegate* delegate) {
182 DCHECK_EQ(UNINITIALIZED, state_);
183 DCHECK(url_request_context_getter);
184 DCHECK(delegate);
186 url_request_context_getter_ = url_request_context_getter;
187 const net::HttpNetworkSession::Params* network_session_params =
188 url_request_context_getter_->GetURLRequestContext()->
189 GetNetworkSessionParams();
190 DCHECK(network_session_params);
191 network_session_ = new net::HttpNetworkSession(*network_session_params);
193 chrome_build_proto_.CopyFrom(chrome_build_proto);
194 account_ids_ = account_ids;
196 gcm_store_.reset(new GCMStoreImpl(path, blocking_task_runner));
197 gservices_settings_.reset(new GServicesSettings(gcm_store_.get()));
199 delegate_ = delegate;
201 state_ = INITIALIZED;
204 void GCMClientImpl::Load() {
205 DCHECK_EQ(INITIALIZED, state_);
207 // Once the loading is completed, the check-in will be initiated.
208 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
209 weak_ptr_factory_.GetWeakPtr()));
210 state_ = LOADING;
213 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
214 DCHECK_EQ(LOADING, state_);
216 if (!result->success) {
217 ResetState();
218 return;
221 registrations_ = result->registrations;
222 device_checkin_info_.android_id = result->device_android_id;
223 device_checkin_info_.secret = result->device_security_token;
224 last_checkin_time_ = result->last_checkin_time;
225 gservices_settings_->UpdateFromLoadResult(*result);
226 InitializeMCSClient(result.Pass());
228 if (device_checkin_info_.IsValid()) {
229 SchedulePeriodicCheckin();
230 OnReady();
231 return;
234 state_ = INITIAL_DEVICE_CHECKIN;
235 device_checkin_info_.Reset();
236 StartCheckin();
239 void GCMClientImpl::InitializeMCSClient(
240 scoped_ptr<GCMStore::LoadResult> result) {
241 std::vector<GURL> endpoints;
242 endpoints.push_back(GURL(kMCSEndpointMain));
243 endpoints.push_back(GURL(kMCSEndpointFallback));
244 connection_factory_ = internals_builder_->BuildConnectionFactory(
245 endpoints,
246 kDefaultBackoffPolicy,
247 network_session_,
248 net_log_.net_log(),
249 &recorder_);
250 mcs_client_ = internals_builder_->BuildMCSClient(
251 chrome_build_proto_.chrome_version(),
252 clock_.get(),
253 connection_factory_.get(),
254 gcm_store_.get(),
255 &recorder_).Pass();
257 mcs_client_->Initialize(
258 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
259 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
260 weak_ptr_factory_.GetWeakPtr()),
261 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
262 weak_ptr_factory_.GetWeakPtr()),
263 result.Pass());
266 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
267 const CheckinInfo& checkin_info) {
268 DCHECK(!device_checkin_info_.IsValid());
270 device_checkin_info_.android_id = checkin_info.android_id;
271 device_checkin_info_.secret = checkin_info.secret;
272 gcm_store_->SetDeviceCredentials(
273 checkin_info.android_id, checkin_info.secret,
274 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
275 weak_ptr_factory_.GetWeakPtr()));
277 OnReady();
280 void GCMClientImpl::OnReady() {
281 state_ = READY;
282 StartMCSLogin();
284 delegate_->OnGCMReady();
287 void GCMClientImpl::StartMCSLogin() {
288 DCHECK_EQ(READY, state_);
289 DCHECK(device_checkin_info_.IsValid());
290 mcs_client_->Login(device_checkin_info_.android_id,
291 device_checkin_info_.secret);
294 void GCMClientImpl::ResetState() {
295 state_ = UNINITIALIZED;
296 // TODO(fgorski): reset all of the necessart objects and start over.
299 void GCMClientImpl::StartCheckin() {
300 // Make sure no checkin is in progress.
301 if (checkin_request_.get())
302 return;
304 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
305 device_checkin_info_.secret,
306 gservices_settings_->digest(),
307 account_ids_,
308 chrome_build_proto_);
309 checkin_request_.reset(
310 new CheckinRequest(gservices_settings_->checkin_url(),
311 request_info,
312 kDefaultBackoffPolicy,
313 base::Bind(&GCMClientImpl::OnCheckinCompleted,
314 weak_ptr_factory_.GetWeakPtr()),
315 url_request_context_getter_));
316 checkin_request_->Start();
319 void GCMClientImpl::OnCheckinCompleted(
320 const checkin_proto::AndroidCheckinResponse& checkin_response) {
321 checkin_request_.reset();
323 if (!checkin_response.has_android_id() ||
324 !checkin_response.has_security_token()) {
325 // TODO(fgorski): I don't think a retry here will help, we should probably
326 // start over. By checking in with (0, 0).
327 return;
330 CheckinInfo checkin_info;
331 checkin_info.android_id = checkin_response.android_id();
332 checkin_info.secret = checkin_response.security_token();
334 if (state_ == INITIAL_DEVICE_CHECKIN) {
335 OnFirstTimeDeviceCheckinCompleted(checkin_info);
336 } else {
337 // checkin_info is not expected to change after a periodic checkin as it
338 // would invalidate the registratoin IDs.
339 DCHECK_EQ(READY, state_);
340 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
341 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
344 if (device_checkin_info_.IsValid()) {
345 // First update G-services settings, as something might have changed.
346 gservices_settings_->UpdateFromCheckinResponse(checkin_response);
347 last_checkin_time_ = clock_->Now();
348 gcm_store_->SetLastCheckinTime(
349 last_checkin_time_,
350 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
351 weak_ptr_factory_.GetWeakPtr()));
352 SchedulePeriodicCheckin();
356 void GCMClientImpl::SchedulePeriodicCheckin() {
357 // Make sure no checkin is in progress.
358 if (checkin_request_.get())
359 return;
361 // There should be only one periodic checkin pending at a time. Removing
362 // pending periodic checkin to schedule a new one.
363 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
365 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
366 if (time_to_next_checkin < base::TimeDelta())
367 time_to_next_checkin = base::TimeDelta();
369 base::MessageLoop::current()->PostDelayedTask(
370 FROM_HERE,
371 base::Bind(&GCMClientImpl::StartCheckin,
372 periodic_checkin_ptr_factory_.GetWeakPtr()),
373 time_to_next_checkin);
376 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
377 return last_checkin_time_ + gservices_settings_->checkin_interval() -
378 clock_->Now();
381 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
382 // TODO(fgorski): This is one of the signals that store needs a rebuild.
383 DCHECK(success);
386 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
387 // TODO(fgorski): This is one of the signals that store needs a rebuild.
388 DCHECK(success);
391 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
392 // TODO(fgorski): This is one of the signals that store needs a rebuild.
393 DCHECK(success);
396 void GCMClientImpl::Stop() {
397 device_checkin_info_.Reset();
398 connection_factory_.reset();
399 mcs_client_.reset();
400 checkin_request_.reset();
401 pending_registration_requests_.clear();
402 state_ = INITIALIZED;
403 gcm_store_->Close();
406 void GCMClientImpl::CheckOut() {
407 Stop();
408 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
409 weak_ptr_factory_.GetWeakPtr()));
412 void GCMClientImpl::Register(const std::string& app_id,
413 const std::vector<std::string>& sender_ids) {
414 DCHECK_EQ(state_, READY);
416 // If the same sender ids is provided, return the cached registration ID
417 // directly.
418 RegistrationInfoMap::const_iterator registrations_iter =
419 registrations_.find(app_id);
420 if (registrations_iter != registrations_.end() &&
421 registrations_iter->second->sender_ids == sender_ids) {
422 delegate_->OnRegisterFinished(
423 app_id, registrations_iter->second->registration_id, SUCCESS);
424 return;
427 RegistrationRequest::RequestInfo request_info(
428 device_checkin_info_.android_id,
429 device_checkin_info_.secret,
430 app_id,
431 sender_ids);
432 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
434 RegistrationRequest* registration_request =
435 new RegistrationRequest(gservices_settings_->registration_url(),
436 request_info,
437 kDefaultBackoffPolicy,
438 base::Bind(&GCMClientImpl::OnRegisterCompleted,
439 weak_ptr_factory_.GetWeakPtr(),
440 app_id,
441 sender_ids),
442 kMaxRegistrationRetries,
443 url_request_context_getter_,
444 &recorder_);
445 pending_registration_requests_[app_id] = registration_request;
446 registration_request->Start();
449 void GCMClientImpl::OnRegisterCompleted(
450 const std::string& app_id,
451 const std::vector<std::string>& sender_ids,
452 RegistrationRequest::Status status,
453 const std::string& registration_id) {
454 DCHECK(delegate_);
456 Result result;
457 PendingRegistrationRequests::iterator iter =
458 pending_registration_requests_.find(app_id);
459 if (iter == pending_registration_requests_.end())
460 result = UNKNOWN_ERROR;
461 else if (status == RegistrationRequest::INVALID_SENDER)
462 result = INVALID_PARAMETER;
463 else if (registration_id.empty())
464 result = SERVER_ERROR;
465 else
466 result = SUCCESS;
468 if (result == SUCCESS) {
469 // Cache it.
470 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
471 registration->sender_ids = sender_ids;
472 registration->registration_id = registration_id;
473 registrations_[app_id] = registration;
475 // Save it in the persistent store.
476 gcm_store_->AddRegistration(
477 app_id,
478 registration,
479 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
480 weak_ptr_factory_.GetWeakPtr()));
483 delegate_->OnRegisterFinished(
484 app_id, result == SUCCESS ? registration_id : std::string(), result);
486 if (iter != pending_registration_requests_.end()) {
487 delete iter->second;
488 pending_registration_requests_.erase(iter);
492 void GCMClientImpl::Unregister(const std::string& app_id) {
493 DCHECK_EQ(state_, READY);
494 if (pending_unregistration_requests_.count(app_id) == 1)
495 return;
497 // Remove from the cache and persistent store.
498 registrations_.erase(app_id);
499 gcm_store_->RemoveRegistration(
500 app_id,
501 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
502 weak_ptr_factory_.GetWeakPtr()));
504 UnregistrationRequest::RequestInfo request_info(
505 device_checkin_info_.android_id,
506 device_checkin_info_.secret,
507 app_id);
509 UnregistrationRequest* unregistration_request =
510 new UnregistrationRequest(
511 gservices_settings_->registration_url(),
512 request_info,
513 kDefaultBackoffPolicy,
514 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
515 weak_ptr_factory_.GetWeakPtr(),
516 app_id),
517 url_request_context_getter_,
518 &recorder_);
519 pending_unregistration_requests_[app_id] = unregistration_request;
520 unregistration_request->Start();
523 void GCMClientImpl::OnUnregisterCompleted(
524 const std::string& app_id,
525 UnregistrationRequest::Status status) {
526 DVLOG(1) << "Unregister completed for app: " << app_id
527 << " with " << (status ? "success." : "failure.");
528 delegate_->OnUnregisterFinished(
529 app_id,
530 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
532 PendingUnregistrationRequests::iterator iter =
533 pending_unregistration_requests_.find(app_id);
534 if (iter == pending_unregistration_requests_.end())
535 return;
537 delete iter->second;
538 pending_unregistration_requests_.erase(iter);
541 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
542 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
543 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
546 void GCMClientImpl::Send(const std::string& app_id,
547 const std::string& receiver_id,
548 const OutgoingMessage& message) {
549 DCHECK_EQ(state_, READY);
551 mcs_proto::DataMessageStanza stanza;
552 stanza.set_ttl(message.time_to_live);
553 stanza.set_sent(clock_->Now().ToInternalValue() /
554 base::Time::kMicrosecondsPerSecond);
555 stanza.set_id(message.id);
556 stanza.set_from(kSendMessageFromValue);
557 stanza.set_to(receiver_id);
558 stanza.set_category(app_id);
560 for (MessageData::const_iterator iter = message.data.begin();
561 iter != message.data.end();
562 ++iter) {
563 mcs_proto::AppData* app_data = stanza.add_app_data();
564 app_data->set_key(iter->first);
565 app_data->set_value(iter->second);
568 MCSMessage mcs_message(stanza);
569 DVLOG(1) << "MCS message size: " << mcs_message.size();
570 mcs_client_->SendMessage(mcs_message);
573 std::string GCMClientImpl::GetStateString() const {
574 switch(state_) {
575 case GCMClientImpl::INITIALIZED:
576 return "INITIALIZED";
577 case GCMClientImpl::UNINITIALIZED:
578 return "UNINITIALIZED";
579 case GCMClientImpl::LOADING:
580 return "LOADING";
581 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
582 return "INITIAL_DEVICE_CHECKIN";
583 case GCMClientImpl::READY:
584 return "READY";
585 default:
586 NOTREACHED();
587 return std::string();
591 void GCMClientImpl::SetRecording(bool recording) {
592 recorder_.SetRecording(recording);
595 void GCMClientImpl::ClearActivityLogs() {
596 recorder_.Clear();
599 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
600 GCMClient::GCMStatistics stats;
601 stats.gcm_client_created = true;
602 stats.is_recording = recorder_.is_recording();
603 stats.gcm_client_state = GetStateString();
604 stats.connection_client_created = mcs_client_.get() != NULL;
605 if (mcs_client_.get()) {
606 stats.connection_state = mcs_client_->GetStateString();
607 stats.send_queue_size = mcs_client_->GetSendQueueSize();
608 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
610 if (device_checkin_info_.android_id > 0)
611 stats.android_id = device_checkin_info_.android_id;
612 recorder_.CollectActivities(&stats.recorded_activities);
614 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
615 it != registrations_.end(); ++it) {
616 stats.registered_app_ids.push_back(it->first);
618 return stats;
621 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
622 switch (message.tag()) {
623 case kLoginResponseTag:
624 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
625 return;
626 case kDataMessageStanzaTag:
627 DVLOG(1) << "A downstream message received. Processing...";
628 HandleIncomingMessage(message);
629 return;
630 default:
631 NOTREACHED() << "Message with unexpected tag received by GCMClient";
632 return;
636 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
637 const std::string& app_id,
638 const std::string& message_id,
639 MCSClient::MessageSendStatus status) {
640 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
641 DCHECK(delegate_);
643 // TTL_EXCEEDED is singled out here, because it can happen long time after the
644 // message was sent. That is why it comes as |OnMessageSendError| event rather
645 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
646 // All other errors will be raised immediately, through asynchronous callback.
647 // It is expected that TTL_EXCEEDED will be issued for a message that was
648 // previously issued |OnSendFinished| with status SUCCESS.
649 // For now, we do not report that the message has been sent and acked
650 // successfully.
651 // TODO(jianli): Consider adding UMA for this status.
652 if (status == MCSClient::TTL_EXCEEDED) {
653 SendErrorDetails send_error_details;
654 send_error_details.message_id = message_id;
655 send_error_details.result = GCMClient::TTL_EXCEEDED;
656 delegate_->OnMessageSendError(app_id, send_error_details);
657 } else if (status != MCSClient::SENT) {
658 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
662 void GCMClientImpl::OnMCSError() {
663 // TODO(fgorski): For now it replaces the initialization method. Long term it
664 // should have an error or status passed in.
667 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
668 DCHECK(delegate_);
670 const mcs_proto::DataMessageStanza& data_message_stanza =
671 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
672 message.GetProtobuf());
673 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
675 // Copying all the data from the stanza to a MessageData object. When present,
676 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
677 // later.
678 MessageData message_data;
679 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
680 std::string key = data_message_stanza.app_data(i).key();
681 message_data[key] = data_message_stanza.app_data(i).value();
684 MessageType message_type = DATA_MESSAGE;
685 MessageData::iterator iter = message_data.find(kMessageTypeKey);
686 if (iter != message_data.end()) {
687 message_type = DecodeMessageType(iter->second);
688 message_data.erase(iter);
691 switch (message_type) {
692 case DATA_MESSAGE:
693 HandleIncomingDataMessage(data_message_stanza, message_data);
694 break;
695 case DELETED_MESSAGES:
696 recorder_.RecordDataMessageRecieved(data_message_stanza.category(),
697 data_message_stanza.from(),
698 data_message_stanza.ByteSize(),
699 true,
700 GCMStatsRecorder::DELETED_MESSAGES);
701 delegate_->OnMessagesDeleted(data_message_stanza.category());
702 break;
703 case SEND_ERROR:
704 HandleIncomingSendError(data_message_stanza, message_data);
705 break;
706 case UNKNOWN:
707 default: // Treat default the same as UNKNOWN.
708 DVLOG(1) << "Unknown message_type received. Message ignored. "
709 << "App ID: " << data_message_stanza.category() << ".";
710 break;
714 void GCMClientImpl::HandleIncomingDataMessage(
715 const mcs_proto::DataMessageStanza& data_message_stanza,
716 MessageData& message_data) {
717 std::string app_id = data_message_stanza.category();
719 // Drop the message when the app is not registered for the sender of the
720 // message.
721 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
722 bool not_registered =
723 iter == registrations_.end() ||
724 std::find(iter->second->sender_ids.begin(),
725 iter->second->sender_ids.end(),
726 data_message_stanza.from()) == iter->second->sender_ids.end();
727 recorder_.RecordDataMessageRecieved(app_id, data_message_stanza.from(),
728 data_message_stanza.ByteSize(), !not_registered,
729 GCMStatsRecorder::DATA_MESSAGE);
730 if (not_registered) {
731 return;
734 IncomingMessage incoming_message;
735 incoming_message.sender_id = data_message_stanza.from();
736 if (data_message_stanza.has_token())
737 incoming_message.collapse_key = data_message_stanza.token();
738 incoming_message.data = message_data;
739 delegate_->OnMessageReceived(app_id, incoming_message);
742 void GCMClientImpl::HandleIncomingSendError(
743 const mcs_proto::DataMessageStanza& data_message_stanza,
744 MessageData& message_data) {
745 SendErrorDetails send_error_details;
746 send_error_details.additional_data = message_data;
747 send_error_details.result = SERVER_ERROR;
749 MessageData::iterator iter =
750 send_error_details.additional_data.find(kSendErrorMessageIdKey);
751 if (iter != send_error_details.additional_data.end()) {
752 send_error_details.message_id = iter->second;
753 send_error_details.additional_data.erase(iter);
756 recorder_.RecordIncomingSendError(
757 data_message_stanza.category(),
758 data_message_stanza.to(),
759 data_message_stanza.id());
760 delegate_->OnMessageSendError(data_message_stanza.category(),
761 send_error_details);
764 } // namespace gcm