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"
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"
33 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
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
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.
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.
61 // Don't use initial delay unless the last request was an error.
65 // Indicates a message type of the received message.
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
{
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
,
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.
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
) {
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
:
115 return GCMClientImpl::UNKNOWN_ERROR
;
118 MessageType
DecodeMessageType(const std::string
& value
) {
119 if (kMessageTypeDeletedMessagesKey
== value
)
120 return DELETED_MESSAGES
;
121 if (kMessageTypeSendErrorKey
== value
)
123 if (kMessageTypeDataMessage
== value
)
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
;
144 ttl_category
= TTL_MAXIMUM
;
146 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
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
,
163 ConnectionFactory
* connection_factory
,
165 GCMStatsRecorder
* recorder
) {
166 return make_scoped_ptr
<MCSClient
>(
167 new MCSClient(version
,
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
,
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
);
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
;
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()));
245 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
246 DCHECK_EQ(LOADING
, state_
);
248 if (!result
->success
) {
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();
266 state_
= INITIAL_DEVICE_CHECKIN
;
267 device_checkin_info_
.Reset();
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(
278 kDefaultBackoffPolicy
,
282 mcs_client_
= internals_builder_
->BuildMCSClient(
283 chrome_build_proto_
.chrome_version(),
285 connection_factory_
.get(),
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()),
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()));
312 void GCMClientImpl::OnReady() {
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())
336 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
337 device_checkin_info_
.secret
,
338 gservices_settings_
.digest(),
340 chrome_build_proto_
);
341 checkin_request_
.reset(
342 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
344 kDefaultBackoffPolicy
,
345 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
346 weak_ptr_factory_
.GetWeakPtr()),
347 url_request_context_getter_
,
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).
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
);
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(
390 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback
,
391 weak_ptr_factory_
.GetWeakPtr()));
392 SchedulePeriodicCheckin();
396 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
400 void GCMClientImpl::SchedulePeriodicCheckin() {
401 // Make sure no checkin is in progress.
402 if (checkin_request_
.get())
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(
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() -
425 void GCMClientImpl::SetLastCheckinTimeCallback(bool success
) {
426 // TODO(fgorski): This is one of the signals that store needs a rebuild.
430 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
431 // TODO(fgorski): This is one of the signals that store needs a rebuild.
435 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
436 // TODO(fgorski): This is one of the signals that store needs a rebuild.
440 void GCMClientImpl::Stop() {
441 device_checkin_info_
.Reset();
442 connection_factory_
.reset();
444 checkin_request_
.reset();
445 pending_registration_requests_
.clear();
446 state_
= INITIALIZED
;
450 void GCMClientImpl::CheckOut() {
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
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
);
471 RegistrationRequest::RequestInfo
request_info(
472 device_checkin_info_
.android_id
,
473 device_checkin_info_
.secret
,
476 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
478 RegistrationRequest
* registration_request
=
479 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
481 kDefaultBackoffPolicy
,
482 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
483 weak_ptr_factory_
.GetWeakPtr(),
486 kMaxRegistrationRetries
,
487 url_request_context_getter_
,
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
) {
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
;
512 if (result
== SUCCESS
) {
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(
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()) {
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)
541 // Remove from the cache and persistent store.
542 registrations_
.erase(app_id
);
543 gcm_store_
->RemoveRegistration(
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
,
553 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
554 gservices_settings_
.GetRegistrationURL(),
556 kDefaultBackoffPolicy
,
557 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
558 weak_ptr_factory_
.GetWeakPtr(),
560 url_request_context_getter_
,
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(
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())
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();
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 {
620 case GCMClientImpl::INITIALIZED
:
621 return "INITIALIZED";
622 case GCMClientImpl::UNINITIALIZED
:
623 return "UNINITIALIZED";
624 case GCMClientImpl::LOADING
:
626 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
627 return "INITIAL_DEVICE_CHECKIN";
628 case GCMClientImpl::READY
:
632 return std::string();
636 void GCMClientImpl::SetRecording(bool recording
) {
637 recorder_
.SetRecording(recording
);
640 void GCMClientImpl::ClearActivityLogs() {
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
);
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.";
675 case kDataMessageStanzaTag
:
676 DVLOG(1) << "A downstream message received. Processing...";
677 HandleIncomingMessage(message
);
680 NOTREACHED() << "Message with unexpected tag received by GCMClient";
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
);
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
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
) {
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
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
) {
742 HandleIncomingDataMessage(data_message_stanza
, message_data
);
744 case DELETED_MESSAGES
:
745 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
746 data_message_stanza
.from(),
747 data_message_stanza
.ByteSize(),
749 GCMStatsRecorder::DELETED_MESSAGES
);
750 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
753 HandleIncomingSendError(data_message_stanza
, message_data
);
756 default: // Treat default the same as UNKNOWN.
757 DVLOG(1) << "Unknown message_type received. Message ignored. "
758 << "App ID: " << data_message_stanza
.category() << ".";
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
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
) {
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(),