Sanitize standard file descriptors in launcher script
[chromium-blink-merge.git] / google_apis / gcm / gcm_client_impl.cc
blobb5a3ae6e5c15b4fed6a296193b57144d18429583
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/checkin.pb.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 enum OutgoingMessageTTLCategory {
75 TTL_ZERO,
76 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
78 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
79 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
80 TTL_MORE_THAN_ONE_WEEK,
81 TTL_MAXIMUM,
82 // NOTE: always keep this entry at the end. Add new TTL category only
83 // immediately above this line. Make sure to update the corresponding
84 // histogram enum accordingly.
85 TTL_CATEGORY_COUNT
88 const int kMaxRegistrationRetries = 5;
89 const char kMessageTypeDataMessage[] = "gcm";
90 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
91 const char kMessageTypeKey[] = "message_type";
92 const char kMessageTypeSendErrorKey[] = "send_error";
93 const char kSendErrorMessageIdKey[] = "google.message_id";
94 const char kSendMessageFromValue[] = "gcm@chrome.com";
95 const int64 kDefaultUserSerialNumber = 0LL;
97 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
98 switch (status) {
99 case MCSClient::QUEUED:
100 return GCMClient::SUCCESS;
101 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
102 return GCMClient::NETWORK_ERROR;
103 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
104 return GCMClient::NETWORK_ERROR;
105 case MCSClient::MESSAGE_TOO_LARGE:
106 return GCMClient::INVALID_PARAMETER;
107 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
108 return GCMClient::NETWORK_ERROR;
109 case MCSClient::TTL_EXCEEDED:
110 return GCMClient::NETWORK_ERROR;
111 case MCSClient::SENT:
112 default:
113 NOTREACHED();
114 break;
116 return GCMClientImpl::UNKNOWN_ERROR;
119 void ToCheckinProtoVersion(
120 const GCMClient::ChromeBuildInfo& chrome_build_info,
121 checkin_proto::ChromeBuildProto* android_build_info) {
122 checkin_proto::ChromeBuildProto_Platform platform;
123 switch (chrome_build_info.platform) {
124 case GCMClient::PLATFORM_WIN:
125 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
126 break;
127 case GCMClient::PLATFORM_MAC:
128 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
129 break;
130 case GCMClient::PLATFORM_LINUX:
131 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
132 break;
133 case GCMClient::PLATFORM_IOS:
134 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
135 break;
136 case GCMClient::PLATFORM_ANDROID:
137 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
138 break;
139 case GCMClient::PLATFORM_CROS:
140 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
141 break;
142 case GCMClient::PLATFORM_UNKNOWN:
143 // For unknown platform, return as LINUX.
144 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
145 break;
146 default:
147 NOTREACHED();
148 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
149 break;
151 android_build_info->set_platform(platform);
153 checkin_proto::ChromeBuildProto_Channel channel;
154 switch (chrome_build_info.channel) {
155 case GCMClient::CHANNEL_STABLE:
156 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
157 break;
158 case GCMClient::CHANNEL_BETA:
159 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
160 break;
161 case GCMClient::CHANNEL_DEV:
162 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
163 break;
164 case GCMClient::CHANNEL_CANARY:
165 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
166 break;
167 case GCMClient::CHANNEL_UNKNOWN:
168 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
169 break;
170 default:
171 NOTREACHED();
172 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
173 break;
175 android_build_info->set_channel(channel);
177 android_build_info->set_chrome_version(chrome_build_info.version);
180 MessageType DecodeMessageType(const std::string& value) {
181 if (kMessageTypeDeletedMessagesKey == value)
182 return DELETED_MESSAGES;
183 if (kMessageTypeSendErrorKey == value)
184 return SEND_ERROR;
185 if (kMessageTypeDataMessage == value)
186 return DATA_MESSAGE;
187 return UNKNOWN;
190 void RecordOutgoingMessageToUMA(
191 const gcm::GCMClient::OutgoingMessage& message) {
192 OutgoingMessageTTLCategory ttl_category;
193 if (message.time_to_live == 0)
194 ttl_category = TTL_ZERO;
195 else if (message.time_to_live <= 60 )
196 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
197 else if (message.time_to_live <= 60 * 60)
198 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
199 else if (message.time_to_live <= 24 * 60 * 60)
200 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
201 else if (message.time_to_live <= 7 * 24 * 60 * 60)
202 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
203 else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
204 ttl_category = TTL_MORE_THAN_ONE_WEEK;
205 else
206 ttl_category = TTL_MAXIMUM;
208 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
209 ttl_category,
210 TTL_CATEGORY_COUNT);
213 } // namespace
215 GCMInternalsBuilder::GCMInternalsBuilder() {}
216 GCMInternalsBuilder::~GCMInternalsBuilder() {}
218 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
219 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
222 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
223 const std::string& version,
224 base::Clock* clock,
225 ConnectionFactory* connection_factory,
226 GCMStore* gcm_store,
227 GCMStatsRecorder* recorder) {
228 return make_scoped_ptr<MCSClient>(
229 new MCSClient(version,
230 clock,
231 connection_factory,
232 gcm_store,
233 recorder));
236 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
237 const std::vector<GURL>& endpoints,
238 const net::BackoffEntry::Policy& backoff_policy,
239 scoped_refptr<net::HttpNetworkSession> network_session,
240 net::NetLog* net_log,
241 GCMStatsRecorder* recorder) {
242 return make_scoped_ptr<ConnectionFactory>(
243 new ConnectionFactoryImpl(endpoints,
244 backoff_policy,
245 network_session,
246 net_log,
247 recorder));
250 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
251 : internals_builder_(internals_builder.Pass()),
252 state_(UNINITIALIZED),
253 clock_(internals_builder_->BuildClock()),
254 url_request_context_getter_(NULL),
255 pending_registration_requests_deleter_(&pending_registration_requests_),
256 pending_unregistration_requests_deleter_(
257 &pending_unregistration_requests_),
258 periodic_checkin_ptr_factory_(this),
259 weak_ptr_factory_(this) {
262 GCMClientImpl::~GCMClientImpl() {
265 void GCMClientImpl::Initialize(
266 const ChromeBuildInfo& chrome_build_info,
267 const base::FilePath& path,
268 const std::vector<std::string>& account_ids,
269 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
270 const scoped_refptr<net::URLRequestContextGetter>&
271 url_request_context_getter,
272 scoped_ptr<Encryptor> encryptor,
273 GCMClient::Delegate* delegate) {
274 DCHECK_EQ(UNINITIALIZED, state_);
275 DCHECK(url_request_context_getter);
276 DCHECK(delegate);
278 url_request_context_getter_ = url_request_context_getter;
279 const net::HttpNetworkSession::Params* network_session_params =
280 url_request_context_getter_->GetURLRequestContext()->
281 GetNetworkSessionParams();
282 DCHECK(network_session_params);
283 network_session_ = new net::HttpNetworkSession(*network_session_params);
285 chrome_build_info_ = chrome_build_info;
286 account_ids_ = account_ids;
288 gcm_store_.reset(
289 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
291 delegate_ = delegate;
293 recorder_.SetDelegate(this);
295 state_ = INITIALIZED;
298 void GCMClientImpl::Start() {
299 DCHECK_EQ(INITIALIZED, state_);
301 // Once the loading is completed, the check-in will be initiated.
302 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
303 weak_ptr_factory_.GetWeakPtr()));
304 state_ = LOADING;
307 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
308 DCHECK_EQ(LOADING, state_);
310 if (!result->success) {
311 ResetState();
312 return;
315 registrations_ = result->registrations;
316 device_checkin_info_.android_id = result->device_android_id;
317 device_checkin_info_.secret = result->device_security_token;
318 last_checkin_time_ = result->last_checkin_time;
319 gservices_settings_.UpdateFromLoadResult(*result);
320 InitializeMCSClient(result.Pass());
322 if (device_checkin_info_.IsValid()) {
323 SchedulePeriodicCheckin();
324 OnReady();
325 return;
328 state_ = INITIAL_DEVICE_CHECKIN;
329 device_checkin_info_.Reset();
330 StartCheckin();
333 void GCMClientImpl::InitializeMCSClient(
334 scoped_ptr<GCMStore::LoadResult> result) {
335 std::vector<GURL> endpoints;
336 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
337 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
338 connection_factory_ = internals_builder_->BuildConnectionFactory(
339 endpoints,
340 kDefaultBackoffPolicy,
341 network_session_,
342 net_log_.net_log(),
343 &recorder_);
344 mcs_client_ = internals_builder_->BuildMCSClient(
345 chrome_build_info_.version,
346 clock_.get(),
347 connection_factory_.get(),
348 gcm_store_.get(),
349 &recorder_).Pass();
351 mcs_client_->Initialize(
352 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
353 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
354 weak_ptr_factory_.GetWeakPtr()),
355 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
356 weak_ptr_factory_.GetWeakPtr()),
357 result.Pass());
360 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
361 const CheckinInfo& checkin_info) {
362 DCHECK(!device_checkin_info_.IsValid());
364 device_checkin_info_.android_id = checkin_info.android_id;
365 device_checkin_info_.secret = checkin_info.secret;
366 gcm_store_->SetDeviceCredentials(
367 checkin_info.android_id, checkin_info.secret,
368 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
369 weak_ptr_factory_.GetWeakPtr()));
371 OnReady();
374 void GCMClientImpl::OnReady() {
375 state_ = READY;
376 StartMCSLogin();
378 delegate_->OnGCMReady();
381 void GCMClientImpl::StartMCSLogin() {
382 DCHECK_EQ(READY, state_);
383 DCHECK(device_checkin_info_.IsValid());
384 mcs_client_->Login(device_checkin_info_.android_id,
385 device_checkin_info_.secret);
388 void GCMClientImpl::ResetState() {
389 state_ = UNINITIALIZED;
390 // TODO(fgorski): reset all of the necessart objects and start over.
393 void GCMClientImpl::StartCheckin() {
394 // Make sure no checkin is in progress.
395 if (checkin_request_.get())
396 return;
398 checkin_proto::ChromeBuildProto chrome_build_proto;
399 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
400 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
401 device_checkin_info_.secret,
402 gservices_settings_.digest(),
403 account_ids_,
404 chrome_build_proto);
405 checkin_request_.reset(
406 new CheckinRequest(gservices_settings_.GetCheckinURL(),
407 request_info,
408 kDefaultBackoffPolicy,
409 base::Bind(&GCMClientImpl::OnCheckinCompleted,
410 weak_ptr_factory_.GetWeakPtr()),
411 url_request_context_getter_,
412 &recorder_));
413 checkin_request_->Start();
416 void GCMClientImpl::OnCheckinCompleted(
417 const checkin_proto::AndroidCheckinResponse& checkin_response) {
418 checkin_request_.reset();
420 if (!checkin_response.has_android_id() ||
421 !checkin_response.has_security_token()) {
422 // TODO(fgorski): I don't think a retry here will help, we should probably
423 // start over. By checking in with (0, 0).
424 return;
427 CheckinInfo checkin_info;
428 checkin_info.android_id = checkin_response.android_id();
429 checkin_info.secret = checkin_response.security_token();
431 if (state_ == INITIAL_DEVICE_CHECKIN) {
432 OnFirstTimeDeviceCheckinCompleted(checkin_info);
433 } else {
434 // checkin_info is not expected to change after a periodic checkin as it
435 // would invalidate the registratoin IDs.
436 DCHECK_EQ(READY, state_);
437 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
438 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
441 if (device_checkin_info_.IsValid()) {
442 // First update G-services settings, as something might have changed.
443 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
444 gcm_store_->SetGServicesSettings(
445 gservices_settings_.settings_map(),
446 gservices_settings_.digest(),
447 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
448 weak_ptr_factory_.GetWeakPtr()));
451 last_checkin_time_ = clock_->Now();
452 gcm_store_->SetLastCheckinTime(
453 last_checkin_time_,
454 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
455 weak_ptr_factory_.GetWeakPtr()));
456 SchedulePeriodicCheckin();
460 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
461 DCHECK(success);
464 void GCMClientImpl::SchedulePeriodicCheckin() {
465 // Make sure no checkin is in progress.
466 if (checkin_request_.get())
467 return;
469 // There should be only one periodic checkin pending at a time. Removing
470 // pending periodic checkin to schedule a new one.
471 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
473 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
474 if (time_to_next_checkin < base::TimeDelta())
475 time_to_next_checkin = base::TimeDelta();
477 base::MessageLoop::current()->PostDelayedTask(
478 FROM_HERE,
479 base::Bind(&GCMClientImpl::StartCheckin,
480 periodic_checkin_ptr_factory_.GetWeakPtr()),
481 time_to_next_checkin);
484 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
485 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
486 clock_->Now();
489 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
490 // TODO(fgorski): This is one of the signals that store needs a rebuild.
491 DCHECK(success);
494 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
495 // TODO(fgorski): This is one of the signals that store needs a rebuild.
496 DCHECK(success);
499 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
500 // TODO(fgorski): This is one of the signals that store needs a rebuild.
501 DCHECK(success);
504 void GCMClientImpl::Stop() {
505 device_checkin_info_.Reset();
506 connection_factory_.reset();
507 mcs_client_.reset();
508 checkin_request_.reset();
509 pending_registration_requests_.clear();
510 state_ = INITIALIZED;
511 gcm_store_->Close();
514 void GCMClientImpl::CheckOut() {
515 Stop();
516 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
517 weak_ptr_factory_.GetWeakPtr()));
520 void GCMClientImpl::Register(const std::string& app_id,
521 const std::vector<std::string>& sender_ids) {
522 DCHECK_EQ(state_, READY);
524 // If the same sender ids is provided, return the cached registration ID
525 // directly.
526 RegistrationInfoMap::const_iterator registrations_iter =
527 registrations_.find(app_id);
528 if (registrations_iter != registrations_.end() &&
529 registrations_iter->second->sender_ids == sender_ids) {
530 delegate_->OnRegisterFinished(
531 app_id, registrations_iter->second->registration_id, SUCCESS);
532 return;
535 RegistrationRequest::RequestInfo request_info(
536 device_checkin_info_.android_id,
537 device_checkin_info_.secret,
538 app_id,
539 sender_ids);
540 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
542 RegistrationRequest* registration_request =
543 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
544 request_info,
545 kDefaultBackoffPolicy,
546 base::Bind(&GCMClientImpl::OnRegisterCompleted,
547 weak_ptr_factory_.GetWeakPtr(),
548 app_id,
549 sender_ids),
550 kMaxRegistrationRetries,
551 url_request_context_getter_,
552 &recorder_);
553 pending_registration_requests_[app_id] = registration_request;
554 registration_request->Start();
557 void GCMClientImpl::OnRegisterCompleted(
558 const std::string& app_id,
559 const std::vector<std::string>& sender_ids,
560 RegistrationRequest::Status status,
561 const std::string& registration_id) {
562 DCHECK(delegate_);
564 Result result;
565 PendingRegistrationRequests::iterator iter =
566 pending_registration_requests_.find(app_id);
567 if (iter == pending_registration_requests_.end())
568 result = UNKNOWN_ERROR;
569 else if (status == RegistrationRequest::INVALID_SENDER)
570 result = INVALID_PARAMETER;
571 else if (registration_id.empty())
572 result = SERVER_ERROR;
573 else
574 result = SUCCESS;
576 if (result == SUCCESS) {
577 // Cache it.
578 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
579 registration->sender_ids = sender_ids;
580 registration->registration_id = registration_id;
581 registrations_[app_id] = registration;
583 // Save it in the persistent store.
584 gcm_store_->AddRegistration(
585 app_id,
586 registration,
587 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
588 weak_ptr_factory_.GetWeakPtr()));
591 delegate_->OnRegisterFinished(
592 app_id, result == SUCCESS ? registration_id : std::string(), result);
594 if (iter != pending_registration_requests_.end()) {
595 delete iter->second;
596 pending_registration_requests_.erase(iter);
600 void GCMClientImpl::Unregister(const std::string& app_id) {
601 DCHECK_EQ(state_, READY);
602 if (pending_unregistration_requests_.count(app_id) == 1)
603 return;
605 // Remove from the cache and persistent store.
606 registrations_.erase(app_id);
607 gcm_store_->RemoveRegistration(
608 app_id,
609 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
610 weak_ptr_factory_.GetWeakPtr()));
612 UnregistrationRequest::RequestInfo request_info(
613 device_checkin_info_.android_id,
614 device_checkin_info_.secret,
615 app_id);
617 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
618 gservices_settings_.GetRegistrationURL(),
619 request_info,
620 kDefaultBackoffPolicy,
621 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
622 weak_ptr_factory_.GetWeakPtr(),
623 app_id),
624 url_request_context_getter_,
625 &recorder_);
626 pending_unregistration_requests_[app_id] = unregistration_request;
627 unregistration_request->Start();
630 void GCMClientImpl::OnUnregisterCompleted(
631 const std::string& app_id,
632 UnregistrationRequest::Status status) {
633 DVLOG(1) << "Unregister completed for app: " << app_id
634 << " with " << (status ? "success." : "failure.");
635 delegate_->OnUnregisterFinished(
636 app_id,
637 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
639 PendingUnregistrationRequests::iterator iter =
640 pending_unregistration_requests_.find(app_id);
641 if (iter == pending_unregistration_requests_.end())
642 return;
644 delete iter->second;
645 pending_unregistration_requests_.erase(iter);
648 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
649 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
650 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
653 void GCMClientImpl::Send(const std::string& app_id,
654 const std::string& receiver_id,
655 const OutgoingMessage& message) {
656 DCHECK_EQ(state_, READY);
658 RecordOutgoingMessageToUMA(message);
660 mcs_proto::DataMessageStanza stanza;
661 stanza.set_ttl(message.time_to_live);
662 stanza.set_sent(clock_->Now().ToInternalValue() /
663 base::Time::kMicrosecondsPerSecond);
664 stanza.set_id(message.id);
665 stanza.set_from(kSendMessageFromValue);
666 stanza.set_to(receiver_id);
667 stanza.set_category(app_id);
669 for (MessageData::const_iterator iter = message.data.begin();
670 iter != message.data.end();
671 ++iter) {
672 mcs_proto::AppData* app_data = stanza.add_app_data();
673 app_data->set_key(iter->first);
674 app_data->set_value(iter->second);
677 MCSMessage mcs_message(stanza);
678 DVLOG(1) << "MCS message size: " << mcs_message.size();
679 mcs_client_->SendMessage(mcs_message);
682 std::string GCMClientImpl::GetStateString() const {
683 switch(state_) {
684 case GCMClientImpl::INITIALIZED:
685 return "INITIALIZED";
686 case GCMClientImpl::UNINITIALIZED:
687 return "UNINITIALIZED";
688 case GCMClientImpl::LOADING:
689 return "LOADING";
690 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
691 return "INITIAL_DEVICE_CHECKIN";
692 case GCMClientImpl::READY:
693 return "READY";
694 default:
695 NOTREACHED();
696 return std::string();
700 void GCMClientImpl::SetRecording(bool recording) {
701 recorder_.SetRecording(recording);
704 void GCMClientImpl::ClearActivityLogs() {
705 recorder_.Clear();
708 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
709 GCMClient::GCMStatistics stats;
710 stats.gcm_client_created = true;
711 stats.is_recording = recorder_.is_recording();
712 stats.gcm_client_state = GetStateString();
713 stats.connection_client_created = mcs_client_.get() != NULL;
714 if (mcs_client_.get()) {
715 stats.connection_state = mcs_client_->GetStateString();
716 stats.send_queue_size = mcs_client_->GetSendQueueSize();
717 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
719 if (device_checkin_info_.android_id > 0)
720 stats.android_id = device_checkin_info_.android_id;
721 recorder_.CollectActivities(&stats.recorded_activities);
723 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
724 it != registrations_.end(); ++it) {
725 stats.registered_app_ids.push_back(it->first);
727 return stats;
730 void GCMClientImpl::OnActivityRecorded() {
731 delegate_->OnActivityRecorded();
734 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
735 switch (message.tag()) {
736 case kLoginResponseTag:
737 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
738 return;
739 case kDataMessageStanzaTag:
740 DVLOG(1) << "A downstream message received. Processing...";
741 HandleIncomingMessage(message);
742 return;
743 default:
744 NOTREACHED() << "Message with unexpected tag received by GCMClient";
745 return;
749 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
750 const std::string& app_id,
751 const std::string& message_id,
752 MCSClient::MessageSendStatus status) {
753 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
754 DCHECK(delegate_);
756 // TTL_EXCEEDED is singled out here, because it can happen long time after the
757 // message was sent. That is why it comes as |OnMessageSendError| event rather
758 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
759 // All other errors will be raised immediately, through asynchronous callback.
760 // It is expected that TTL_EXCEEDED will be issued for a message that was
761 // previously issued |OnSendFinished| with status SUCCESS.
762 // For now, we do not report that the message has been sent and acked
763 // successfully.
764 // TODO(jianli): Consider adding UMA for this status.
765 if (status == MCSClient::TTL_EXCEEDED) {
766 SendErrorDetails send_error_details;
767 send_error_details.message_id = message_id;
768 send_error_details.result = GCMClient::TTL_EXCEEDED;
769 delegate_->OnMessageSendError(app_id, send_error_details);
770 } else if (status != MCSClient::SENT) {
771 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
775 void GCMClientImpl::OnMCSError() {
776 // TODO(fgorski): For now it replaces the initialization method. Long term it
777 // should have an error or status passed in.
780 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
781 DCHECK(delegate_);
783 const mcs_proto::DataMessageStanza& data_message_stanza =
784 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
785 message.GetProtobuf());
786 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
788 // Copying all the data from the stanza to a MessageData object. When present,
789 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
790 // later.
791 MessageData message_data;
792 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
793 std::string key = data_message_stanza.app_data(i).key();
794 message_data[key] = data_message_stanza.app_data(i).value();
797 MessageType message_type = DATA_MESSAGE;
798 MessageData::iterator iter = message_data.find(kMessageTypeKey);
799 if (iter != message_data.end()) {
800 message_type = DecodeMessageType(iter->second);
801 message_data.erase(iter);
804 switch (message_type) {
805 case DATA_MESSAGE:
806 HandleIncomingDataMessage(data_message_stanza, message_data);
807 break;
808 case DELETED_MESSAGES:
809 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
810 data_message_stanza.from(),
811 data_message_stanza.ByteSize(),
812 true,
813 GCMStatsRecorder::DELETED_MESSAGES);
814 delegate_->OnMessagesDeleted(data_message_stanza.category());
815 break;
816 case SEND_ERROR:
817 HandleIncomingSendError(data_message_stanza, message_data);
818 break;
819 case UNKNOWN:
820 default: // Treat default the same as UNKNOWN.
821 DVLOG(1) << "Unknown message_type received. Message ignored. "
822 << "App ID: " << data_message_stanza.category() << ".";
823 break;
827 void GCMClientImpl::HandleIncomingDataMessage(
828 const mcs_proto::DataMessageStanza& data_message_stanza,
829 MessageData& message_data) {
830 std::string app_id = data_message_stanza.category();
832 // Drop the message when the app is not registered for the sender of the
833 // message.
834 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
835 bool not_registered =
836 iter == registrations_.end() ||
837 std::find(iter->second->sender_ids.begin(),
838 iter->second->sender_ids.end(),
839 data_message_stanza.from()) == iter->second->sender_ids.end();
840 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
841 data_message_stanza.ByteSize(), !not_registered,
842 GCMStatsRecorder::DATA_MESSAGE);
843 if (not_registered) {
844 return;
847 IncomingMessage incoming_message;
848 incoming_message.sender_id = data_message_stanza.from();
849 if (data_message_stanza.has_token())
850 incoming_message.collapse_key = data_message_stanza.token();
851 incoming_message.data = message_data;
852 delegate_->OnMessageReceived(app_id, incoming_message);
855 void GCMClientImpl::HandleIncomingSendError(
856 const mcs_proto::DataMessageStanza& data_message_stanza,
857 MessageData& message_data) {
858 SendErrorDetails send_error_details;
859 send_error_details.additional_data = message_data;
860 send_error_details.result = SERVER_ERROR;
862 MessageData::iterator iter =
863 send_error_details.additional_data.find(kSendErrorMessageIdKey);
864 if (iter != send_error_details.additional_data.end()) {
865 send_error_details.message_id = iter->second;
866 send_error_details.additional_data.erase(iter);
869 recorder_.RecordIncomingSendError(
870 data_message_stanza.category(),
871 data_message_stanza.to(),
872 data_message_stanza.id());
873 delegate_->OnMessageSendError(data_message_stanza.category(),
874 send_error_details);
877 } // namespace gcm