cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blob1a95809f6fb1b7832cd09af1d2ea0f3326498dbb
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/location.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/time/default_clock.h"
20 #include "base/timer/timer.h"
21 #include "components/gcm_driver/gcm_account_mapper.h"
22 #include "components/gcm_driver/gcm_backoff_policy.h"
23 #include "google_apis/gcm/base/encryptor.h"
24 #include "google_apis/gcm/base/mcs_message.h"
25 #include "google_apis/gcm/base/mcs_util.h"
26 #include "google_apis/gcm/engine/checkin_request.h"
27 #include "google_apis/gcm/engine/connection_factory_impl.h"
28 #include "google_apis/gcm/engine/gcm_registration_request_handler.h"
29 #include "google_apis/gcm/engine/gcm_store_impl.h"
30 #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h"
31 #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h"
32 #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h"
33 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
34 #include "google_apis/gcm/protocol/checkin.pb.h"
35 #include "google_apis/gcm/protocol/mcs.pb.h"
36 #include "net/http/http_network_session.h"
37 #include "net/http/http_transaction_factory.h"
38 #include "net/url_request/url_request_context.h"
39 #include "url/gurl.h"
41 namespace gcm {
43 namespace {
45 // Indicates a message type of the received message.
46 enum MessageType {
47 UNKNOWN, // Undetermined type.
48 DATA_MESSAGE, // Regular data message.
49 DELETED_MESSAGES, // Messages were deleted on the server.
50 SEND_ERROR, // Error sending a message.
53 enum OutgoingMessageTTLCategory {
54 TTL_ZERO,
55 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
56 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
57 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
58 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
59 TTL_MORE_THAN_ONE_WEEK,
60 TTL_MAXIMUM,
61 // NOTE: always keep this entry at the end. Add new TTL category only
62 // immediately above this line. Make sure to update the corresponding
63 // histogram enum accordingly.
64 TTL_CATEGORY_COUNT
67 enum ResetStoreError {
68 DESTROYING_STORE_FAILED,
69 INFINITE_STORE_RESET,
70 // NOTE: always keep this entry at the end. Add new value only immediately
71 // above this line. Make sure to update the corresponding histogram enum
72 // accordingly.
73 RESET_STORE_ERROR_COUNT
76 const char kGCMScope[] = "GCM";
77 const int kMaxRegistrationRetries = 5;
78 const int kMaxUnregistrationRetries = 5;
79 const char kMessageTypeDataMessage[] = "gcm";
80 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
81 const char kMessageTypeKey[] = "message_type";
82 const char kMessageTypeSendErrorKey[] = "send_error";
83 const char kSendErrorMessageIdKey[] = "google.message_id";
84 const char kSendMessageFromValue[] = "gcm@chrome.com";
85 const int64 kDefaultUserSerialNumber = 0LL;
86 const int kDestroyGCMStoreDelayMS = 5 * 60 * 1000; // 5 minutes.
88 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
89 switch (status) {
90 case MCSClient::QUEUED:
91 return GCMClient::SUCCESS;
92 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
93 return GCMClient::NETWORK_ERROR;
94 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
95 return GCMClient::NETWORK_ERROR;
96 case MCSClient::MESSAGE_TOO_LARGE:
97 return GCMClient::INVALID_PARAMETER;
98 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
99 return GCMClient::NETWORK_ERROR;
100 case MCSClient::TTL_EXCEEDED:
101 return GCMClient::NETWORK_ERROR;
102 case MCSClient::SENT:
103 default:
104 NOTREACHED();
105 break;
107 return GCMClientImpl::UNKNOWN_ERROR;
110 void ToCheckinProtoVersion(
111 const GCMClient::ChromeBuildInfo& chrome_build_info,
112 checkin_proto::ChromeBuildProto* android_build_info) {
113 checkin_proto::ChromeBuildProto_Platform platform;
114 switch (chrome_build_info.platform) {
115 case GCMClient::PLATFORM_WIN:
116 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
117 break;
118 case GCMClient::PLATFORM_MAC:
119 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
120 break;
121 case GCMClient::PLATFORM_LINUX:
122 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
123 break;
124 case GCMClient::PLATFORM_IOS:
125 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
126 break;
127 case GCMClient::PLATFORM_ANDROID:
128 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
129 break;
130 case GCMClient::PLATFORM_CROS:
131 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
132 break;
133 case GCMClient::PLATFORM_UNKNOWN:
134 // For unknown platform, return as LINUX.
135 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
136 break;
137 default:
138 NOTREACHED();
139 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
140 break;
142 android_build_info->set_platform(platform);
144 checkin_proto::ChromeBuildProto_Channel channel;
145 switch (chrome_build_info.channel) {
146 case GCMClient::CHANNEL_STABLE:
147 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
148 break;
149 case GCMClient::CHANNEL_BETA:
150 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
151 break;
152 case GCMClient::CHANNEL_DEV:
153 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
154 break;
155 case GCMClient::CHANNEL_CANARY:
156 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
157 break;
158 case GCMClient::CHANNEL_UNKNOWN:
159 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
160 break;
161 default:
162 NOTREACHED();
163 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
164 break;
166 android_build_info->set_channel(channel);
168 android_build_info->set_chrome_version(chrome_build_info.version);
171 MessageType DecodeMessageType(const std::string& value) {
172 if (kMessageTypeDeletedMessagesKey == value)
173 return DELETED_MESSAGES;
174 if (kMessageTypeSendErrorKey == value)
175 return SEND_ERROR;
176 if (kMessageTypeDataMessage == value)
177 return DATA_MESSAGE;
178 return UNKNOWN;
181 int ConstructGCMVersion(const std::string& chrome_version) {
182 // Major Chrome version is passed as GCM version.
183 size_t pos = chrome_version.find('.');
184 if (pos == std::string::npos) {
185 NOTREACHED();
186 return 0;
189 int gcm_version = 0;
190 base::StringToInt(
191 base::StringPiece(chrome_version.c_str(), pos), &gcm_version);
192 return gcm_version;
195 std::string SerializeInstanceIDData(const std::string& instance_id,
196 const std::string& extra_data) {
197 DCHECK(!instance_id.empty() && !extra_data.empty());
198 DCHECK(instance_id.find(',') == std::string::npos);
199 return instance_id + "," + extra_data;
202 bool DeserializeInstanceIDData(const std::string& serialized_data,
203 std::string* instance_id,
204 std::string* extra_data) {
205 DCHECK(instance_id && extra_data);
206 std::size_t pos = serialized_data.find(',');
207 if (pos == std::string::npos)
208 return false;
209 *instance_id = serialized_data.substr(0, pos);
210 *extra_data = serialized_data.substr(pos + 1);
211 return !instance_id->empty() && !extra_data->empty();
214 void RecordOutgoingMessageToUMA(const gcm::OutgoingMessage& message) {
215 OutgoingMessageTTLCategory ttl_category;
216 if (message.time_to_live == 0)
217 ttl_category = TTL_ZERO;
218 else if (message.time_to_live <= 60 )
219 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
220 else if (message.time_to_live <= 60 * 60)
221 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
222 else if (message.time_to_live <= 24 * 60 * 60)
223 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
224 else
225 ttl_category = TTL_MAXIMUM;
227 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
228 ttl_category,
229 TTL_CATEGORY_COUNT);
232 void RecordResetStoreErrorToUMA(ResetStoreError error) {
233 UMA_HISTOGRAM_ENUMERATION("GCM.ResetStore", error, RESET_STORE_ERROR_COUNT);
236 } // namespace
238 GCMInternalsBuilder::GCMInternalsBuilder() {}
239 GCMInternalsBuilder::~GCMInternalsBuilder() {}
241 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
242 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
245 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
246 const std::string& version,
247 base::Clock* clock,
248 ConnectionFactory* connection_factory,
249 GCMStore* gcm_store,
250 GCMStatsRecorder* recorder) {
251 return scoped_ptr<MCSClient>(new MCSClient(
252 version, clock, connection_factory, gcm_store, recorder));
255 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
256 const std::vector<GURL>& endpoints,
257 const net::BackoffEntry::Policy& backoff_policy,
258 const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
259 const scoped_refptr<net::HttpNetworkSession>& http_network_session,
260 net::NetLog* net_log,
261 GCMStatsRecorder* recorder) {
262 return make_scoped_ptr<ConnectionFactory>(
263 new ConnectionFactoryImpl(endpoints,
264 backoff_policy,
265 gcm_network_session,
266 http_network_session,
267 net_log,
268 recorder));
271 GCMClientImpl::CheckinInfo::CheckinInfo()
272 : android_id(0), secret(0), accounts_set(false) {
275 GCMClientImpl::CheckinInfo::~CheckinInfo() {
278 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
279 last_checkin_accounts.clear();
280 for (std::map<std::string, std::string>::iterator iter =
281 account_tokens.begin();
282 iter != account_tokens.end();
283 ++iter) {
284 last_checkin_accounts.insert(iter->first);
288 void GCMClientImpl::CheckinInfo::Reset() {
289 android_id = 0;
290 secret = 0;
291 accounts_set = false;
292 account_tokens.clear();
293 last_checkin_accounts.clear();
296 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
297 : internals_builder_(internals_builder.Pass()),
298 state_(UNINITIALIZED),
299 delegate_(NULL),
300 start_mode_(DELAYED_START),
301 clock_(internals_builder_->BuildClock()),
302 gcm_store_reset_(false),
303 url_request_context_getter_(NULL),
304 periodic_checkin_ptr_factory_(this),
305 destroying_gcm_store_ptr_factory_(this),
306 weak_ptr_factory_(this) {
309 GCMClientImpl::~GCMClientImpl() {
312 void GCMClientImpl::Initialize(
313 const ChromeBuildInfo& chrome_build_info,
314 const base::FilePath& path,
315 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
316 const scoped_refptr<net::URLRequestContextGetter>&
317 url_request_context_getter,
318 scoped_ptr<Encryptor> encryptor,
319 GCMClient::Delegate* delegate) {
320 DCHECK_EQ(UNINITIALIZED, state_);
321 DCHECK(url_request_context_getter.get());
322 DCHECK(delegate);
324 url_request_context_getter_ = url_request_context_getter;
325 const net::HttpNetworkSession::Params* network_session_params =
326 url_request_context_getter_->GetURLRequestContext()->
327 GetNetworkSessionParams();
328 DCHECK(network_session_params);
329 network_session_ = new net::HttpNetworkSession(*network_session_params);
331 chrome_build_info_ = chrome_build_info;
333 gcm_store_.reset(
334 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
336 delegate_ = delegate;
338 recorder_.SetDelegate(this);
340 state_ = INITIALIZED;
343 void GCMClientImpl::Start(StartMode start_mode) {
344 DCHECK_NE(UNINITIALIZED, state_);
346 if (state_ == LOADED) {
347 // Start the GCM if not yet.
348 if (start_mode == IMMEDIATE_START) {
349 // Give up the scheduling to wipe out the store since now some one starts
350 // to use GCM.
351 destroying_gcm_store_ptr_factory_.InvalidateWeakPtrs();
353 StartGCM();
355 return;
358 // The delay start behavior will be abandoned when Start has been called
359 // once with IMMEDIATE_START behavior.
360 if (start_mode == IMMEDIATE_START)
361 start_mode_ = IMMEDIATE_START;
363 // Bail out if the loading is not started or completed.
364 if (state_ != INITIALIZED)
365 return;
367 // Once the loading is completed, the check-in will be initiated.
368 // If we're in lazy start mode, don't create a new store since none is really
369 // using GCM functionality yet.
370 gcm_store_->Load(
371 (start_mode == IMMEDIATE_START) ?
372 GCMStore::CREATE_IF_MISSING :
373 GCMStore::DO_NOT_CREATE,
374 base::Bind(&GCMClientImpl::OnLoadCompleted,
375 weak_ptr_factory_.GetWeakPtr()));
376 state_ = LOADING;
379 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
380 DCHECK_EQ(LOADING, state_);
382 if (!result->success) {
383 if (result->store_does_not_exist) {
384 if (start_mode_ == IMMEDIATE_START) {
385 // An immediate start was requested during the delayed start that just
386 // completed. Perform it now.
387 gcm_store_->Load(GCMStore::CREATE_IF_MISSING,
388 base::Bind(&GCMClientImpl::OnLoadCompleted,
389 weak_ptr_factory_.GetWeakPtr()));
390 } else {
391 // In the case that the store does not exist, set |state_| back to
392 // INITIALIZED such that store loading could be triggered again when
393 // Start() is called with IMMEDIATE_START.
394 state_ = INITIALIZED;
396 } else {
397 // Otherwise, destroy the store to try again.
398 ResetStore();
400 return;
402 gcm_store_reset_ = false;
404 device_checkin_info_.android_id = result->device_android_id;
405 device_checkin_info_.secret = result->device_security_token;
406 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
407 // A case where there were previously no accounts reported with checkin is
408 // considered to be the same as when the list of accounts is empty. It enables
409 // scheduling a periodic checkin for devices with no signed in users
410 // immediately after restart, while keeping |accounts_set == false| delays the
411 // checkin until the list of accounts is set explicitly.
412 if (result->last_checkin_accounts.size() == 0)
413 device_checkin_info_.accounts_set = true;
414 last_checkin_time_ = result->last_checkin_time;
415 gservices_settings_.UpdateFromLoadResult(*result);
417 for (auto iter = result->registrations.begin();
418 iter != result->registrations.end();
419 ++iter) {
420 std::string registration_id;
421 scoped_ptr<RegistrationInfo> registration =
422 RegistrationInfo::BuildFromString(
423 iter->first, iter->second, &registration_id);
424 // TODO(jianli): Add UMA to track the error case.
425 if (registration.get())
426 registrations_[make_linked_ptr(registration.release())] = registration_id;
429 for (auto iter = result->instance_id_data.begin();
430 iter != result->instance_id_data.end();
431 ++iter) {
432 std::string instance_id;
433 std::string extra_data;
434 if (DeserializeInstanceIDData(iter->second, &instance_id, &extra_data))
435 instance_id_data_[iter->first] = std::make_pair(instance_id, extra_data);
438 load_result_ = result.Pass();
439 state_ = LOADED;
441 // Don't initiate the GCM connection when GCM is in delayed start mode and
442 // not any standalone app has registered GCM yet.
443 if (start_mode_ == DELAYED_START && !HasStandaloneRegisteredApp()) {
444 // If no standalone app is using GCM and the device ID is present, schedule
445 // to have the store wiped out.
446 if (device_checkin_info_.android_id) {
447 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
448 FROM_HERE, base::Bind(&GCMClientImpl::DestroyStoreWhenNotNeeded,
449 destroying_gcm_store_ptr_factory_.GetWeakPtr()),
450 base::TimeDelta::FromMilliseconds(kDestroyGCMStoreDelayMS));
453 return;
456 StartGCM();
459 void GCMClientImpl::StartGCM() {
460 // Taking over the value of account_mappings before passing the ownership of
461 // load result to InitializeMCSClient.
462 std::vector<AccountMapping> account_mappings;
463 account_mappings.swap(load_result_->account_mappings);
464 base::Time last_token_fetch_time = load_result_->last_token_fetch_time;
466 InitializeMCSClient();
468 if (device_checkin_info_.IsValid()) {
469 SchedulePeriodicCheckin();
470 OnReady(account_mappings, last_token_fetch_time);
471 return;
474 state_ = INITIAL_DEVICE_CHECKIN;
475 device_checkin_info_.Reset();
476 StartCheckin();
479 void GCMClientImpl::InitializeMCSClient() {
480 std::vector<GURL> endpoints;
481 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
482 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
483 connection_factory_ = internals_builder_->BuildConnectionFactory(
484 endpoints,
485 GetGCMBackoffPolicy(),
486 network_session_,
487 url_request_context_getter_->GetURLRequestContext()
488 ->http_transaction_factory()
489 ->GetSession(),
490 net_log_.net_log(),
491 &recorder_);
492 connection_factory_->SetConnectionListener(this);
493 mcs_client_ = internals_builder_->BuildMCSClient(
494 chrome_build_info_.version,
495 clock_.get(),
496 connection_factory_.get(),
497 gcm_store_.get(),
498 &recorder_).Pass();
500 mcs_client_->Initialize(
501 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
502 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
503 weak_ptr_factory_.GetWeakPtr()),
504 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
505 weak_ptr_factory_.GetWeakPtr()),
506 load_result_.Pass());
509 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
510 const CheckinInfo& checkin_info) {
511 DCHECK(!device_checkin_info_.IsValid());
513 device_checkin_info_.android_id = checkin_info.android_id;
514 device_checkin_info_.secret = checkin_info.secret;
515 // If accounts were not set by now, we can consider them set (to empty list)
516 // to make sure periodic checkins get scheduled after initial checkin.
517 device_checkin_info_.accounts_set = true;
518 gcm_store_->SetDeviceCredentials(
519 checkin_info.android_id, checkin_info.secret,
520 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
521 weak_ptr_factory_.GetWeakPtr()));
523 OnReady(std::vector<AccountMapping>(), base::Time());
526 void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
527 const base::Time& last_token_fetch_time) {
528 state_ = READY;
529 StartMCSLogin();
531 delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
534 void GCMClientImpl::StartMCSLogin() {
535 DCHECK_EQ(READY, state_);
536 DCHECK(device_checkin_info_.IsValid());
537 mcs_client_->Login(device_checkin_info_.android_id,
538 device_checkin_info_.secret);
541 void GCMClientImpl::DestroyStoreWhenNotNeeded() {
542 if (state_ != LOADED || start_mode_ != DELAYED_START)
543 return;
545 gcm_store_->Destroy(base::Bind(&GCMClientImpl::DestroyStoreCallback,
546 weak_ptr_factory_.GetWeakPtr()));
549 void GCMClientImpl::ResetStore() {
550 DCHECK_EQ(LOADING, state_);
552 // If already being reset, don't do it again. We want to prevent from
553 // resetting and loading from the store again and again.
554 if (gcm_store_reset_) {
555 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET);
556 state_ = UNINITIALIZED;
557 return;
559 gcm_store_reset_ = true;
561 // Destroy the GCM store to start over.
562 gcm_store_->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback,
563 weak_ptr_factory_.GetWeakPtr()));
566 void GCMClientImpl::SetAccountTokens(
567 const std::vector<AccountTokenInfo>& account_tokens) {
568 device_checkin_info_.account_tokens.clear();
569 for (std::vector<AccountTokenInfo>::const_iterator iter =
570 account_tokens.begin();
571 iter != account_tokens.end();
572 ++iter) {
573 device_checkin_info_.account_tokens[iter->email] = iter->access_token;
576 bool accounts_set_before = device_checkin_info_.accounts_set;
577 device_checkin_info_.accounts_set = true;
579 DVLOG(1) << "Set account called with: " << account_tokens.size()
580 << " accounts.";
582 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
583 return;
585 bool account_removed = false;
586 for (std::set<std::string>::iterator iter =
587 device_checkin_info_.last_checkin_accounts.begin();
588 iter != device_checkin_info_.last_checkin_accounts.end();
589 ++iter) {
590 if (device_checkin_info_.account_tokens.find(*iter) ==
591 device_checkin_info_.account_tokens.end()) {
592 account_removed = true;
596 // Checkin will be forced when any of the accounts was removed during the
597 // current Chrome session or if there has been an account removed between the
598 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
599 // We only force checkin when user signs out. When there is a new account
600 // signed in, the periodic checkin will take care of adding the association in
601 // reasonable time.
602 if (account_removed) {
603 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
604 checkin_request_.reset();
605 StartCheckin();
606 } else if (!accounts_set_before) {
607 SchedulePeriodicCheckin();
608 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
612 void GCMClientImpl::UpdateAccountMapping(
613 const AccountMapping& account_mapping) {
614 gcm_store_->AddAccountMapping(account_mapping,
615 base::Bind(&GCMClientImpl::DefaultStoreCallback,
616 weak_ptr_factory_.GetWeakPtr()));
619 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
620 gcm_store_->RemoveAccountMapping(
621 account_id,
622 base::Bind(&GCMClientImpl::DefaultStoreCallback,
623 weak_ptr_factory_.GetWeakPtr()));
626 void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
627 gcm_store_->SetLastTokenFetchTime(
628 time,
629 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
630 weak_ptr_factory_.GetWeakPtr()));
633 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
634 DCHECK(mcs_client_);
635 mcs_client_->UpdateHeartbeatTimer(timer.Pass());
638 void GCMClientImpl::AddInstanceIDData(const std::string& app_id,
639 const std::string& instance_id,
640 const std::string& extra_data) {
641 instance_id_data_[app_id] = std::make_pair(instance_id, extra_data);
642 gcm_store_->AddInstanceIDData(
643 app_id,
644 SerializeInstanceIDData(instance_id, extra_data),
645 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
646 weak_ptr_factory_.GetWeakPtr()));
649 void GCMClientImpl::RemoveInstanceIDData(const std::string& app_id) {
650 instance_id_data_.erase(app_id);
651 gcm_store_->RemoveInstanceIDData(
652 app_id,
653 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
654 weak_ptr_factory_.GetWeakPtr()));
657 void GCMClientImpl::GetInstanceIDData(const std::string& app_id,
658 std::string* instance_id,
659 std::string* extra_data) {
660 DCHECK(instance_id && extra_data);
662 auto iter = instance_id_data_.find(app_id);
663 if (iter == instance_id_data_.end())
664 return;
665 *instance_id = iter->second.first;
666 *extra_data = iter->second.second;
669 void GCMClientImpl::AddHeartbeatInterval(const std::string& scope,
670 int interval_ms) {
671 DCHECK(mcs_client_);
672 mcs_client_->AddHeartbeatInterval(scope, interval_ms);
675 void GCMClientImpl::RemoveHeartbeatInterval(const std::string& scope) {
676 DCHECK(mcs_client_);
677 mcs_client_->RemoveHeartbeatInterval(scope);
680 void GCMClientImpl::StartCheckin() {
681 // Make sure no checkin is in progress.
682 if (checkin_request_.get())
683 return;
685 checkin_proto::ChromeBuildProto chrome_build_proto;
686 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
687 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
688 device_checkin_info_.secret,
689 device_checkin_info_.account_tokens,
690 gservices_settings_.digest(),
691 chrome_build_proto);
692 checkin_request_.reset(
693 new CheckinRequest(gservices_settings_.GetCheckinURL(),
694 request_info,
695 GetGCMBackoffPolicy(),
696 base::Bind(&GCMClientImpl::OnCheckinCompleted,
697 weak_ptr_factory_.GetWeakPtr()),
698 url_request_context_getter_.get(),
699 &recorder_));
700 // Taking a snapshot of the accounts count here, as there might be an asynch
701 // update of the account tokens while checkin is in progress.
702 device_checkin_info_.SnapshotCheckinAccounts();
703 checkin_request_->Start();
706 void GCMClientImpl::OnCheckinCompleted(
707 const checkin_proto::AndroidCheckinResponse& checkin_response) {
708 checkin_request_.reset();
710 if (!checkin_response.has_android_id() ||
711 !checkin_response.has_security_token()) {
712 // TODO(fgorski): I don't think a retry here will help, we should probably
713 // start over. By checking in with (0, 0).
714 return;
717 CheckinInfo checkin_info;
718 checkin_info.android_id = checkin_response.android_id();
719 checkin_info.secret = checkin_response.security_token();
721 if (state_ == INITIAL_DEVICE_CHECKIN) {
722 OnFirstTimeDeviceCheckinCompleted(checkin_info);
723 } else {
724 // checkin_info is not expected to change after a periodic checkin as it
725 // would invalidate the registratoin IDs.
726 DCHECK_EQ(READY, state_);
727 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
728 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
731 if (device_checkin_info_.IsValid()) {
732 // First update G-services settings, as something might have changed.
733 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
734 gcm_store_->SetGServicesSettings(
735 gservices_settings_.settings_map(),
736 gservices_settings_.digest(),
737 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
738 weak_ptr_factory_.GetWeakPtr()));
741 last_checkin_time_ = clock_->Now();
742 gcm_store_->SetLastCheckinInfo(
743 last_checkin_time_,
744 device_checkin_info_.last_checkin_accounts,
745 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
746 weak_ptr_factory_.GetWeakPtr()));
747 SchedulePeriodicCheckin();
751 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
752 DCHECK(success);
755 void GCMClientImpl::SchedulePeriodicCheckin() {
756 // Make sure no checkin is in progress.
757 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
758 return;
760 // There should be only one periodic checkin pending at a time. Removing
761 // pending periodic checkin to schedule a new one.
762 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
764 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
765 if (time_to_next_checkin < base::TimeDelta())
766 time_to_next_checkin = base::TimeDelta();
768 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
769 FROM_HERE, base::Bind(&GCMClientImpl::StartCheckin,
770 periodic_checkin_ptr_factory_.GetWeakPtr()),
771 time_to_next_checkin);
774 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
775 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
776 clock_->Now();
779 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
780 // TODO(fgorski): This is one of the signals that store needs a rebuild.
781 DCHECK(success);
784 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
785 // TODO(fgorski): This is one of the signals that store needs a rebuild.
786 DCHECK(success);
789 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
790 // TODO(fgorski): This is one of the signals that store needs a rebuild.
791 DCHECK(success);
794 void GCMClientImpl::DefaultStoreCallback(bool success) {
795 DCHECK(success);
798 void GCMClientImpl::IgnoreWriteResultCallback(bool success) {
799 // TODO(fgorski): Ignoring the write result for now to make sure
800 // sync_intergration_tests are not broken.
803 void GCMClientImpl::DestroyStoreCallback(bool success) {
804 ResetCache();
806 if (!success) {
807 LOG(ERROR) << "Failed to destroy GCM store";
808 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
809 state_ = UNINITIALIZED;
810 return;
813 state_ = INITIALIZED;
816 void GCMClientImpl::ResetStoreCallback(bool success) {
817 if (!success) {
818 LOG(ERROR) << "Failed to reset GCM store";
819 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
820 state_ = UNINITIALIZED;
821 return;
824 state_ = INITIALIZED;
825 Start(start_mode_);
828 void GCMClientImpl::Stop() {
829 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
830 // Shutdown.
831 DVLOG(1) << "Stopping the GCM Client";
832 ResetCache();
833 state_ = INITIALIZED;
834 gcm_store_->Close();
837 void GCMClientImpl::ResetCache() {
838 weak_ptr_factory_.InvalidateWeakPtrs();
839 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
840 device_checkin_info_.Reset();
841 connection_factory_.reset();
842 delegate_->OnDisconnected();
843 mcs_client_.reset();
844 checkin_request_.reset();
845 // Delete all of the pending registration and unregistration requests.
846 pending_registration_requests_.clear();
847 pending_unregistration_requests_.clear();
850 void GCMClientImpl::Register(
851 const linked_ptr<RegistrationInfo>& registration_info) {
852 DCHECK_EQ(state_, READY);
854 // Find and use the cached registration ID.
855 RegistrationInfoMap::const_iterator registrations_iter =
856 registrations_.find(registration_info);
857 if (registrations_iter != registrations_.end()) {
858 bool matched = true;
860 // For GCM registration, we also match the sender IDs since multiple
861 // registrations are not supported.
862 const GCMRegistrationInfo* gcm_registration_info =
863 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
864 if (gcm_registration_info) {
865 const GCMRegistrationInfo* cached_gcm_registration_info =
866 GCMRegistrationInfo::FromRegistrationInfo(
867 registrations_iter->first.get());
868 DCHECK(cached_gcm_registration_info);
869 if (cached_gcm_registration_info &&
870 gcm_registration_info->sender_ids !=
871 cached_gcm_registration_info->sender_ids) {
872 matched = false;
876 if (matched) {
877 delegate_->OnRegisterFinished(
878 registration_info, registrations_iter->second, SUCCESS);
879 return;
883 scoped_ptr<RegistrationRequest::CustomRequestHandler> request_handler;
884 std::string source_to_record;
886 const GCMRegistrationInfo* gcm_registration_info =
887 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
888 if (gcm_registration_info) {
889 std::string senders;
890 for (auto iter = gcm_registration_info->sender_ids.begin();
891 iter != gcm_registration_info->sender_ids.end();
892 ++iter) {
893 if (!senders.empty())
894 senders.append(",");
895 senders.append(*iter);
897 UMA_HISTOGRAM_COUNTS("GCM.RegistrationSenderIdCount",
898 gcm_registration_info->sender_ids.size());
900 request_handler.reset(new GCMRegistrationRequestHandler(senders));
901 source_to_record = senders;
904 const InstanceIDTokenInfo* instance_id_token_info =
905 InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
906 if (instance_id_token_info) {
907 auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
908 DCHECK(instance_id_iter != instance_id_data_.end());
910 request_handler.reset(new InstanceIDGetTokenRequestHandler(
911 instance_id_iter->second.first,
912 instance_id_token_info->authorized_entity,
913 instance_id_token_info->scope,
914 ConstructGCMVersion(chrome_build_info_.version),
915 instance_id_token_info->options));
916 source_to_record = instance_id_token_info->authorized_entity + "/" +
917 instance_id_token_info->scope;
920 RegistrationRequest::RequestInfo request_info(
921 device_checkin_info_.android_id,
922 device_checkin_info_.secret,
923 registration_info->app_id);
925 scoped_ptr<RegistrationRequest> registration_request(new RegistrationRequest(
926 gservices_settings_.GetRegistrationURL(), request_info,
927 request_handler.Pass(), GetGCMBackoffPolicy(),
928 base::Bind(&GCMClientImpl::OnRegisterCompleted,
929 weak_ptr_factory_.GetWeakPtr(), registration_info),
930 kMaxRegistrationRetries, url_request_context_getter_, &recorder_,
931 source_to_record));
932 registration_request->Start();
933 pending_registration_requests_.insert(registration_info,
934 registration_request.Pass());
937 void GCMClientImpl::OnRegisterCompleted(
938 const linked_ptr<RegistrationInfo>& registration_info,
939 RegistrationRequest::Status status,
940 const std::string& registration_id) {
941 DCHECK(delegate_);
943 Result result;
944 PendingRegistrationRequests::const_iterator iter =
945 pending_registration_requests_.find(registration_info);
946 if (iter == pending_registration_requests_.end())
947 result = UNKNOWN_ERROR;
948 else if (status == RegistrationRequest::INVALID_SENDER)
949 result = INVALID_PARAMETER;
950 else if (registration_id.empty())
951 result = SERVER_ERROR;
952 else
953 result = SUCCESS;
955 if (result == SUCCESS) {
956 // Cache it.
957 // Note that the existing cached record has to be removed first because
958 // otherwise the key value in registrations_ will not be updated. For GCM
959 // registrations, the key consists of pair of app_id and sender_ids though
960 // only app_id is used in the comparison.
961 registrations_.erase(registration_info);
962 registrations_[registration_info] = registration_id;
964 // Save it in the persistent store.
965 gcm_store_->AddRegistration(
966 registration_info->GetSerializedKey(),
967 registration_info->GetSerializedValue(registration_id),
968 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
969 weak_ptr_factory_.GetWeakPtr()));
972 delegate_->OnRegisterFinished(
973 registration_info,
974 result == SUCCESS ? registration_id : std::string(),
975 result);
977 if (iter != pending_registration_requests_.end())
978 pending_registration_requests_.erase(iter);
981 void GCMClientImpl::Unregister(
982 const linked_ptr<RegistrationInfo>& registration_info) {
983 DCHECK_EQ(state_, READY);
985 scoped_ptr<UnregistrationRequest::CustomRequestHandler> request_handler;
986 std::string source_to_record;
988 const GCMRegistrationInfo* gcm_registration_info =
989 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
990 if (gcm_registration_info) {
991 request_handler.reset(
992 new GCMUnregistrationRequestHandler(registration_info->app_id));
995 const InstanceIDTokenInfo* instance_id_token_info =
996 InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
997 if (instance_id_token_info) {
998 auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
999 if (instance_id_iter == instance_id_data_.end()) {
1000 // This should not be reached since we should not delete tokens when
1001 // an InstanceID has not been created yet.
1002 NOTREACHED();
1003 return;
1005 request_handler.reset(new InstanceIDDeleteTokenRequestHandler(
1006 instance_id_iter->second.first,
1007 instance_id_token_info->authorized_entity,
1008 instance_id_token_info->scope,
1009 ConstructGCMVersion(chrome_build_info_.version)));
1010 source_to_record = instance_id_token_info->authorized_entity + "/" +
1011 instance_id_token_info->scope;
1014 // Remove the registration/token(s) from the cache and the store.
1015 // TODO(jianli): Remove it only when the request is successful.
1016 if (instance_id_token_info &&
1017 instance_id_token_info->authorized_entity == "*" &&
1018 instance_id_token_info->scope == "*") {
1019 // If authorized_entity and scope are '*', find and remove all associated
1020 // tokens.
1021 bool token_found = false;
1022 for (auto iter = registrations_.begin();
1023 iter != registrations_.end();) {
1024 InstanceIDTokenInfo* cached_instance_id_token_info =
1025 InstanceIDTokenInfo::FromRegistrationInfo(iter->first.get());
1026 if (cached_instance_id_token_info &&
1027 cached_instance_id_token_info->app_id == registration_info->app_id) {
1028 token_found = true;
1029 gcm_store_->RemoveRegistration(
1030 cached_instance_id_token_info->GetSerializedKey(),
1031 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
1032 weak_ptr_factory_.GetWeakPtr()));
1033 registrations_.erase(iter++);
1034 } else {
1035 ++iter;
1039 // If no token is found for the Instance ID, don't need to unregister
1040 // since the Instance ID is not sent to the server yet.
1041 if (!token_found) {
1042 OnUnregisterCompleted(registration_info,
1043 UnregistrationRequest::SUCCESS);
1044 return;
1046 } else {
1047 auto iter = registrations_.find(registration_info);
1048 if (iter == registrations_.end()) {
1049 delegate_->OnUnregisterFinished(registration_info, INVALID_PARAMETER);
1050 return;
1052 registrations_.erase(iter);
1054 gcm_store_->RemoveRegistration(
1055 registration_info->GetSerializedKey(),
1056 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
1057 weak_ptr_factory_.GetWeakPtr()));
1060 UnregistrationRequest::RequestInfo request_info(
1061 device_checkin_info_.android_id,
1062 device_checkin_info_.secret,
1063 registration_info->app_id);
1065 scoped_ptr<UnregistrationRequest> unregistration_request(
1066 new UnregistrationRequest(
1067 gservices_settings_.GetRegistrationURL(), request_info,
1068 request_handler.Pass(), GetGCMBackoffPolicy(),
1069 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
1070 weak_ptr_factory_.GetWeakPtr(), registration_info),
1071 kMaxUnregistrationRetries, url_request_context_getter_, &recorder_,
1072 source_to_record));
1073 unregistration_request->Start();
1074 pending_unregistration_requests_.insert(registration_info,
1075 unregistration_request.Pass());
1078 void GCMClientImpl::OnUnregisterCompleted(
1079 const linked_ptr<RegistrationInfo>& registration_info,
1080 UnregistrationRequest::Status status) {
1081 DVLOG(1) << "Unregister completed for app: " << registration_info->app_id
1082 << " with " << (status ? "success." : "failure.");
1084 Result result;
1085 switch (status) {
1086 case UnregistrationRequest::SUCCESS:
1087 result = SUCCESS;
1088 break;
1089 case UnregistrationRequest::INVALID_PARAMETERS:
1090 result = INVALID_PARAMETER;
1091 break;
1092 default:
1093 // All other errors are treated as SERVER_ERROR.
1094 result = SERVER_ERROR;
1095 break;
1097 delegate_->OnUnregisterFinished(registration_info, result);
1099 pending_unregistration_requests_.erase(registration_info);
1102 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
1103 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
1104 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
1107 void GCMClientImpl::Send(const std::string& app_id,
1108 const std::string& receiver_id,
1109 const OutgoingMessage& message) {
1110 DCHECK_EQ(state_, READY);
1112 RecordOutgoingMessageToUMA(message);
1114 mcs_proto::DataMessageStanza stanza;
1115 stanza.set_ttl(message.time_to_live);
1116 stanza.set_sent(clock_->Now().ToInternalValue() /
1117 base::Time::kMicrosecondsPerSecond);
1118 stanza.set_id(message.id);
1119 stanza.set_from(kSendMessageFromValue);
1120 stanza.set_to(receiver_id);
1121 stanza.set_category(app_id);
1123 for (MessageData::const_iterator iter = message.data.begin();
1124 iter != message.data.end();
1125 ++iter) {
1126 mcs_proto::AppData* app_data = stanza.add_app_data();
1127 app_data->set_key(iter->first);
1128 app_data->set_value(iter->second);
1131 MCSMessage mcs_message(stanza);
1132 DVLOG(1) << "MCS message size: " << mcs_message.size();
1133 mcs_client_->SendMessage(mcs_message);
1136 std::string GCMClientImpl::GetStateString() const {
1137 switch(state_) {
1138 case GCMClientImpl::INITIALIZED:
1139 return "INITIALIZED";
1140 case GCMClientImpl::UNINITIALIZED:
1141 return "UNINITIALIZED";
1142 case GCMClientImpl::LOADING:
1143 return "LOADING";
1144 case GCMClientImpl::LOADED:
1145 return "LOADED";
1146 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
1147 return "INITIAL_DEVICE_CHECKIN";
1148 case GCMClientImpl::READY:
1149 return "READY";
1150 default:
1151 NOTREACHED();
1152 return std::string();
1156 void GCMClientImpl::SetRecording(bool recording) {
1157 recorder_.SetRecording(recording);
1160 void GCMClientImpl::ClearActivityLogs() {
1161 recorder_.Clear();
1164 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
1165 GCMClient::GCMStatistics stats;
1166 stats.gcm_client_created = true;
1167 stats.is_recording = recorder_.is_recording();
1168 stats.gcm_client_state = GetStateString();
1169 stats.connection_client_created = mcs_client_.get() != NULL;
1170 if (connection_factory_.get())
1171 stats.connection_state = connection_factory_->GetConnectionStateString();
1172 if (mcs_client_.get()) {
1173 stats.send_queue_size = mcs_client_->GetSendQueueSize();
1174 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
1176 if (device_checkin_info_.android_id > 0)
1177 stats.android_id = device_checkin_info_.android_id;
1178 recorder_.CollectActivities(&stats.recorded_activities);
1180 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
1181 it != registrations_.end(); ++it) {
1182 stats.registered_app_ids.push_back(it->first->app_id);
1184 return stats;
1187 void GCMClientImpl::OnActivityRecorded() {
1188 delegate_->OnActivityRecorded();
1191 void GCMClientImpl::OnConnected(const GURL& current_server,
1192 const net::IPEndPoint& ip_endpoint) {
1193 // TODO(gcm): expose current server in debug page.
1194 delegate_->OnActivityRecorded();
1195 delegate_->OnConnected(ip_endpoint);
1198 void GCMClientImpl::OnDisconnected() {
1199 delegate_->OnActivityRecorded();
1200 delegate_->OnDisconnected();
1203 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
1204 switch (message.tag()) {
1205 case kLoginResponseTag:
1206 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
1207 return;
1208 case kDataMessageStanzaTag:
1209 DVLOG(1) << "A downstream message received. Processing...";
1210 HandleIncomingMessage(message);
1211 return;
1212 default:
1213 NOTREACHED() << "Message with unexpected tag received by GCMClient";
1214 return;
1218 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
1219 const std::string& app_id,
1220 const std::string& message_id,
1221 MCSClient::MessageSendStatus status) {
1222 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
1223 DCHECK(delegate_);
1225 // TTL_EXCEEDED is singled out here, because it can happen long time after the
1226 // message was sent. That is why it comes as |OnMessageSendError| event rather
1227 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
1228 // All other errors will be raised immediately, through asynchronous callback.
1229 // It is expected that TTL_EXCEEDED will be issued for a message that was
1230 // previously issued |OnSendFinished| with status SUCCESS.
1231 // TODO(jianli): Consider adding UMA for this status.
1232 if (status == MCSClient::TTL_EXCEEDED) {
1233 SendErrorDetails send_error_details;
1234 send_error_details.message_id = message_id;
1235 send_error_details.result = GCMClient::TTL_EXCEEDED;
1236 delegate_->OnMessageSendError(app_id, send_error_details);
1237 } else if (status == MCSClient::SENT) {
1238 delegate_->OnSendAcknowledged(app_id, message_id);
1239 } else {
1240 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
1244 void GCMClientImpl::OnMCSError() {
1245 // TODO(fgorski): For now it replaces the initialization method. Long term it
1246 // should have an error or status passed in.
1249 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
1250 DCHECK(delegate_);
1252 const mcs_proto::DataMessageStanza& data_message_stanza =
1253 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
1254 message.GetProtobuf());
1255 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
1257 // Copying all the data from the stanza to a MessageData object. When present,
1258 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
1259 // later.
1260 MessageData message_data;
1261 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
1262 std::string key = data_message_stanza.app_data(i).key();
1263 message_data[key] = data_message_stanza.app_data(i).value();
1266 MessageType message_type = DATA_MESSAGE;
1267 MessageData::iterator iter = message_data.find(kMessageTypeKey);
1268 if (iter != message_data.end()) {
1269 message_type = DecodeMessageType(iter->second);
1270 message_data.erase(iter);
1273 switch (message_type) {
1274 case DATA_MESSAGE:
1275 HandleIncomingDataMessage(data_message_stanza, message_data);
1276 break;
1277 case DELETED_MESSAGES:
1278 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
1279 data_message_stanza.from(),
1280 data_message_stanza.ByteSize(),
1281 true,
1282 GCMStatsRecorder::DELETED_MESSAGES);
1283 delegate_->OnMessagesDeleted(data_message_stanza.category());
1284 break;
1285 case SEND_ERROR:
1286 HandleIncomingSendError(data_message_stanza, message_data);
1287 break;
1288 case UNKNOWN:
1289 default: // Treat default the same as UNKNOWN.
1290 DVLOG(1) << "Unknown message_type received. Message ignored. "
1291 << "App ID: " << data_message_stanza.category() << ".";
1292 break;
1296 void GCMClientImpl::HandleIncomingDataMessage(
1297 const mcs_proto::DataMessageStanza& data_message_stanza,
1298 MessageData& message_data) {
1299 std::string app_id = data_message_stanza.category();
1300 std::string sender = data_message_stanza.from();
1302 // Drop the message when the app is not registered for the sender of the
1303 // message.
1304 bool registered = false;
1306 // First, find among all GCM registrations.
1307 scoped_ptr<GCMRegistrationInfo> gcm_registration(new GCMRegistrationInfo);
1308 gcm_registration->app_id = app_id;
1309 auto gcm_registration_iter = registrations_.find(
1310 make_linked_ptr<RegistrationInfo>(gcm_registration.release()));
1311 if (gcm_registration_iter != registrations_.end()) {
1312 GCMRegistrationInfo* cached_gcm_registration =
1313 GCMRegistrationInfo::FromRegistrationInfo(
1314 gcm_registration_iter->first.get());
1315 if (cached_gcm_registration &&
1316 std::find(cached_gcm_registration->sender_ids.begin(),
1317 cached_gcm_registration->sender_ids.end(),
1318 sender) != cached_gcm_registration->sender_ids.end()) {
1319 registered = true;
1323 // Then, find among all InstanceID registrations.
1324 if (!registered) {
1325 scoped_ptr<InstanceIDTokenInfo> instance_id_token(new InstanceIDTokenInfo);
1326 instance_id_token->app_id = app_id;
1327 instance_id_token->authorized_entity = sender;
1328 instance_id_token->scope = kGCMScope;
1329 auto instance_id_token_iter = registrations_.find(
1330 make_linked_ptr<RegistrationInfo>(instance_id_token.release()));
1331 if (instance_id_token_iter != registrations_.end())
1332 registered = true;
1335 recorder_.RecordDataMessageReceived(app_id, sender,
1336 data_message_stanza.ByteSize(), registered,
1337 GCMStatsRecorder::DATA_MESSAGE);
1338 if (!registered)
1339 return;
1341 IncomingMessage incoming_message;
1342 incoming_message.sender_id = data_message_stanza.from();
1343 if (data_message_stanza.has_token())
1344 incoming_message.collapse_key = data_message_stanza.token();
1345 incoming_message.data = message_data;
1346 incoming_message.raw_data = data_message_stanza.raw_data();
1348 delegate_->OnMessageReceived(app_id, incoming_message);
1351 void GCMClientImpl::HandleIncomingSendError(
1352 const mcs_proto::DataMessageStanza& data_message_stanza,
1353 MessageData& message_data) {
1354 SendErrorDetails send_error_details;
1355 send_error_details.additional_data = message_data;
1356 send_error_details.result = SERVER_ERROR;
1358 MessageData::iterator iter =
1359 send_error_details.additional_data.find(kSendErrorMessageIdKey);
1360 if (iter != send_error_details.additional_data.end()) {
1361 send_error_details.message_id = iter->second;
1362 send_error_details.additional_data.erase(iter);
1365 recorder_.RecordIncomingSendError(
1366 data_message_stanza.category(),
1367 data_message_stanza.to(),
1368 data_message_stanza.id());
1369 delegate_->OnMessageSendError(data_message_stanza.category(),
1370 send_error_details);
1373 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1374 if (registrations_.empty())
1375 return false;
1376 // Note that account mapper is not counted as a standalone app since it is
1377 // automatically started when other app uses GCM.
1378 return registrations_.size() > 1 ||
1379 !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId);
1382 } // namespace gcm