Allow RunShellCommand to work even with very large commands
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blobd320df553eab3ec1121c04fd5232bd0e43fdff9b
1 // Copyright 2014 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 "components/gcm_driver/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/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/default_clock.h"
18 #include "base/timer/timer.h"
19 #include "components/gcm_driver/gcm_backoff_policy.h"
20 #include "google_apis/gcm/base/encryptor.h"
21 #include "google_apis/gcm/base/mcs_message.h"
22 #include "google_apis/gcm/base/mcs_util.h"
23 #include "google_apis/gcm/engine/checkin_request.h"
24 #include "google_apis/gcm/engine/connection_factory_impl.h"
25 #include "google_apis/gcm/engine/gcm_store_impl.h"
26 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
27 #include "google_apis/gcm/protocol/checkin.pb.h"
28 #include "google_apis/gcm/protocol/mcs.pb.h"
29 #include "net/http/http_network_session.h"
30 #include "net/http/http_transaction_factory.h"
31 #include "net/url_request/url_request_context.h"
32 #include "url/gurl.h"
34 namespace gcm {
36 namespace {
38 // Indicates a message type of the received message.
39 enum MessageType {
40 UNKNOWN, // Undetermined type.
41 DATA_MESSAGE, // Regular data message.
42 DELETED_MESSAGES, // Messages were deleted on the server.
43 SEND_ERROR, // Error sending a message.
46 enum OutgoingMessageTTLCategory {
47 TTL_ZERO,
48 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
49 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
50 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
51 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
52 TTL_MORE_THAN_ONE_WEEK,
53 TTL_MAXIMUM,
54 // NOTE: always keep this entry at the end. Add new TTL category only
55 // immediately above this line. Make sure to update the corresponding
56 // histogram enum accordingly.
57 TTL_CATEGORY_COUNT
60 const int kMaxRegistrationRetries = 5;
61 const char kMessageTypeDataMessage[] = "gcm";
62 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
63 const char kMessageTypeKey[] = "message_type";
64 const char kMessageTypeSendErrorKey[] = "send_error";
65 const char kSendErrorMessageIdKey[] = "google.message_id";
66 const char kSendMessageFromValue[] = "gcm@chrome.com";
67 const int64 kDefaultUserSerialNumber = 0LL;
69 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
70 switch (status) {
71 case MCSClient::QUEUED:
72 return GCMClient::SUCCESS;
73 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
74 return GCMClient::NETWORK_ERROR;
75 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
76 return GCMClient::NETWORK_ERROR;
77 case MCSClient::MESSAGE_TOO_LARGE:
78 return GCMClient::INVALID_PARAMETER;
79 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
80 return GCMClient::NETWORK_ERROR;
81 case MCSClient::TTL_EXCEEDED:
82 return GCMClient::NETWORK_ERROR;
83 case MCSClient::SENT:
84 default:
85 NOTREACHED();
86 break;
88 return GCMClientImpl::UNKNOWN_ERROR;
91 void ToCheckinProtoVersion(
92 const GCMClient::ChromeBuildInfo& chrome_build_info,
93 checkin_proto::ChromeBuildProto* android_build_info) {
94 checkin_proto::ChromeBuildProto_Platform platform;
95 switch (chrome_build_info.platform) {
96 case GCMClient::PLATFORM_WIN:
97 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
98 break;
99 case GCMClient::PLATFORM_MAC:
100 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
101 break;
102 case GCMClient::PLATFORM_LINUX:
103 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
104 break;
105 case GCMClient::PLATFORM_IOS:
106 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
107 break;
108 case GCMClient::PLATFORM_ANDROID:
109 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
110 break;
111 case GCMClient::PLATFORM_CROS:
112 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
113 break;
114 case GCMClient::PLATFORM_UNKNOWN:
115 // For unknown platform, return as LINUX.
116 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
117 break;
118 default:
119 NOTREACHED();
120 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
121 break;
123 android_build_info->set_platform(platform);
125 checkin_proto::ChromeBuildProto_Channel channel;
126 switch (chrome_build_info.channel) {
127 case GCMClient::CHANNEL_STABLE:
128 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
129 break;
130 case GCMClient::CHANNEL_BETA:
131 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
132 break;
133 case GCMClient::CHANNEL_DEV:
134 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
135 break;
136 case GCMClient::CHANNEL_CANARY:
137 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
138 break;
139 case GCMClient::CHANNEL_UNKNOWN:
140 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
141 break;
142 default:
143 NOTREACHED();
144 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
145 break;
147 android_build_info->set_channel(channel);
149 android_build_info->set_chrome_version(chrome_build_info.version);
152 MessageType DecodeMessageType(const std::string& value) {
153 if (kMessageTypeDeletedMessagesKey == value)
154 return DELETED_MESSAGES;
155 if (kMessageTypeSendErrorKey == value)
156 return SEND_ERROR;
157 if (kMessageTypeDataMessage == value)
158 return DATA_MESSAGE;
159 return UNKNOWN;
162 void RecordOutgoingMessageToUMA(
163 const gcm::GCMClient::OutgoingMessage& message) {
164 OutgoingMessageTTLCategory ttl_category;
165 if (message.time_to_live == 0)
166 ttl_category = TTL_ZERO;
167 else if (message.time_to_live <= 60 )
168 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
169 else if (message.time_to_live <= 60 * 60)
170 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
171 else if (message.time_to_live <= 24 * 60 * 60)
172 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
173 else
174 ttl_category = TTL_MAXIMUM;
176 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
177 ttl_category,
178 TTL_CATEGORY_COUNT);
181 } // namespace
183 GCMInternalsBuilder::GCMInternalsBuilder() {}
184 GCMInternalsBuilder::~GCMInternalsBuilder() {}
186 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
187 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
190 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
191 const std::string& version,
192 base::Clock* clock,
193 ConnectionFactory* connection_factory,
194 GCMStore* gcm_store,
195 GCMStatsRecorder* recorder) {
196 return scoped_ptr<MCSClient>(new MCSClient(
197 version, clock, connection_factory, gcm_store, recorder));
200 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
201 const std::vector<GURL>& endpoints,
202 const net::BackoffEntry::Policy& backoff_policy,
203 const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
204 const scoped_refptr<net::HttpNetworkSession>& http_network_session,
205 net::NetLog* net_log,
206 GCMStatsRecorder* recorder) {
207 return make_scoped_ptr<ConnectionFactory>(
208 new ConnectionFactoryImpl(endpoints,
209 backoff_policy,
210 gcm_network_session,
211 http_network_session,
212 net_log,
213 recorder));
216 GCMClientImpl::CheckinInfo::CheckinInfo()
217 : android_id(0), secret(0), accounts_set(false) {
220 GCMClientImpl::CheckinInfo::~CheckinInfo() {
223 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
224 last_checkin_accounts.clear();
225 for (std::map<std::string, std::string>::iterator iter =
226 account_tokens.begin();
227 iter != account_tokens.end();
228 ++iter) {
229 last_checkin_accounts.insert(iter->first);
233 void GCMClientImpl::CheckinInfo::Reset() {
234 android_id = 0;
235 secret = 0;
236 accounts_set = false;
237 account_tokens.clear();
238 last_checkin_accounts.clear();
241 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
242 : internals_builder_(internals_builder.Pass()),
243 state_(UNINITIALIZED),
244 delegate_(NULL),
245 clock_(internals_builder_->BuildClock()),
246 url_request_context_getter_(NULL),
247 pending_registration_requests_deleter_(&pending_registration_requests_),
248 pending_unregistration_requests_deleter_(
249 &pending_unregistration_requests_),
250 periodic_checkin_ptr_factory_(this),
251 weak_ptr_factory_(this) {
254 GCMClientImpl::~GCMClientImpl() {
257 void GCMClientImpl::Initialize(
258 const ChromeBuildInfo& chrome_build_info,
259 const base::FilePath& path,
260 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
261 const scoped_refptr<net::URLRequestContextGetter>&
262 url_request_context_getter,
263 scoped_ptr<Encryptor> encryptor,
264 GCMClient::Delegate* delegate) {
265 DCHECK_EQ(UNINITIALIZED, state_);
266 DCHECK(url_request_context_getter.get());
267 DCHECK(delegate);
269 url_request_context_getter_ = url_request_context_getter;
270 const net::HttpNetworkSession::Params* network_session_params =
271 url_request_context_getter_->GetURLRequestContext()->
272 GetNetworkSessionParams();
273 DCHECK(network_session_params);
274 network_session_ = new net::HttpNetworkSession(*network_session_params);
276 chrome_build_info_ = chrome_build_info;
278 gcm_store_.reset(
279 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
281 delegate_ = delegate;
283 recorder_.SetDelegate(this);
285 state_ = INITIALIZED;
288 void GCMClientImpl::Start() {
289 DCHECK_EQ(INITIALIZED, state_);
291 // Once the loading is completed, the check-in will be initiated.
292 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
293 weak_ptr_factory_.GetWeakPtr()));
294 state_ = LOADING;
297 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
298 DCHECK_EQ(LOADING, state_);
300 if (!result->success) {
301 ResetState();
302 return;
305 registrations_ = result->registrations;
306 device_checkin_info_.android_id = result->device_android_id;
307 device_checkin_info_.secret = result->device_security_token;
308 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
309 // A case where there were previously no accounts reported with checkin is
310 // considered to be the same as when the list of accounts is empty. It enables
311 // scheduling a periodic checkin for devices with no signed in users
312 // immediately after restart, while keeping |accounts_set == false| delays the
313 // checkin until the list of accounts is set explicitly.
314 if (result->last_checkin_accounts.size() == 0)
315 device_checkin_info_.accounts_set = true;
316 last_checkin_time_ = result->last_checkin_time;
317 gservices_settings_.UpdateFromLoadResult(*result);
318 // Taking over the value of account_mappings before passing the ownership of
319 // load result to InitializeMCSClient.
320 std::vector<AccountMapping> account_mappings;
321 account_mappings.swap(result->account_mappings);
322 base::Time last_token_fetch_time = result->last_token_fetch_time;
324 InitializeMCSClient(result.Pass());
326 if (device_checkin_info_.IsValid()) {
327 SchedulePeriodicCheckin();
328 OnReady(account_mappings, last_token_fetch_time);
329 return;
332 state_ = INITIAL_DEVICE_CHECKIN;
333 device_checkin_info_.Reset();
334 StartCheckin();
337 void GCMClientImpl::InitializeMCSClient(
338 scoped_ptr<GCMStore::LoadResult> result) {
339 std::vector<GURL> endpoints;
340 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
341 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
342 connection_factory_ = internals_builder_->BuildConnectionFactory(
343 endpoints,
344 GetGCMBackoffPolicy(),
345 network_session_,
346 url_request_context_getter_->GetURLRequestContext()
347 ->http_transaction_factory()
348 ->GetSession(),
349 net_log_.net_log(),
350 &recorder_);
351 connection_factory_->SetConnectionListener(this);
352 mcs_client_ = internals_builder_->BuildMCSClient(
353 chrome_build_info_.version,
354 clock_.get(),
355 connection_factory_.get(),
356 gcm_store_.get(),
357 &recorder_).Pass();
359 mcs_client_->Initialize(
360 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
361 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
362 weak_ptr_factory_.GetWeakPtr()),
363 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
364 weak_ptr_factory_.GetWeakPtr()),
365 result.Pass());
368 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
369 const CheckinInfo& checkin_info) {
370 DCHECK(!device_checkin_info_.IsValid());
372 device_checkin_info_.android_id = checkin_info.android_id;
373 device_checkin_info_.secret = checkin_info.secret;
374 // If accounts were not set by now, we can consider them set (to empty list)
375 // to make sure periodic checkins get scheduled after initial checkin.
376 device_checkin_info_.accounts_set = true;
377 gcm_store_->SetDeviceCredentials(
378 checkin_info.android_id, checkin_info.secret,
379 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
380 weak_ptr_factory_.GetWeakPtr()));
382 OnReady(std::vector<AccountMapping>(), base::Time());
385 void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
386 const base::Time& last_token_fetch_time) {
387 state_ = READY;
388 StartMCSLogin();
390 delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
393 void GCMClientImpl::StartMCSLogin() {
394 DCHECK_EQ(READY, state_);
395 DCHECK(device_checkin_info_.IsValid());
396 mcs_client_->Login(device_checkin_info_.android_id,
397 device_checkin_info_.secret);
400 void GCMClientImpl::ResetState() {
401 state_ = UNINITIALIZED;
402 // TODO(fgorski): reset all of the necessart objects and start over.
405 void GCMClientImpl::SetAccountTokens(
406 const std::vector<AccountTokenInfo>& account_tokens) {
407 device_checkin_info_.account_tokens.clear();
408 for (std::vector<AccountTokenInfo>::const_iterator iter =
409 account_tokens.begin();
410 iter != account_tokens.end();
411 ++iter) {
412 device_checkin_info_.account_tokens[iter->email] = iter->access_token;
415 bool accounts_set_before = device_checkin_info_.accounts_set;
416 device_checkin_info_.accounts_set = true;
418 DVLOG(1) << "Set account called with: " << account_tokens.size()
419 << " accounts.";
421 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
422 return;
424 bool account_removed = false;
425 for (std::set<std::string>::iterator iter =
426 device_checkin_info_.last_checkin_accounts.begin();
427 iter != device_checkin_info_.last_checkin_accounts.end();
428 ++iter) {
429 if (device_checkin_info_.account_tokens.find(*iter) ==
430 device_checkin_info_.account_tokens.end()) {
431 account_removed = true;
435 // Checkin will be forced when any of the accounts was removed during the
436 // current Chrome session or if there has been an account removed between the
437 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
438 // We only force checkin when user signs out. When there is a new account
439 // signed in, the periodic checkin will take care of adding the association in
440 // reasonable time.
441 if (account_removed) {
442 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
443 checkin_request_.reset();
444 StartCheckin();
445 } else if (!accounts_set_before) {
446 SchedulePeriodicCheckin();
447 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
451 void GCMClientImpl::UpdateAccountMapping(
452 const AccountMapping& account_mapping) {
453 gcm_store_->AddAccountMapping(account_mapping,
454 base::Bind(&GCMClientImpl::DefaultStoreCallback,
455 weak_ptr_factory_.GetWeakPtr()));
458 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
459 gcm_store_->RemoveAccountMapping(
460 account_id,
461 base::Bind(&GCMClientImpl::DefaultStoreCallback,
462 weak_ptr_factory_.GetWeakPtr()));
465 void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
466 gcm_store_->SetLastTokenFetchTime(
467 time,
468 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
469 weak_ptr_factory_.GetWeakPtr()));
472 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
473 DCHECK(mcs_client_);
474 mcs_client_->UpdateHeartbeatTimer(timer.Pass());
477 void GCMClientImpl::StartCheckin() {
478 // Make sure no checkin is in progress.
479 if (checkin_request_.get())
480 return;
482 checkin_proto::ChromeBuildProto chrome_build_proto;
483 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
484 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
485 device_checkin_info_.secret,
486 device_checkin_info_.account_tokens,
487 gservices_settings_.digest(),
488 chrome_build_proto);
489 checkin_request_.reset(
490 new CheckinRequest(gservices_settings_.GetCheckinURL(),
491 request_info,
492 GetGCMBackoffPolicy(),
493 base::Bind(&GCMClientImpl::OnCheckinCompleted,
494 weak_ptr_factory_.GetWeakPtr()),
495 url_request_context_getter_.get(),
496 &recorder_));
497 // Taking a snapshot of the accounts count here, as there might be an asynch
498 // update of the account tokens while checkin is in progress.
499 device_checkin_info_.SnapshotCheckinAccounts();
500 checkin_request_->Start();
503 void GCMClientImpl::OnCheckinCompleted(
504 const checkin_proto::AndroidCheckinResponse& checkin_response) {
505 checkin_request_.reset();
507 if (!checkin_response.has_android_id() ||
508 !checkin_response.has_security_token()) {
509 // TODO(fgorski): I don't think a retry here will help, we should probably
510 // start over. By checking in with (0, 0).
511 return;
514 CheckinInfo checkin_info;
515 checkin_info.android_id = checkin_response.android_id();
516 checkin_info.secret = checkin_response.security_token();
518 if (state_ == INITIAL_DEVICE_CHECKIN) {
519 OnFirstTimeDeviceCheckinCompleted(checkin_info);
520 } else {
521 // checkin_info is not expected to change after a periodic checkin as it
522 // would invalidate the registratoin IDs.
523 DCHECK_EQ(READY, state_);
524 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
525 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
528 if (device_checkin_info_.IsValid()) {
529 // First update G-services settings, as something might have changed.
530 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
531 gcm_store_->SetGServicesSettings(
532 gservices_settings_.settings_map(),
533 gservices_settings_.digest(),
534 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
535 weak_ptr_factory_.GetWeakPtr()));
538 last_checkin_time_ = clock_->Now();
539 gcm_store_->SetLastCheckinInfo(
540 last_checkin_time_,
541 device_checkin_info_.last_checkin_accounts,
542 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
543 weak_ptr_factory_.GetWeakPtr()));
544 SchedulePeriodicCheckin();
548 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
549 DCHECK(success);
552 void GCMClientImpl::SchedulePeriodicCheckin() {
553 // Make sure no checkin is in progress.
554 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
555 return;
557 // There should be only one periodic checkin pending at a time. Removing
558 // pending periodic checkin to schedule a new one.
559 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
561 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
562 if (time_to_next_checkin < base::TimeDelta())
563 time_to_next_checkin = base::TimeDelta();
565 base::MessageLoop::current()->PostDelayedTask(
566 FROM_HERE,
567 base::Bind(&GCMClientImpl::StartCheckin,
568 periodic_checkin_ptr_factory_.GetWeakPtr()),
569 time_to_next_checkin);
572 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
573 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
574 clock_->Now();
577 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
578 // TODO(fgorski): This is one of the signals that store needs a rebuild.
579 DCHECK(success);
582 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
583 // TODO(fgorski): This is one of the signals that store needs a rebuild.
584 DCHECK(success);
587 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
588 // TODO(fgorski): This is one of the signals that store needs a rebuild.
589 DCHECK(success);
592 void GCMClientImpl::DefaultStoreCallback(bool success) {
593 DCHECK(success);
596 void GCMClientImpl::IgnoreWriteResultCallback(bool success) {
597 // TODO(fgorski): Ignoring the write result for now to make sure
598 // sync_intergration_tests are not broken.
601 void GCMClientImpl::Stop() {
602 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
603 // Shutdown.
604 DVLOG(1) << "Stopping the GCM Client";
605 weak_ptr_factory_.InvalidateWeakPtrs();
606 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
607 device_checkin_info_.Reset();
608 connection_factory_.reset();
609 delegate_->OnDisconnected();
610 mcs_client_.reset();
611 checkin_request_.reset();
612 // Delete all of the pending registration and unregistration requests.
613 STLDeleteValues(&pending_registration_requests_);
614 STLDeleteValues(&pending_unregistration_requests_);
615 state_ = INITIALIZED;
616 gcm_store_->Close();
619 void GCMClientImpl::CheckOut() {
620 Stop();
621 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
622 weak_ptr_factory_.GetWeakPtr()));
625 void GCMClientImpl::Register(const std::string& app_id,
626 const std::vector<std::string>& sender_ids) {
627 DCHECK_EQ(state_, READY);
629 // If the same sender ids is provided, return the cached registration ID
630 // directly.
631 RegistrationInfoMap::const_iterator registrations_iter =
632 registrations_.find(app_id);
633 if (registrations_iter != registrations_.end() &&
634 registrations_iter->second->sender_ids == sender_ids) {
635 delegate_->OnRegisterFinished(
636 app_id, registrations_iter->second->registration_id, SUCCESS);
637 return;
640 RegistrationRequest::RequestInfo request_info(
641 device_checkin_info_.android_id,
642 device_checkin_info_.secret,
643 app_id,
644 sender_ids);
645 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
647 RegistrationRequest* registration_request =
648 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
649 request_info,
650 GetGCMBackoffPolicy(),
651 base::Bind(&GCMClientImpl::OnRegisterCompleted,
652 weak_ptr_factory_.GetWeakPtr(),
653 app_id,
654 sender_ids),
655 kMaxRegistrationRetries,
656 url_request_context_getter_,
657 &recorder_);
658 pending_registration_requests_[app_id] = registration_request;
659 registration_request->Start();
662 void GCMClientImpl::OnRegisterCompleted(
663 const std::string& app_id,
664 const std::vector<std::string>& sender_ids,
665 RegistrationRequest::Status status,
666 const std::string& registration_id) {
667 DCHECK(delegate_);
669 Result result;
670 PendingRegistrationRequests::iterator iter =
671 pending_registration_requests_.find(app_id);
672 if (iter == pending_registration_requests_.end())
673 result = UNKNOWN_ERROR;
674 else if (status == RegistrationRequest::INVALID_SENDER)
675 result = INVALID_PARAMETER;
676 else if (registration_id.empty())
677 result = SERVER_ERROR;
678 else
679 result = SUCCESS;
681 if (result == SUCCESS) {
682 // Cache it.
683 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
684 registration->sender_ids = sender_ids;
685 registration->registration_id = registration_id;
686 registrations_[app_id] = registration;
688 // Save it in the persistent store.
689 gcm_store_->AddRegistration(
690 app_id,
691 registration,
692 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
693 weak_ptr_factory_.GetWeakPtr()));
696 delegate_->OnRegisterFinished(
697 app_id, result == SUCCESS ? registration_id : std::string(), result);
699 if (iter != pending_registration_requests_.end()) {
700 delete iter->second;
701 pending_registration_requests_.erase(iter);
705 void GCMClientImpl::Unregister(const std::string& app_id) {
706 DCHECK_EQ(state_, READY);
707 if (pending_unregistration_requests_.count(app_id) == 1)
708 return;
710 // Remove from the cache and persistent store.
711 registrations_.erase(app_id);
712 gcm_store_->RemoveRegistration(
713 app_id,
714 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
715 weak_ptr_factory_.GetWeakPtr()));
717 UnregistrationRequest::RequestInfo request_info(
718 device_checkin_info_.android_id,
719 device_checkin_info_.secret,
720 app_id);
722 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
723 gservices_settings_.GetRegistrationURL(),
724 request_info,
725 GetGCMBackoffPolicy(),
726 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
727 weak_ptr_factory_.GetWeakPtr(),
728 app_id),
729 url_request_context_getter_,
730 &recorder_);
731 pending_unregistration_requests_[app_id] = unregistration_request;
732 unregistration_request->Start();
735 void GCMClientImpl::OnUnregisterCompleted(
736 const std::string& app_id,
737 UnregistrationRequest::Status status) {
738 DVLOG(1) << "Unregister completed for app: " << app_id
739 << " with " << (status ? "success." : "failure.");
740 delegate_->OnUnregisterFinished(
741 app_id,
742 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
744 PendingUnregistrationRequests::iterator iter =
745 pending_unregistration_requests_.find(app_id);
746 if (iter == pending_unregistration_requests_.end())
747 return;
749 delete iter->second;
750 pending_unregistration_requests_.erase(iter);
753 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
754 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
755 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
758 void GCMClientImpl::Send(const std::string& app_id,
759 const std::string& receiver_id,
760 const OutgoingMessage& message) {
761 DCHECK_EQ(state_, READY);
763 RecordOutgoingMessageToUMA(message);
765 mcs_proto::DataMessageStanza stanza;
766 stanza.set_ttl(message.time_to_live);
767 stanza.set_sent(clock_->Now().ToInternalValue() /
768 base::Time::kMicrosecondsPerSecond);
769 stanza.set_id(message.id);
770 stanza.set_from(kSendMessageFromValue);
771 stanza.set_to(receiver_id);
772 stanza.set_category(app_id);
774 for (MessageData::const_iterator iter = message.data.begin();
775 iter != message.data.end();
776 ++iter) {
777 mcs_proto::AppData* app_data = stanza.add_app_data();
778 app_data->set_key(iter->first);
779 app_data->set_value(iter->second);
782 MCSMessage mcs_message(stanza);
783 DVLOG(1) << "MCS message size: " << mcs_message.size();
784 mcs_client_->SendMessage(mcs_message);
787 std::string GCMClientImpl::GetStateString() const {
788 switch(state_) {
789 case GCMClientImpl::INITIALIZED:
790 return "INITIALIZED";
791 case GCMClientImpl::UNINITIALIZED:
792 return "UNINITIALIZED";
793 case GCMClientImpl::LOADING:
794 return "LOADING";
795 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
796 return "INITIAL_DEVICE_CHECKIN";
797 case GCMClientImpl::READY:
798 return "READY";
799 default:
800 NOTREACHED();
801 return std::string();
805 void GCMClientImpl::SetRecording(bool recording) {
806 recorder_.SetRecording(recording);
809 void GCMClientImpl::ClearActivityLogs() {
810 recorder_.Clear();
813 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
814 GCMClient::GCMStatistics stats;
815 stats.gcm_client_created = true;
816 stats.is_recording = recorder_.is_recording();
817 stats.gcm_client_state = GetStateString();
818 stats.connection_client_created = mcs_client_.get() != NULL;
819 if (connection_factory_.get())
820 stats.connection_state = connection_factory_->GetConnectionStateString();
821 if (mcs_client_.get()) {
822 stats.send_queue_size = mcs_client_->GetSendQueueSize();
823 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
825 if (device_checkin_info_.android_id > 0)
826 stats.android_id = device_checkin_info_.android_id;
827 recorder_.CollectActivities(&stats.recorded_activities);
829 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
830 it != registrations_.end(); ++it) {
831 stats.registered_app_ids.push_back(it->first);
833 return stats;
836 void GCMClientImpl::OnActivityRecorded() {
837 delegate_->OnActivityRecorded();
840 void GCMClientImpl::OnConnected(const GURL& current_server,
841 const net::IPEndPoint& ip_endpoint) {
842 // TODO(gcm): expose current server in debug page.
843 delegate_->OnActivityRecorded();
844 delegate_->OnConnected(ip_endpoint);
847 void GCMClientImpl::OnDisconnected() {
848 delegate_->OnActivityRecorded();
849 delegate_->OnDisconnected();
852 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
853 switch (message.tag()) {
854 case kLoginResponseTag:
855 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
856 return;
857 case kDataMessageStanzaTag:
858 DVLOG(1) << "A downstream message received. Processing...";
859 HandleIncomingMessage(message);
860 return;
861 default:
862 NOTREACHED() << "Message with unexpected tag received by GCMClient";
863 return;
867 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
868 const std::string& app_id,
869 const std::string& message_id,
870 MCSClient::MessageSendStatus status) {
871 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
872 DCHECK(delegate_);
874 // TTL_EXCEEDED is singled out here, because it can happen long time after the
875 // message was sent. That is why it comes as |OnMessageSendError| event rather
876 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
877 // All other errors will be raised immediately, through asynchronous callback.
878 // It is expected that TTL_EXCEEDED will be issued for a message that was
879 // previously issued |OnSendFinished| with status SUCCESS.
880 // TODO(jianli): Consider adding UMA for this status.
881 if (status == MCSClient::TTL_EXCEEDED) {
882 SendErrorDetails send_error_details;
883 send_error_details.message_id = message_id;
884 send_error_details.result = GCMClient::TTL_EXCEEDED;
885 delegate_->OnMessageSendError(app_id, send_error_details);
886 } else if (status == MCSClient::SENT) {
887 delegate_->OnSendAcknowledged(app_id, message_id);
888 } else {
889 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
893 void GCMClientImpl::OnMCSError() {
894 // TODO(fgorski): For now it replaces the initialization method. Long term it
895 // should have an error or status passed in.
898 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
899 DCHECK(delegate_);
901 const mcs_proto::DataMessageStanza& data_message_stanza =
902 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
903 message.GetProtobuf());
904 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
906 // Copying all the data from the stanza to a MessageData object. When present,
907 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
908 // later.
909 MessageData message_data;
910 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
911 std::string key = data_message_stanza.app_data(i).key();
912 message_data[key] = data_message_stanza.app_data(i).value();
915 MessageType message_type = DATA_MESSAGE;
916 MessageData::iterator iter = message_data.find(kMessageTypeKey);
917 if (iter != message_data.end()) {
918 message_type = DecodeMessageType(iter->second);
919 message_data.erase(iter);
922 switch (message_type) {
923 case DATA_MESSAGE:
924 HandleIncomingDataMessage(data_message_stanza, message_data);
925 break;
926 case DELETED_MESSAGES:
927 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
928 data_message_stanza.from(),
929 data_message_stanza.ByteSize(),
930 true,
931 GCMStatsRecorder::DELETED_MESSAGES);
932 delegate_->OnMessagesDeleted(data_message_stanza.category());
933 break;
934 case SEND_ERROR:
935 HandleIncomingSendError(data_message_stanza, message_data);
936 break;
937 case UNKNOWN:
938 default: // Treat default the same as UNKNOWN.
939 DVLOG(1) << "Unknown message_type received. Message ignored. "
940 << "App ID: " << data_message_stanza.category() << ".";
941 break;
945 void GCMClientImpl::HandleIncomingDataMessage(
946 const mcs_proto::DataMessageStanza& data_message_stanza,
947 MessageData& message_data) {
948 std::string app_id = data_message_stanza.category();
950 // Drop the message when the app is not registered for the sender of the
951 // message.
952 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
953 bool not_registered =
954 iter == registrations_.end() ||
955 std::find(iter->second->sender_ids.begin(),
956 iter->second->sender_ids.end(),
957 data_message_stanza.from()) == iter->second->sender_ids.end();
958 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
959 data_message_stanza.ByteSize(), !not_registered,
960 GCMStatsRecorder::DATA_MESSAGE);
961 if (not_registered) {
962 return;
965 IncomingMessage incoming_message;
966 incoming_message.sender_id = data_message_stanza.from();
967 if (data_message_stanza.has_token())
968 incoming_message.collapse_key = data_message_stanza.token();
969 incoming_message.data = message_data;
970 delegate_->OnMessageReceived(app_id, incoming_message);
973 void GCMClientImpl::HandleIncomingSendError(
974 const mcs_proto::DataMessageStanza& data_message_stanza,
975 MessageData& message_data) {
976 SendErrorDetails send_error_details;
977 send_error_details.additional_data = message_data;
978 send_error_details.result = SERVER_ERROR;
980 MessageData::iterator iter =
981 send_error_details.additional_data.find(kSendErrorMessageIdKey);
982 if (iter != send_error_details.additional_data.end()) {
983 send_error_details.message_id = iter->second;
984 send_error_details.additional_data.erase(iter);
987 recorder_.RecordIncomingSendError(
988 data_message_stanza.category(),
989 data_message_stanza.to(),
990 data_message_stanza.id());
991 delegate_->OnMessageSendError(data_message_stanza.category(),
992 send_error_details);
995 } // namespace gcm