[AndroidWebViewShell] Replace rebaseline script with a new version using test_runner.py
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blob25f4be1cf48232faf924aa0adb9d5d629d3bf6a0
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 // In the case that the store does not exist, set |state| back to
385 // INITIALIZED such that store loading could be triggered again when
386 // Start() is called with IMMEDIATE_START.
387 state_ = INITIALIZED;
388 } else {
389 // Otherwise, destroy the store to try again.
390 ResetStore();
392 return;
394 gcm_store_reset_ = false;
396 device_checkin_info_.android_id = result->device_android_id;
397 device_checkin_info_.secret = result->device_security_token;
398 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
399 // A case where there were previously no accounts reported with checkin is
400 // considered to be the same as when the list of accounts is empty. It enables
401 // scheduling a periodic checkin for devices with no signed in users
402 // immediately after restart, while keeping |accounts_set == false| delays the
403 // checkin until the list of accounts is set explicitly.
404 if (result->last_checkin_accounts.size() == 0)
405 device_checkin_info_.accounts_set = true;
406 last_checkin_time_ = result->last_checkin_time;
407 gservices_settings_.UpdateFromLoadResult(*result);
409 for (auto iter = result->registrations.begin();
410 iter != result->registrations.end();
411 ++iter) {
412 std::string registration_id;
413 scoped_ptr<RegistrationInfo> registration =
414 RegistrationInfo::BuildFromString(
415 iter->first, iter->second, &registration_id);
416 // TODO(jianli): Add UMA to track the error case.
417 if (registration.get())
418 registrations_[make_linked_ptr(registration.release())] = registration_id;
421 for (auto iter = result->instance_id_data.begin();
422 iter != result->instance_id_data.end();
423 ++iter) {
424 std::string instance_id;
425 std::string extra_data;
426 if (DeserializeInstanceIDData(iter->second, &instance_id, &extra_data))
427 instance_id_data_[iter->first] = std::make_pair(instance_id, extra_data);
430 load_result_ = result.Pass();
431 state_ = LOADED;
433 // Don't initiate the GCM connection when GCM is in delayed start mode and
434 // not any standalone app has registered GCM yet.
435 if (start_mode_ == DELAYED_START && !HasStandaloneRegisteredApp()) {
436 // If no standalone app is using GCM and the device ID is present, schedule
437 // to have the store wiped out.
438 if (device_checkin_info_.android_id) {
439 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
440 FROM_HERE, base::Bind(&GCMClientImpl::DestroyStoreWhenNotNeeded,
441 destroying_gcm_store_ptr_factory_.GetWeakPtr()),
442 base::TimeDelta::FromMilliseconds(kDestroyGCMStoreDelayMS));
445 return;
448 StartGCM();
451 void GCMClientImpl::StartGCM() {
452 // Taking over the value of account_mappings before passing the ownership of
453 // load result to InitializeMCSClient.
454 std::vector<AccountMapping> account_mappings;
455 account_mappings.swap(load_result_->account_mappings);
456 base::Time last_token_fetch_time = load_result_->last_token_fetch_time;
458 InitializeMCSClient();
460 if (device_checkin_info_.IsValid()) {
461 SchedulePeriodicCheckin();
462 OnReady(account_mappings, last_token_fetch_time);
463 return;
466 state_ = INITIAL_DEVICE_CHECKIN;
467 device_checkin_info_.Reset();
468 StartCheckin();
471 void GCMClientImpl::InitializeMCSClient() {
472 std::vector<GURL> endpoints;
473 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
474 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
475 connection_factory_ = internals_builder_->BuildConnectionFactory(
476 endpoints,
477 GetGCMBackoffPolicy(),
478 network_session_,
479 url_request_context_getter_->GetURLRequestContext()
480 ->http_transaction_factory()
481 ->GetSession(),
482 net_log_.net_log(),
483 &recorder_);
484 connection_factory_->SetConnectionListener(this);
485 mcs_client_ = internals_builder_->BuildMCSClient(
486 chrome_build_info_.version,
487 clock_.get(),
488 connection_factory_.get(),
489 gcm_store_.get(),
490 &recorder_).Pass();
492 mcs_client_->Initialize(
493 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
494 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
495 weak_ptr_factory_.GetWeakPtr()),
496 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
497 weak_ptr_factory_.GetWeakPtr()),
498 load_result_.Pass());
501 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
502 const CheckinInfo& checkin_info) {
503 DCHECK(!device_checkin_info_.IsValid());
505 device_checkin_info_.android_id = checkin_info.android_id;
506 device_checkin_info_.secret = checkin_info.secret;
507 // If accounts were not set by now, we can consider them set (to empty list)
508 // to make sure periodic checkins get scheduled after initial checkin.
509 device_checkin_info_.accounts_set = true;
510 gcm_store_->SetDeviceCredentials(
511 checkin_info.android_id, checkin_info.secret,
512 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
513 weak_ptr_factory_.GetWeakPtr()));
515 OnReady(std::vector<AccountMapping>(), base::Time());
518 void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
519 const base::Time& last_token_fetch_time) {
520 state_ = READY;
521 StartMCSLogin();
523 delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
526 void GCMClientImpl::StartMCSLogin() {
527 DCHECK_EQ(READY, state_);
528 DCHECK(device_checkin_info_.IsValid());
529 mcs_client_->Login(device_checkin_info_.android_id,
530 device_checkin_info_.secret);
533 void GCMClientImpl::DestroyStoreWhenNotNeeded() {
534 if (state_ != LOADED || start_mode_ != DELAYED_START)
535 return;
537 gcm_store_->Destroy(base::Bind(&GCMClientImpl::DestroyStoreCallback,
538 weak_ptr_factory_.GetWeakPtr()));
541 void GCMClientImpl::ResetStore() {
542 DCHECK_EQ(LOADING, state_);
544 // If already being reset, don't do it again. We want to prevent from
545 // resetting and loading from the store again and again.
546 if (gcm_store_reset_) {
547 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET);
548 state_ = UNINITIALIZED;
549 return;
551 gcm_store_reset_ = true;
553 // Destroy the GCM store to start over.
554 gcm_store_->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback,
555 weak_ptr_factory_.GetWeakPtr()));
558 void GCMClientImpl::SetAccountTokens(
559 const std::vector<AccountTokenInfo>& account_tokens) {
560 device_checkin_info_.account_tokens.clear();
561 for (std::vector<AccountTokenInfo>::const_iterator iter =
562 account_tokens.begin();
563 iter != account_tokens.end();
564 ++iter) {
565 device_checkin_info_.account_tokens[iter->email] = iter->access_token;
568 bool accounts_set_before = device_checkin_info_.accounts_set;
569 device_checkin_info_.accounts_set = true;
571 DVLOG(1) << "Set account called with: " << account_tokens.size()
572 << " accounts.";
574 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
575 return;
577 bool account_removed = false;
578 for (std::set<std::string>::iterator iter =
579 device_checkin_info_.last_checkin_accounts.begin();
580 iter != device_checkin_info_.last_checkin_accounts.end();
581 ++iter) {
582 if (device_checkin_info_.account_tokens.find(*iter) ==
583 device_checkin_info_.account_tokens.end()) {
584 account_removed = true;
588 // Checkin will be forced when any of the accounts was removed during the
589 // current Chrome session or if there has been an account removed between the
590 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
591 // We only force checkin when user signs out. When there is a new account
592 // signed in, the periodic checkin will take care of adding the association in
593 // reasonable time.
594 if (account_removed) {
595 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
596 checkin_request_.reset();
597 StartCheckin();
598 } else if (!accounts_set_before) {
599 SchedulePeriodicCheckin();
600 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
604 void GCMClientImpl::UpdateAccountMapping(
605 const AccountMapping& account_mapping) {
606 gcm_store_->AddAccountMapping(account_mapping,
607 base::Bind(&GCMClientImpl::DefaultStoreCallback,
608 weak_ptr_factory_.GetWeakPtr()));
611 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
612 gcm_store_->RemoveAccountMapping(
613 account_id,
614 base::Bind(&GCMClientImpl::DefaultStoreCallback,
615 weak_ptr_factory_.GetWeakPtr()));
618 void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
619 gcm_store_->SetLastTokenFetchTime(
620 time,
621 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
622 weak_ptr_factory_.GetWeakPtr()));
625 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
626 DCHECK(mcs_client_);
627 mcs_client_->UpdateHeartbeatTimer(timer.Pass());
630 void GCMClientImpl::AddInstanceIDData(const std::string& app_id,
631 const std::string& instance_id,
632 const std::string& extra_data) {
633 instance_id_data_[app_id] = std::make_pair(instance_id, extra_data);
634 gcm_store_->AddInstanceIDData(
635 app_id,
636 SerializeInstanceIDData(instance_id, extra_data),
637 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
638 weak_ptr_factory_.GetWeakPtr()));
641 void GCMClientImpl::RemoveInstanceIDData(const std::string& app_id) {
642 instance_id_data_.erase(app_id);
643 gcm_store_->RemoveInstanceIDData(
644 app_id,
645 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
646 weak_ptr_factory_.GetWeakPtr()));
649 void GCMClientImpl::GetInstanceIDData(const std::string& app_id,
650 std::string* instance_id,
651 std::string* extra_data) {
652 DCHECK(instance_id && extra_data);
654 auto iter = instance_id_data_.find(app_id);
655 if (iter == instance_id_data_.end())
656 return;
657 *instance_id = iter->second.first;
658 *extra_data = iter->second.second;
661 void GCMClientImpl::AddHeartbeatInterval(const std::string& scope,
662 int interval_ms) {
663 DCHECK(mcs_client_);
664 mcs_client_->AddHeartbeatInterval(scope, interval_ms);
667 void GCMClientImpl::RemoveHeartbeatInterval(const std::string& scope) {
668 DCHECK(mcs_client_);
669 mcs_client_->RemoveHeartbeatInterval(scope);
672 void GCMClientImpl::StartCheckin() {
673 // Make sure no checkin is in progress.
674 if (checkin_request_.get())
675 return;
677 checkin_proto::ChromeBuildProto chrome_build_proto;
678 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
679 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
680 device_checkin_info_.secret,
681 device_checkin_info_.account_tokens,
682 gservices_settings_.digest(),
683 chrome_build_proto);
684 checkin_request_.reset(
685 new CheckinRequest(gservices_settings_.GetCheckinURL(),
686 request_info,
687 GetGCMBackoffPolicy(),
688 base::Bind(&GCMClientImpl::OnCheckinCompleted,
689 weak_ptr_factory_.GetWeakPtr()),
690 url_request_context_getter_.get(),
691 &recorder_));
692 // Taking a snapshot of the accounts count here, as there might be an asynch
693 // update of the account tokens while checkin is in progress.
694 device_checkin_info_.SnapshotCheckinAccounts();
695 checkin_request_->Start();
698 void GCMClientImpl::OnCheckinCompleted(
699 const checkin_proto::AndroidCheckinResponse& checkin_response) {
700 checkin_request_.reset();
702 if (!checkin_response.has_android_id() ||
703 !checkin_response.has_security_token()) {
704 // TODO(fgorski): I don't think a retry here will help, we should probably
705 // start over. By checking in with (0, 0).
706 return;
709 CheckinInfo checkin_info;
710 checkin_info.android_id = checkin_response.android_id();
711 checkin_info.secret = checkin_response.security_token();
713 if (state_ == INITIAL_DEVICE_CHECKIN) {
714 OnFirstTimeDeviceCheckinCompleted(checkin_info);
715 } else {
716 // checkin_info is not expected to change after a periodic checkin as it
717 // would invalidate the registratoin IDs.
718 DCHECK_EQ(READY, state_);
719 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
720 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
723 if (device_checkin_info_.IsValid()) {
724 // First update G-services settings, as something might have changed.
725 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
726 gcm_store_->SetGServicesSettings(
727 gservices_settings_.settings_map(),
728 gservices_settings_.digest(),
729 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
730 weak_ptr_factory_.GetWeakPtr()));
733 last_checkin_time_ = clock_->Now();
734 gcm_store_->SetLastCheckinInfo(
735 last_checkin_time_,
736 device_checkin_info_.last_checkin_accounts,
737 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
738 weak_ptr_factory_.GetWeakPtr()));
739 SchedulePeriodicCheckin();
743 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
744 DCHECK(success);
747 void GCMClientImpl::SchedulePeriodicCheckin() {
748 // Make sure no checkin is in progress.
749 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
750 return;
752 // There should be only one periodic checkin pending at a time. Removing
753 // pending periodic checkin to schedule a new one.
754 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
756 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
757 if (time_to_next_checkin < base::TimeDelta())
758 time_to_next_checkin = base::TimeDelta();
760 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
761 FROM_HERE, base::Bind(&GCMClientImpl::StartCheckin,
762 periodic_checkin_ptr_factory_.GetWeakPtr()),
763 time_to_next_checkin);
766 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
767 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
768 clock_->Now();
771 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
772 // TODO(fgorski): This is one of the signals that store needs a rebuild.
773 DCHECK(success);
776 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
777 // TODO(fgorski): This is one of the signals that store needs a rebuild.
778 DCHECK(success);
781 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
782 // TODO(fgorski): This is one of the signals that store needs a rebuild.
783 DCHECK(success);
786 void GCMClientImpl::DefaultStoreCallback(bool success) {
787 DCHECK(success);
790 void GCMClientImpl::IgnoreWriteResultCallback(bool success) {
791 // TODO(fgorski): Ignoring the write result for now to make sure
792 // sync_intergration_tests are not broken.
795 void GCMClientImpl::DestroyStoreCallback(bool success) {
796 ResetCache();
798 if (!success) {
799 LOG(ERROR) << "Failed to destroy GCM store";
800 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
801 state_ = UNINITIALIZED;
802 return;
805 state_ = INITIALIZED;
808 void GCMClientImpl::ResetStoreCallback(bool success) {
809 if (!success) {
810 LOG(ERROR) << "Failed to reset GCM store";
811 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
812 state_ = UNINITIALIZED;
813 return;
816 state_ = INITIALIZED;
817 Start(start_mode_);
820 void GCMClientImpl::Stop() {
821 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
822 // Shutdown.
823 DVLOG(1) << "Stopping the GCM Client";
824 ResetCache();
825 state_ = INITIALIZED;
826 gcm_store_->Close();
829 void GCMClientImpl::ResetCache() {
830 weak_ptr_factory_.InvalidateWeakPtrs();
831 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
832 device_checkin_info_.Reset();
833 connection_factory_.reset();
834 delegate_->OnDisconnected();
835 mcs_client_.reset();
836 checkin_request_.reset();
837 // Delete all of the pending registration and unregistration requests.
838 pending_registration_requests_.clear();
839 pending_unregistration_requests_.clear();
842 void GCMClientImpl::Register(
843 const linked_ptr<RegistrationInfo>& registration_info) {
844 DCHECK_EQ(state_, READY);
846 // Find and use the cached registration ID.
847 RegistrationInfoMap::const_iterator registrations_iter =
848 registrations_.find(registration_info);
849 if (registrations_iter != registrations_.end()) {
850 bool matched = true;
852 // For GCM registration, we also match the sender IDs since multiple
853 // registrations are not supported.
854 const GCMRegistrationInfo* gcm_registration_info =
855 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
856 if (gcm_registration_info) {
857 const GCMRegistrationInfo* cached_gcm_registration_info =
858 GCMRegistrationInfo::FromRegistrationInfo(
859 registrations_iter->first.get());
860 DCHECK(cached_gcm_registration_info);
861 if (cached_gcm_registration_info &&
862 gcm_registration_info->sender_ids !=
863 cached_gcm_registration_info->sender_ids) {
864 matched = false;
868 if (matched) {
869 delegate_->OnRegisterFinished(
870 registration_info, registrations_iter->second, SUCCESS);
871 return;
875 scoped_ptr<RegistrationRequest::CustomRequestHandler> request_handler;
876 std::string source_to_record;
878 const GCMRegistrationInfo* gcm_registration_info =
879 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
880 if (gcm_registration_info) {
881 std::string senders;
882 for (auto iter = gcm_registration_info->sender_ids.begin();
883 iter != gcm_registration_info->sender_ids.end();
884 ++iter) {
885 if (!senders.empty())
886 senders.append(",");
887 senders.append(*iter);
889 UMA_HISTOGRAM_COUNTS("GCM.RegistrationSenderIdCount",
890 gcm_registration_info->sender_ids.size());
892 request_handler.reset(new GCMRegistrationRequestHandler(senders));
893 source_to_record = senders;
896 const InstanceIDTokenInfo* instance_id_token_info =
897 InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
898 if (instance_id_token_info) {
899 auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
900 DCHECK(instance_id_iter != instance_id_data_.end());
902 request_handler.reset(new InstanceIDGetTokenRequestHandler(
903 instance_id_iter->second.first,
904 instance_id_token_info->authorized_entity,
905 instance_id_token_info->scope,
906 ConstructGCMVersion(chrome_build_info_.version),
907 instance_id_token_info->options));
908 source_to_record = instance_id_token_info->authorized_entity + "/" +
909 instance_id_token_info->scope;
912 RegistrationRequest::RequestInfo request_info(
913 device_checkin_info_.android_id,
914 device_checkin_info_.secret,
915 registration_info->app_id);
917 scoped_ptr<RegistrationRequest> registration_request(new RegistrationRequest(
918 gservices_settings_.GetRegistrationURL(), request_info,
919 request_handler.Pass(), GetGCMBackoffPolicy(),
920 base::Bind(&GCMClientImpl::OnRegisterCompleted,
921 weak_ptr_factory_.GetWeakPtr(), registration_info),
922 kMaxRegistrationRetries, url_request_context_getter_, &recorder_,
923 source_to_record));
924 registration_request->Start();
925 pending_registration_requests_.insert(registration_info,
926 registration_request.Pass());
929 void GCMClientImpl::OnRegisterCompleted(
930 const linked_ptr<RegistrationInfo>& registration_info,
931 RegistrationRequest::Status status,
932 const std::string& registration_id) {
933 DCHECK(delegate_);
935 Result result;
936 PendingRegistrationRequests::const_iterator iter =
937 pending_registration_requests_.find(registration_info);
938 if (iter == pending_registration_requests_.end())
939 result = UNKNOWN_ERROR;
940 else if (status == RegistrationRequest::INVALID_SENDER)
941 result = INVALID_PARAMETER;
942 else if (registration_id.empty())
943 result = SERVER_ERROR;
944 else
945 result = SUCCESS;
947 if (result == SUCCESS) {
948 // Cache it.
949 // Note that the existing cached record has to be removed first because
950 // otherwise the key value in registrations_ will not be updated. For GCM
951 // registrations, the key consists of pair of app_id and sender_ids though
952 // only app_id is used in the comparison.
953 registrations_.erase(registration_info);
954 registrations_[registration_info] = registration_id;
956 // Save it in the persistent store.
957 gcm_store_->AddRegistration(
958 registration_info->GetSerializedKey(),
959 registration_info->GetSerializedValue(registration_id),
960 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
961 weak_ptr_factory_.GetWeakPtr()));
964 delegate_->OnRegisterFinished(
965 registration_info,
966 result == SUCCESS ? registration_id : std::string(),
967 result);
969 if (iter != pending_registration_requests_.end())
970 pending_registration_requests_.erase(iter);
973 void GCMClientImpl::Unregister(
974 const linked_ptr<RegistrationInfo>& registration_info) {
975 DCHECK_EQ(state_, READY);
977 scoped_ptr<UnregistrationRequest::CustomRequestHandler> request_handler;
978 std::string source_to_record;
980 const GCMRegistrationInfo* gcm_registration_info =
981 GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
982 if (gcm_registration_info) {
983 request_handler.reset(
984 new GCMUnregistrationRequestHandler(registration_info->app_id));
987 const InstanceIDTokenInfo* instance_id_token_info =
988 InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
989 if (instance_id_token_info) {
990 auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
991 if (instance_id_iter == instance_id_data_.end()) {
992 // This should not be reached since we should not delete tokens when
993 // an InstanceID has not been created yet.
994 NOTREACHED();
995 return;
997 request_handler.reset(new InstanceIDDeleteTokenRequestHandler(
998 instance_id_iter->second.first,
999 instance_id_token_info->authorized_entity,
1000 instance_id_token_info->scope,
1001 ConstructGCMVersion(chrome_build_info_.version)));
1002 source_to_record = instance_id_token_info->authorized_entity + "/" +
1003 instance_id_token_info->scope;
1006 // Remove the registration/token(s) from the cache and the store.
1007 // TODO(jianli): Remove it only when the request is successful.
1008 if (instance_id_token_info &&
1009 instance_id_token_info->authorized_entity == "*" &&
1010 instance_id_token_info->scope == "*") {
1011 // If authorized_entity and scope are '*', find and remove all associated
1012 // tokens.
1013 bool token_found = false;
1014 for (auto iter = registrations_.begin();
1015 iter != registrations_.end();) {
1016 InstanceIDTokenInfo* cached_instance_id_token_info =
1017 InstanceIDTokenInfo::FromRegistrationInfo(iter->first.get());
1018 if (cached_instance_id_token_info &&
1019 cached_instance_id_token_info->app_id == registration_info->app_id) {
1020 token_found = true;
1021 gcm_store_->RemoveRegistration(
1022 cached_instance_id_token_info->GetSerializedKey(),
1023 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
1024 weak_ptr_factory_.GetWeakPtr()));
1025 registrations_.erase(iter++);
1026 } else {
1027 ++iter;
1031 // If no token is found for the Instance ID, don't need to unregister
1032 // since the Instance ID is not sent to the server yet.
1033 if (!token_found) {
1034 OnUnregisterCompleted(registration_info,
1035 UnregistrationRequest::SUCCESS);
1036 return;
1038 } else {
1039 auto iter = registrations_.find(registration_info);
1040 if (iter == registrations_.end()) {
1041 delegate_->OnUnregisterFinished(registration_info, INVALID_PARAMETER);
1042 return;
1044 registrations_.erase(iter);
1046 gcm_store_->RemoveRegistration(
1047 registration_info->GetSerializedKey(),
1048 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
1049 weak_ptr_factory_.GetWeakPtr()));
1052 UnregistrationRequest::RequestInfo request_info(
1053 device_checkin_info_.android_id,
1054 device_checkin_info_.secret,
1055 registration_info->app_id);
1057 scoped_ptr<UnregistrationRequest> unregistration_request(
1058 new UnregistrationRequest(
1059 gservices_settings_.GetRegistrationURL(), request_info,
1060 request_handler.Pass(), GetGCMBackoffPolicy(),
1061 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
1062 weak_ptr_factory_.GetWeakPtr(), registration_info),
1063 kMaxUnregistrationRetries, url_request_context_getter_, &recorder_,
1064 source_to_record));
1065 unregistration_request->Start();
1066 pending_unregistration_requests_.insert(registration_info,
1067 unregistration_request.Pass());
1070 void GCMClientImpl::OnUnregisterCompleted(
1071 const linked_ptr<RegistrationInfo>& registration_info,
1072 UnregistrationRequest::Status status) {
1073 DVLOG(1) << "Unregister completed for app: " << registration_info->app_id
1074 << " with " << (status ? "success." : "failure.");
1076 Result result;
1077 switch (status) {
1078 case UnregistrationRequest::SUCCESS:
1079 result = SUCCESS;
1080 break;
1081 case UnregistrationRequest::INVALID_PARAMETERS:
1082 result = INVALID_PARAMETER;
1083 break;
1084 default:
1085 // All other errors are treated as SERVER_ERROR.
1086 result = SERVER_ERROR;
1087 break;
1089 delegate_->OnUnregisterFinished(registration_info, result);
1091 pending_unregistration_requests_.erase(registration_info);
1094 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
1095 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
1096 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
1099 void GCMClientImpl::Send(const std::string& app_id,
1100 const std::string& receiver_id,
1101 const OutgoingMessage& message) {
1102 DCHECK_EQ(state_, READY);
1104 RecordOutgoingMessageToUMA(message);
1106 mcs_proto::DataMessageStanza stanza;
1107 stanza.set_ttl(message.time_to_live);
1108 stanza.set_sent(clock_->Now().ToInternalValue() /
1109 base::Time::kMicrosecondsPerSecond);
1110 stanza.set_id(message.id);
1111 stanza.set_from(kSendMessageFromValue);
1112 stanza.set_to(receiver_id);
1113 stanza.set_category(app_id);
1115 for (MessageData::const_iterator iter = message.data.begin();
1116 iter != message.data.end();
1117 ++iter) {
1118 mcs_proto::AppData* app_data = stanza.add_app_data();
1119 app_data->set_key(iter->first);
1120 app_data->set_value(iter->second);
1123 MCSMessage mcs_message(stanza);
1124 DVLOG(1) << "MCS message size: " << mcs_message.size();
1125 mcs_client_->SendMessage(mcs_message);
1128 std::string GCMClientImpl::GetStateString() const {
1129 switch(state_) {
1130 case GCMClientImpl::INITIALIZED:
1131 return "INITIALIZED";
1132 case GCMClientImpl::UNINITIALIZED:
1133 return "UNINITIALIZED";
1134 case GCMClientImpl::LOADING:
1135 return "LOADING";
1136 case GCMClientImpl::LOADED:
1137 return "LOADED";
1138 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
1139 return "INITIAL_DEVICE_CHECKIN";
1140 case GCMClientImpl::READY:
1141 return "READY";
1142 default:
1143 NOTREACHED();
1144 return std::string();
1148 void GCMClientImpl::SetRecording(bool recording) {
1149 recorder_.SetRecording(recording);
1152 void GCMClientImpl::ClearActivityLogs() {
1153 recorder_.Clear();
1156 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
1157 GCMClient::GCMStatistics stats;
1158 stats.gcm_client_created = true;
1159 stats.is_recording = recorder_.is_recording();
1160 stats.gcm_client_state = GetStateString();
1161 stats.connection_client_created = mcs_client_.get() != NULL;
1162 if (connection_factory_.get())
1163 stats.connection_state = connection_factory_->GetConnectionStateString();
1164 if (mcs_client_.get()) {
1165 stats.send_queue_size = mcs_client_->GetSendQueueSize();
1166 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
1168 if (device_checkin_info_.android_id > 0)
1169 stats.android_id = device_checkin_info_.android_id;
1170 recorder_.CollectActivities(&stats.recorded_activities);
1172 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
1173 it != registrations_.end(); ++it) {
1174 stats.registered_app_ids.push_back(it->first->app_id);
1176 return stats;
1179 void GCMClientImpl::OnActivityRecorded() {
1180 delegate_->OnActivityRecorded();
1183 void GCMClientImpl::OnConnected(const GURL& current_server,
1184 const net::IPEndPoint& ip_endpoint) {
1185 // TODO(gcm): expose current server in debug page.
1186 delegate_->OnActivityRecorded();
1187 delegate_->OnConnected(ip_endpoint);
1190 void GCMClientImpl::OnDisconnected() {
1191 delegate_->OnActivityRecorded();
1192 delegate_->OnDisconnected();
1195 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
1196 switch (message.tag()) {
1197 case kLoginResponseTag:
1198 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
1199 return;
1200 case kDataMessageStanzaTag:
1201 DVLOG(1) << "A downstream message received. Processing...";
1202 HandleIncomingMessage(message);
1203 return;
1204 default:
1205 NOTREACHED() << "Message with unexpected tag received by GCMClient";
1206 return;
1210 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
1211 const std::string& app_id,
1212 const std::string& message_id,
1213 MCSClient::MessageSendStatus status) {
1214 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
1215 DCHECK(delegate_);
1217 // TTL_EXCEEDED is singled out here, because it can happen long time after the
1218 // message was sent. That is why it comes as |OnMessageSendError| event rather
1219 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
1220 // All other errors will be raised immediately, through asynchronous callback.
1221 // It is expected that TTL_EXCEEDED will be issued for a message that was
1222 // previously issued |OnSendFinished| with status SUCCESS.
1223 // TODO(jianli): Consider adding UMA for this status.
1224 if (status == MCSClient::TTL_EXCEEDED) {
1225 SendErrorDetails send_error_details;
1226 send_error_details.message_id = message_id;
1227 send_error_details.result = GCMClient::TTL_EXCEEDED;
1228 delegate_->OnMessageSendError(app_id, send_error_details);
1229 } else if (status == MCSClient::SENT) {
1230 delegate_->OnSendAcknowledged(app_id, message_id);
1231 } else {
1232 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
1236 void GCMClientImpl::OnMCSError() {
1237 // TODO(fgorski): For now it replaces the initialization method. Long term it
1238 // should have an error or status passed in.
1241 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
1242 DCHECK(delegate_);
1244 const mcs_proto::DataMessageStanza& data_message_stanza =
1245 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
1246 message.GetProtobuf());
1247 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
1249 // Copying all the data from the stanza to a MessageData object. When present,
1250 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
1251 // later.
1252 MessageData message_data;
1253 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
1254 std::string key = data_message_stanza.app_data(i).key();
1255 message_data[key] = data_message_stanza.app_data(i).value();
1258 MessageType message_type = DATA_MESSAGE;
1259 MessageData::iterator iter = message_data.find(kMessageTypeKey);
1260 if (iter != message_data.end()) {
1261 message_type = DecodeMessageType(iter->second);
1262 message_data.erase(iter);
1265 switch (message_type) {
1266 case DATA_MESSAGE:
1267 HandleIncomingDataMessage(data_message_stanza, message_data);
1268 break;
1269 case DELETED_MESSAGES:
1270 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
1271 data_message_stanza.from(),
1272 data_message_stanza.ByteSize(),
1273 true,
1274 GCMStatsRecorder::DELETED_MESSAGES);
1275 delegate_->OnMessagesDeleted(data_message_stanza.category());
1276 break;
1277 case SEND_ERROR:
1278 HandleIncomingSendError(data_message_stanza, message_data);
1279 break;
1280 case UNKNOWN:
1281 default: // Treat default the same as UNKNOWN.
1282 DVLOG(1) << "Unknown message_type received. Message ignored. "
1283 << "App ID: " << data_message_stanza.category() << ".";
1284 break;
1288 void GCMClientImpl::HandleIncomingDataMessage(
1289 const mcs_proto::DataMessageStanza& data_message_stanza,
1290 MessageData& message_data) {
1291 std::string app_id = data_message_stanza.category();
1292 std::string sender = data_message_stanza.from();
1294 // Drop the message when the app is not registered for the sender of the
1295 // message.
1296 bool registered = false;
1298 // First, find among all GCM registrations.
1299 scoped_ptr<GCMRegistrationInfo> gcm_registration(new GCMRegistrationInfo);
1300 gcm_registration->app_id = app_id;
1301 auto gcm_registration_iter = registrations_.find(
1302 make_linked_ptr<RegistrationInfo>(gcm_registration.release()));
1303 if (gcm_registration_iter != registrations_.end()) {
1304 GCMRegistrationInfo* cached_gcm_registration =
1305 GCMRegistrationInfo::FromRegistrationInfo(
1306 gcm_registration_iter->first.get());
1307 if (cached_gcm_registration &&
1308 std::find(cached_gcm_registration->sender_ids.begin(),
1309 cached_gcm_registration->sender_ids.end(),
1310 sender) != cached_gcm_registration->sender_ids.end()) {
1311 registered = true;
1315 // Then, find among all InstanceID registrations.
1316 if (!registered) {
1317 scoped_ptr<InstanceIDTokenInfo> instance_id_token(new InstanceIDTokenInfo);
1318 instance_id_token->app_id = app_id;
1319 instance_id_token->authorized_entity = sender;
1320 instance_id_token->scope = kGCMScope;
1321 auto instance_id_token_iter = registrations_.find(
1322 make_linked_ptr<RegistrationInfo>(instance_id_token.release()));
1323 if (instance_id_token_iter != registrations_.end())
1324 registered = true;
1327 recorder_.RecordDataMessageReceived(app_id, sender,
1328 data_message_stanza.ByteSize(), registered,
1329 GCMStatsRecorder::DATA_MESSAGE);
1330 if (!registered)
1331 return;
1333 IncomingMessage incoming_message;
1334 incoming_message.sender_id = data_message_stanza.from();
1335 if (data_message_stanza.has_token())
1336 incoming_message.collapse_key = data_message_stanza.token();
1337 incoming_message.data = message_data;
1338 delegate_->OnMessageReceived(app_id, incoming_message);
1341 void GCMClientImpl::HandleIncomingSendError(
1342 const mcs_proto::DataMessageStanza& data_message_stanza,
1343 MessageData& message_data) {
1344 SendErrorDetails send_error_details;
1345 send_error_details.additional_data = message_data;
1346 send_error_details.result = SERVER_ERROR;
1348 MessageData::iterator iter =
1349 send_error_details.additional_data.find(kSendErrorMessageIdKey);
1350 if (iter != send_error_details.additional_data.end()) {
1351 send_error_details.message_id = iter->second;
1352 send_error_details.additional_data.erase(iter);
1355 recorder_.RecordIncomingSendError(
1356 data_message_stanza.category(),
1357 data_message_stanza.to(),
1358 data_message_stanza.id());
1359 delegate_->OnMessageSendError(data_message_stanza.category(),
1360 send_error_details);
1363 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1364 if (registrations_.empty())
1365 return false;
1366 // Note that account mapper is not counted as a standalone app since it is
1367 // automatically started when other app uses GCM.
1368 return registrations_.size() > 1 ||
1369 !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId);
1372 } // namespace gcm