Delay start the GCM when it is actually used
[chromium-blink-merge.git] / components / gcm_driver / gcm_client_impl.cc
blob6b1c980f00ff8441153b58afde58881516509f0c
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/gcm_driver/gcm_client_impl.h"
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/default_clock.h"
18 #include "base/timer/timer.h"
19 #include "components/gcm_driver/gcm_account_mapper.h"
20 #include "components/gcm_driver/gcm_backoff_policy.h"
21 #include "google_apis/gcm/base/encryptor.h"
22 #include "google_apis/gcm/base/mcs_message.h"
23 #include "google_apis/gcm/base/mcs_util.h"
24 #include "google_apis/gcm/engine/checkin_request.h"
25 #include "google_apis/gcm/engine/connection_factory_impl.h"
26 #include "google_apis/gcm/engine/gcm_store_impl.h"
27 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
28 #include "google_apis/gcm/protocol/checkin.pb.h"
29 #include "google_apis/gcm/protocol/mcs.pb.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_transaction_factory.h"
32 #include "net/url_request/url_request_context.h"
33 #include "url/gurl.h"
35 namespace gcm {
37 namespace {
39 // Indicates a message type of the received message.
40 enum MessageType {
41 UNKNOWN, // Undetermined type.
42 DATA_MESSAGE, // Regular data message.
43 DELETED_MESSAGES, // Messages were deleted on the server.
44 SEND_ERROR, // Error sending a message.
47 enum OutgoingMessageTTLCategory {
48 TTL_ZERO,
49 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
50 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
51 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
52 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
53 TTL_MORE_THAN_ONE_WEEK,
54 TTL_MAXIMUM,
55 // NOTE: always keep this entry at the end. Add new TTL category only
56 // immediately above this line. Make sure to update the corresponding
57 // histogram enum accordingly.
58 TTL_CATEGORY_COUNT
61 const int kMaxRegistrationRetries = 5;
62 const char kMessageTypeDataMessage[] = "gcm";
63 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
64 const char kMessageTypeKey[] = "message_type";
65 const char kMessageTypeSendErrorKey[] = "send_error";
66 const char kSendErrorMessageIdKey[] = "google.message_id";
67 const char kSendMessageFromValue[] = "gcm@chrome.com";
68 const int64 kDefaultUserSerialNumber = 0LL;
70 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
71 switch (status) {
72 case MCSClient::QUEUED:
73 return GCMClient::SUCCESS;
74 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
75 return GCMClient::NETWORK_ERROR;
76 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
77 return GCMClient::NETWORK_ERROR;
78 case MCSClient::MESSAGE_TOO_LARGE:
79 return GCMClient::INVALID_PARAMETER;
80 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
81 return GCMClient::NETWORK_ERROR;
82 case MCSClient::TTL_EXCEEDED:
83 return GCMClient::NETWORK_ERROR;
84 case MCSClient::SENT:
85 default:
86 NOTREACHED();
87 break;
89 return GCMClientImpl::UNKNOWN_ERROR;
92 void ToCheckinProtoVersion(
93 const GCMClient::ChromeBuildInfo& chrome_build_info,
94 checkin_proto::ChromeBuildProto* android_build_info) {
95 checkin_proto::ChromeBuildProto_Platform platform;
96 switch (chrome_build_info.platform) {
97 case GCMClient::PLATFORM_WIN:
98 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
99 break;
100 case GCMClient::PLATFORM_MAC:
101 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
102 break;
103 case GCMClient::PLATFORM_LINUX:
104 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
105 break;
106 case GCMClient::PLATFORM_IOS:
107 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
108 break;
109 case GCMClient::PLATFORM_ANDROID:
110 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
111 break;
112 case GCMClient::PLATFORM_CROS:
113 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
114 break;
115 case GCMClient::PLATFORM_UNKNOWN:
116 // For unknown platform, return as LINUX.
117 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
118 break;
119 default:
120 NOTREACHED();
121 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
122 break;
124 android_build_info->set_platform(platform);
126 checkin_proto::ChromeBuildProto_Channel channel;
127 switch (chrome_build_info.channel) {
128 case GCMClient::CHANNEL_STABLE:
129 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
130 break;
131 case GCMClient::CHANNEL_BETA:
132 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
133 break;
134 case GCMClient::CHANNEL_DEV:
135 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
136 break;
137 case GCMClient::CHANNEL_CANARY:
138 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
139 break;
140 case GCMClient::CHANNEL_UNKNOWN:
141 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
142 break;
143 default:
144 NOTREACHED();
145 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
146 break;
148 android_build_info->set_channel(channel);
150 android_build_info->set_chrome_version(chrome_build_info.version);
153 MessageType DecodeMessageType(const std::string& value) {
154 if (kMessageTypeDeletedMessagesKey == value)
155 return DELETED_MESSAGES;
156 if (kMessageTypeSendErrorKey == value)
157 return SEND_ERROR;
158 if (kMessageTypeDataMessage == value)
159 return DATA_MESSAGE;
160 return UNKNOWN;
163 void RecordOutgoingMessageToUMA(
164 const gcm::GCMClient::OutgoingMessage& message) {
165 OutgoingMessageTTLCategory ttl_category;
166 if (message.time_to_live == 0)
167 ttl_category = TTL_ZERO;
168 else if (message.time_to_live <= 60 )
169 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
170 else if (message.time_to_live <= 60 * 60)
171 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
172 else if (message.time_to_live <= 24 * 60 * 60)
173 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
174 else
175 ttl_category = TTL_MAXIMUM;
177 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
178 ttl_category,
179 TTL_CATEGORY_COUNT);
182 } // namespace
184 GCMInternalsBuilder::GCMInternalsBuilder() {}
185 GCMInternalsBuilder::~GCMInternalsBuilder() {}
187 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
188 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
191 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
192 const std::string& version,
193 base::Clock* clock,
194 ConnectionFactory* connection_factory,
195 GCMStore* gcm_store,
196 GCMStatsRecorder* recorder) {
197 return scoped_ptr<MCSClient>(new MCSClient(
198 version, clock, connection_factory, gcm_store, recorder));
201 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
202 const std::vector<GURL>& endpoints,
203 const net::BackoffEntry::Policy& backoff_policy,
204 const scoped_refptr<net::HttpNetworkSession>& gcm_network_session,
205 const scoped_refptr<net::HttpNetworkSession>& http_network_session,
206 net::NetLog* net_log,
207 GCMStatsRecorder* recorder) {
208 return make_scoped_ptr<ConnectionFactory>(
209 new ConnectionFactoryImpl(endpoints,
210 backoff_policy,
211 gcm_network_session,
212 http_network_session,
213 net_log,
214 recorder));
217 GCMClientImpl::CheckinInfo::CheckinInfo()
218 : android_id(0), secret(0), accounts_set(false) {
221 GCMClientImpl::CheckinInfo::~CheckinInfo() {
224 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
225 last_checkin_accounts.clear();
226 for (std::map<std::string, std::string>::iterator iter =
227 account_tokens.begin();
228 iter != account_tokens.end();
229 ++iter) {
230 last_checkin_accounts.insert(iter->first);
234 void GCMClientImpl::CheckinInfo::Reset() {
235 android_id = 0;
236 secret = 0;
237 accounts_set = false;
238 account_tokens.clear();
239 last_checkin_accounts.clear();
242 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
243 : internals_builder_(internals_builder.Pass()),
244 state_(UNINITIALIZED),
245 delegate_(NULL),
246 start_mode_(DELAYED_START),
247 clock_(internals_builder_->BuildClock()),
248 url_request_context_getter_(NULL),
249 pending_registration_requests_deleter_(&pending_registration_requests_),
250 pending_unregistration_requests_deleter_(
251 &pending_unregistration_requests_),
252 periodic_checkin_ptr_factory_(this),
253 weak_ptr_factory_(this) {
256 GCMClientImpl::~GCMClientImpl() {
259 void GCMClientImpl::Initialize(
260 const ChromeBuildInfo& chrome_build_info,
261 const base::FilePath& path,
262 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
263 const scoped_refptr<net::URLRequestContextGetter>&
264 url_request_context_getter,
265 scoped_ptr<Encryptor> encryptor,
266 GCMClient::Delegate* delegate) {
267 DCHECK_EQ(UNINITIALIZED, state_);
268 DCHECK(url_request_context_getter.get());
269 DCHECK(delegate);
271 url_request_context_getter_ = url_request_context_getter;
272 const net::HttpNetworkSession::Params* network_session_params =
273 url_request_context_getter_->GetURLRequestContext()->
274 GetNetworkSessionParams();
275 DCHECK(network_session_params);
276 network_session_ = new net::HttpNetworkSession(*network_session_params);
278 chrome_build_info_ = chrome_build_info;
280 gcm_store_.reset(
281 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
283 delegate_ = delegate;
285 recorder_.SetDelegate(this);
287 state_ = INITIALIZED;
290 void GCMClientImpl::Start(StartMode start_mode) {
291 DCHECK_NE(UNINITIALIZED, state_);
293 if (state_ == LOADED) {
294 // Start the GCM if not yet.
295 if (start_mode == IMMEDIATE_START)
296 StartGCM();
297 return;
300 // The delay start behavior will be abandoned when Start has been called
301 // once with IMMEDIATE_START behavior.
302 if (start_mode == IMMEDIATE_START)
303 start_mode_ = IMMEDIATE_START;
305 // Bail out if the loading is not started or completed.
306 if (state_ != INITIALIZED)
307 return;
309 // Once the loading is completed, the check-in will be initiated.
310 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
311 weak_ptr_factory_.GetWeakPtr()));
312 state_ = LOADING;
315 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
316 DCHECK_EQ(LOADING, state_);
318 if (!result->success) {
319 ResetState();
320 return;
323 registrations_ = result->registrations;
324 device_checkin_info_.android_id = result->device_android_id;
325 device_checkin_info_.secret = result->device_security_token;
326 device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
327 // A case where there were previously no accounts reported with checkin is
328 // considered to be the same as when the list of accounts is empty. It enables
329 // scheduling a periodic checkin for devices with no signed in users
330 // immediately after restart, while keeping |accounts_set == false| delays the
331 // checkin until the list of accounts is set explicitly.
332 if (result->last_checkin_accounts.size() == 0)
333 device_checkin_info_.accounts_set = true;
334 last_checkin_time_ = result->last_checkin_time;
335 gservices_settings_.UpdateFromLoadResult(*result);
336 load_result_ = result.Pass();
337 state_ = LOADED;
339 // Don't initiate the GCM connection when GCM is in delayed start mode and
340 // not any standalone app has registered GCM yet.
341 if (start_mode_ == DELAYED_START && !HasStandaloneRegisteredApp())
342 return;
344 StartGCM();
347 void GCMClientImpl::StartGCM() {
348 // Taking over the value of account_mappings before passing the ownership of
349 // load result to InitializeMCSClient.
350 std::vector<AccountMapping> account_mappings;
351 account_mappings.swap(load_result_->account_mappings);
352 base::Time last_token_fetch_time = load_result_->last_token_fetch_time;
354 InitializeMCSClient();
356 if (device_checkin_info_.IsValid()) {
357 SchedulePeriodicCheckin();
358 OnReady(account_mappings, last_token_fetch_time);
359 return;
362 state_ = INITIAL_DEVICE_CHECKIN;
363 device_checkin_info_.Reset();
364 StartCheckin();
367 void GCMClientImpl::InitializeMCSClient() {
368 std::vector<GURL> endpoints;
369 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
370 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
371 connection_factory_ = internals_builder_->BuildConnectionFactory(
372 endpoints,
373 GetGCMBackoffPolicy(),
374 network_session_,
375 url_request_context_getter_->GetURLRequestContext()
376 ->http_transaction_factory()
377 ->GetSession(),
378 net_log_.net_log(),
379 &recorder_);
380 connection_factory_->SetConnectionListener(this);
381 mcs_client_ = internals_builder_->BuildMCSClient(
382 chrome_build_info_.version,
383 clock_.get(),
384 connection_factory_.get(),
385 gcm_store_.get(),
386 &recorder_).Pass();
388 mcs_client_->Initialize(
389 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
390 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
391 weak_ptr_factory_.GetWeakPtr()),
392 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
393 weak_ptr_factory_.GetWeakPtr()),
394 load_result_.Pass());
397 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
398 const CheckinInfo& checkin_info) {
399 DCHECK(!device_checkin_info_.IsValid());
401 device_checkin_info_.android_id = checkin_info.android_id;
402 device_checkin_info_.secret = checkin_info.secret;
403 // If accounts were not set by now, we can consider them set (to empty list)
404 // to make sure periodic checkins get scheduled after initial checkin.
405 device_checkin_info_.accounts_set = true;
406 gcm_store_->SetDeviceCredentials(
407 checkin_info.android_id, checkin_info.secret,
408 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
409 weak_ptr_factory_.GetWeakPtr()));
411 OnReady(std::vector<AccountMapping>(), base::Time());
414 void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
415 const base::Time& last_token_fetch_time) {
416 state_ = READY;
417 StartMCSLogin();
419 delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
422 void GCMClientImpl::StartMCSLogin() {
423 DCHECK_EQ(READY, state_);
424 DCHECK(device_checkin_info_.IsValid());
425 mcs_client_->Login(device_checkin_info_.android_id,
426 device_checkin_info_.secret);
429 void GCMClientImpl::ResetState() {
430 state_ = UNINITIALIZED;
431 // TODO(fgorski): reset all of the necessart objects and start over.
434 void GCMClientImpl::SetAccountTokens(
435 const std::vector<AccountTokenInfo>& account_tokens) {
436 device_checkin_info_.account_tokens.clear();
437 for (std::vector<AccountTokenInfo>::const_iterator iter =
438 account_tokens.begin();
439 iter != account_tokens.end();
440 ++iter) {
441 device_checkin_info_.account_tokens[iter->email] = iter->access_token;
444 bool accounts_set_before = device_checkin_info_.accounts_set;
445 device_checkin_info_.accounts_set = true;
447 DVLOG(1) << "Set account called with: " << account_tokens.size()
448 << " accounts.";
450 if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
451 return;
453 bool account_removed = false;
454 for (std::set<std::string>::iterator iter =
455 device_checkin_info_.last_checkin_accounts.begin();
456 iter != device_checkin_info_.last_checkin_accounts.end();
457 ++iter) {
458 if (device_checkin_info_.account_tokens.find(*iter) ==
459 device_checkin_info_.account_tokens.end()) {
460 account_removed = true;
464 // Checkin will be forced when any of the accounts was removed during the
465 // current Chrome session or if there has been an account removed between the
466 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
467 // We only force checkin when user signs out. When there is a new account
468 // signed in, the periodic checkin will take care of adding the association in
469 // reasonable time.
470 if (account_removed) {
471 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
472 checkin_request_.reset();
473 StartCheckin();
474 } else if (!accounts_set_before) {
475 SchedulePeriodicCheckin();
476 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
480 void GCMClientImpl::UpdateAccountMapping(
481 const AccountMapping& account_mapping) {
482 gcm_store_->AddAccountMapping(account_mapping,
483 base::Bind(&GCMClientImpl::DefaultStoreCallback,
484 weak_ptr_factory_.GetWeakPtr()));
487 void GCMClientImpl::RemoveAccountMapping(const std::string& account_id) {
488 gcm_store_->RemoveAccountMapping(
489 account_id,
490 base::Bind(&GCMClientImpl::DefaultStoreCallback,
491 weak_ptr_factory_.GetWeakPtr()));
494 void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
495 gcm_store_->SetLastTokenFetchTime(
496 time,
497 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback,
498 weak_ptr_factory_.GetWeakPtr()));
501 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) {
502 DCHECK(mcs_client_);
503 mcs_client_->UpdateHeartbeatTimer(timer.Pass());
506 void GCMClientImpl::StartCheckin() {
507 // Make sure no checkin is in progress.
508 if (checkin_request_.get())
509 return;
511 checkin_proto::ChromeBuildProto chrome_build_proto;
512 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
513 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
514 device_checkin_info_.secret,
515 device_checkin_info_.account_tokens,
516 gservices_settings_.digest(),
517 chrome_build_proto);
518 checkin_request_.reset(
519 new CheckinRequest(gservices_settings_.GetCheckinURL(),
520 request_info,
521 GetGCMBackoffPolicy(),
522 base::Bind(&GCMClientImpl::OnCheckinCompleted,
523 weak_ptr_factory_.GetWeakPtr()),
524 url_request_context_getter_.get(),
525 &recorder_));
526 // Taking a snapshot of the accounts count here, as there might be an asynch
527 // update of the account tokens while checkin is in progress.
528 device_checkin_info_.SnapshotCheckinAccounts();
529 checkin_request_->Start();
532 void GCMClientImpl::OnCheckinCompleted(
533 const checkin_proto::AndroidCheckinResponse& checkin_response) {
534 checkin_request_.reset();
536 if (!checkin_response.has_android_id() ||
537 !checkin_response.has_security_token()) {
538 // TODO(fgorski): I don't think a retry here will help, we should probably
539 // start over. By checking in with (0, 0).
540 return;
543 CheckinInfo checkin_info;
544 checkin_info.android_id = checkin_response.android_id();
545 checkin_info.secret = checkin_response.security_token();
547 if (state_ == INITIAL_DEVICE_CHECKIN) {
548 OnFirstTimeDeviceCheckinCompleted(checkin_info);
549 } else {
550 // checkin_info is not expected to change after a periodic checkin as it
551 // would invalidate the registratoin IDs.
552 DCHECK_EQ(READY, state_);
553 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
554 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
557 if (device_checkin_info_.IsValid()) {
558 // First update G-services settings, as something might have changed.
559 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
560 gcm_store_->SetGServicesSettings(
561 gservices_settings_.settings_map(),
562 gservices_settings_.digest(),
563 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
564 weak_ptr_factory_.GetWeakPtr()));
567 last_checkin_time_ = clock_->Now();
568 gcm_store_->SetLastCheckinInfo(
569 last_checkin_time_,
570 device_checkin_info_.last_checkin_accounts,
571 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback,
572 weak_ptr_factory_.GetWeakPtr()));
573 SchedulePeriodicCheckin();
577 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
578 DCHECK(success);
581 void GCMClientImpl::SchedulePeriodicCheckin() {
582 // Make sure no checkin is in progress.
583 if (checkin_request_.get() || !device_checkin_info_.accounts_set)
584 return;
586 // There should be only one periodic checkin pending at a time. Removing
587 // pending periodic checkin to schedule a new one.
588 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
590 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
591 if (time_to_next_checkin < base::TimeDelta())
592 time_to_next_checkin = base::TimeDelta();
594 base::MessageLoop::current()->PostDelayedTask(
595 FROM_HERE,
596 base::Bind(&GCMClientImpl::StartCheckin,
597 periodic_checkin_ptr_factory_.GetWeakPtr()),
598 time_to_next_checkin);
601 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
602 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
603 clock_->Now();
606 void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
607 // TODO(fgorski): This is one of the signals that store needs a rebuild.
608 DCHECK(success);
611 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
612 // TODO(fgorski): This is one of the signals that store needs a rebuild.
613 DCHECK(success);
616 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
617 // TODO(fgorski): This is one of the signals that store needs a rebuild.
618 DCHECK(success);
621 void GCMClientImpl::DefaultStoreCallback(bool success) {
622 DCHECK(success);
625 void GCMClientImpl::IgnoreWriteResultCallback(bool success) {
626 // TODO(fgorski): Ignoring the write result for now to make sure
627 // sync_intergration_tests are not broken.
630 void GCMClientImpl::Stop() {
631 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
632 // Shutdown.
633 DVLOG(1) << "Stopping the GCM Client";
634 weak_ptr_factory_.InvalidateWeakPtrs();
635 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
636 device_checkin_info_.Reset();
637 connection_factory_.reset();
638 delegate_->OnDisconnected();
639 mcs_client_.reset();
640 checkin_request_.reset();
641 // Delete all of the pending registration and unregistration requests.
642 STLDeleteValues(&pending_registration_requests_);
643 STLDeleteValues(&pending_unregistration_requests_);
644 state_ = INITIALIZED;
645 gcm_store_->Close();
648 void GCMClientImpl::Register(const std::string& app_id,
649 const std::vector<std::string>& sender_ids) {
650 DCHECK_EQ(state_, READY);
652 // If the same sender ids is provided, return the cached registration ID
653 // directly.
654 RegistrationInfoMap::const_iterator registrations_iter =
655 registrations_.find(app_id);
656 if (registrations_iter != registrations_.end() &&
657 registrations_iter->second->sender_ids == sender_ids) {
658 delegate_->OnRegisterFinished(
659 app_id, registrations_iter->second->registration_id, SUCCESS);
660 return;
663 RegistrationRequest::RequestInfo request_info(
664 device_checkin_info_.android_id,
665 device_checkin_info_.secret,
666 app_id,
667 sender_ids);
668 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
670 RegistrationRequest* registration_request =
671 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
672 request_info,
673 GetGCMBackoffPolicy(),
674 base::Bind(&GCMClientImpl::OnRegisterCompleted,
675 weak_ptr_factory_.GetWeakPtr(),
676 app_id,
677 sender_ids),
678 kMaxRegistrationRetries,
679 url_request_context_getter_,
680 &recorder_);
681 pending_registration_requests_[app_id] = registration_request;
682 registration_request->Start();
685 void GCMClientImpl::OnRegisterCompleted(
686 const std::string& app_id,
687 const std::vector<std::string>& sender_ids,
688 RegistrationRequest::Status status,
689 const std::string& registration_id) {
690 DCHECK(delegate_);
692 Result result;
693 PendingRegistrationRequests::iterator iter =
694 pending_registration_requests_.find(app_id);
695 if (iter == pending_registration_requests_.end())
696 result = UNKNOWN_ERROR;
697 else if (status == RegistrationRequest::INVALID_SENDER)
698 result = INVALID_PARAMETER;
699 else if (registration_id.empty())
700 result = SERVER_ERROR;
701 else
702 result = SUCCESS;
704 if (result == SUCCESS) {
705 // Cache it.
706 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
707 registration->sender_ids = sender_ids;
708 registration->registration_id = registration_id;
709 registrations_[app_id] = registration;
711 // Save it in the persistent store.
712 gcm_store_->AddRegistration(
713 app_id,
714 registration,
715 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
716 weak_ptr_factory_.GetWeakPtr()));
719 delegate_->OnRegisterFinished(
720 app_id, result == SUCCESS ? registration_id : std::string(), result);
722 if (iter != pending_registration_requests_.end()) {
723 delete iter->second;
724 pending_registration_requests_.erase(iter);
728 void GCMClientImpl::Unregister(const std::string& app_id) {
729 DCHECK_EQ(state_, READY);
730 if (pending_unregistration_requests_.count(app_id) == 1)
731 return;
733 // Remove from the cache and persistent store.
734 registrations_.erase(app_id);
735 gcm_store_->RemoveRegistration(
736 app_id,
737 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
738 weak_ptr_factory_.GetWeakPtr()));
740 UnregistrationRequest::RequestInfo request_info(
741 device_checkin_info_.android_id,
742 device_checkin_info_.secret,
743 app_id);
745 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
746 gservices_settings_.GetRegistrationURL(),
747 request_info,
748 GetGCMBackoffPolicy(),
749 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
750 weak_ptr_factory_.GetWeakPtr(),
751 app_id),
752 url_request_context_getter_,
753 &recorder_);
754 pending_unregistration_requests_[app_id] = unregistration_request;
755 unregistration_request->Start();
758 void GCMClientImpl::OnUnregisterCompleted(
759 const std::string& app_id,
760 UnregistrationRequest::Status status) {
761 DVLOG(1) << "Unregister completed for app: " << app_id
762 << " with " << (status ? "success." : "failure.");
763 delegate_->OnUnregisterFinished(
764 app_id,
765 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
767 PendingUnregistrationRequests::iterator iter =
768 pending_unregistration_requests_.find(app_id);
769 if (iter == pending_unregistration_requests_.end())
770 return;
772 delete iter->second;
773 pending_unregistration_requests_.erase(iter);
776 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
777 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
778 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
781 void GCMClientImpl::Send(const std::string& app_id,
782 const std::string& receiver_id,
783 const OutgoingMessage& message) {
784 DCHECK_EQ(state_, READY);
786 RecordOutgoingMessageToUMA(message);
788 mcs_proto::DataMessageStanza stanza;
789 stanza.set_ttl(message.time_to_live);
790 stanza.set_sent(clock_->Now().ToInternalValue() /
791 base::Time::kMicrosecondsPerSecond);
792 stanza.set_id(message.id);
793 stanza.set_from(kSendMessageFromValue);
794 stanza.set_to(receiver_id);
795 stanza.set_category(app_id);
797 for (MessageData::const_iterator iter = message.data.begin();
798 iter != message.data.end();
799 ++iter) {
800 mcs_proto::AppData* app_data = stanza.add_app_data();
801 app_data->set_key(iter->first);
802 app_data->set_value(iter->second);
805 MCSMessage mcs_message(stanza);
806 DVLOG(1) << "MCS message size: " << mcs_message.size();
807 mcs_client_->SendMessage(mcs_message);
810 std::string GCMClientImpl::GetStateString() const {
811 switch(state_) {
812 case GCMClientImpl::INITIALIZED:
813 return "INITIALIZED";
814 case GCMClientImpl::UNINITIALIZED:
815 return "UNINITIALIZED";
816 case GCMClientImpl::LOADING:
817 return "LOADING";
818 case GCMClientImpl::LOADED:
819 return "LOADED";
820 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
821 return "INITIAL_DEVICE_CHECKIN";
822 case GCMClientImpl::READY:
823 return "READY";
824 default:
825 NOTREACHED();
826 return std::string();
830 void GCMClientImpl::SetRecording(bool recording) {
831 recorder_.SetRecording(recording);
834 void GCMClientImpl::ClearActivityLogs() {
835 recorder_.Clear();
838 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
839 GCMClient::GCMStatistics stats;
840 stats.gcm_client_created = true;
841 stats.is_recording = recorder_.is_recording();
842 stats.gcm_client_state = GetStateString();
843 stats.connection_client_created = mcs_client_.get() != NULL;
844 if (connection_factory_.get())
845 stats.connection_state = connection_factory_->GetConnectionStateString();
846 if (mcs_client_.get()) {
847 stats.send_queue_size = mcs_client_->GetSendQueueSize();
848 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
850 if (device_checkin_info_.android_id > 0)
851 stats.android_id = device_checkin_info_.android_id;
852 recorder_.CollectActivities(&stats.recorded_activities);
854 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
855 it != registrations_.end(); ++it) {
856 stats.registered_app_ids.push_back(it->first);
858 return stats;
861 void GCMClientImpl::OnActivityRecorded() {
862 delegate_->OnActivityRecorded();
865 void GCMClientImpl::OnConnected(const GURL& current_server,
866 const net::IPEndPoint& ip_endpoint) {
867 // TODO(gcm): expose current server in debug page.
868 delegate_->OnActivityRecorded();
869 delegate_->OnConnected(ip_endpoint);
872 void GCMClientImpl::OnDisconnected() {
873 delegate_->OnActivityRecorded();
874 delegate_->OnDisconnected();
877 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
878 switch (message.tag()) {
879 case kLoginResponseTag:
880 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
881 return;
882 case kDataMessageStanzaTag:
883 DVLOG(1) << "A downstream message received. Processing...";
884 HandleIncomingMessage(message);
885 return;
886 default:
887 NOTREACHED() << "Message with unexpected tag received by GCMClient";
888 return;
892 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
893 const std::string& app_id,
894 const std::string& message_id,
895 MCSClient::MessageSendStatus status) {
896 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
897 DCHECK(delegate_);
899 // TTL_EXCEEDED is singled out here, because it can happen long time after the
900 // message was sent. That is why it comes as |OnMessageSendError| event rather
901 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
902 // All other errors will be raised immediately, through asynchronous callback.
903 // It is expected that TTL_EXCEEDED will be issued for a message that was
904 // previously issued |OnSendFinished| with status SUCCESS.
905 // TODO(jianli): Consider adding UMA for this status.
906 if (status == MCSClient::TTL_EXCEEDED) {
907 SendErrorDetails send_error_details;
908 send_error_details.message_id = message_id;
909 send_error_details.result = GCMClient::TTL_EXCEEDED;
910 delegate_->OnMessageSendError(app_id, send_error_details);
911 } else if (status == MCSClient::SENT) {
912 delegate_->OnSendAcknowledged(app_id, message_id);
913 } else {
914 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
918 void GCMClientImpl::OnMCSError() {
919 // TODO(fgorski): For now it replaces the initialization method. Long term it
920 // should have an error or status passed in.
923 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
924 DCHECK(delegate_);
926 const mcs_proto::DataMessageStanza& data_message_stanza =
927 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
928 message.GetProtobuf());
929 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
931 // Copying all the data from the stanza to a MessageData object. When present,
932 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
933 // later.
934 MessageData message_data;
935 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
936 std::string key = data_message_stanza.app_data(i).key();
937 message_data[key] = data_message_stanza.app_data(i).value();
940 MessageType message_type = DATA_MESSAGE;
941 MessageData::iterator iter = message_data.find(kMessageTypeKey);
942 if (iter != message_data.end()) {
943 message_type = DecodeMessageType(iter->second);
944 message_data.erase(iter);
947 switch (message_type) {
948 case DATA_MESSAGE:
949 HandleIncomingDataMessage(data_message_stanza, message_data);
950 break;
951 case DELETED_MESSAGES:
952 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
953 data_message_stanza.from(),
954 data_message_stanza.ByteSize(),
955 true,
956 GCMStatsRecorder::DELETED_MESSAGES);
957 delegate_->OnMessagesDeleted(data_message_stanza.category());
958 break;
959 case SEND_ERROR:
960 HandleIncomingSendError(data_message_stanza, message_data);
961 break;
962 case UNKNOWN:
963 default: // Treat default the same as UNKNOWN.
964 DVLOG(1) << "Unknown message_type received. Message ignored. "
965 << "App ID: " << data_message_stanza.category() << ".";
966 break;
970 void GCMClientImpl::HandleIncomingDataMessage(
971 const mcs_proto::DataMessageStanza& data_message_stanza,
972 MessageData& message_data) {
973 std::string app_id = data_message_stanza.category();
975 // Drop the message when the app is not registered for the sender of the
976 // message.
977 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
978 bool not_registered =
979 iter == registrations_.end() ||
980 std::find(iter->second->sender_ids.begin(),
981 iter->second->sender_ids.end(),
982 data_message_stanza.from()) == iter->second->sender_ids.end();
983 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
984 data_message_stanza.ByteSize(), !not_registered,
985 GCMStatsRecorder::DATA_MESSAGE);
986 if (not_registered) {
987 return;
990 IncomingMessage incoming_message;
991 incoming_message.sender_id = data_message_stanza.from();
992 if (data_message_stanza.has_token())
993 incoming_message.collapse_key = data_message_stanza.token();
994 incoming_message.data = message_data;
995 delegate_->OnMessageReceived(app_id, incoming_message);
998 void GCMClientImpl::HandleIncomingSendError(
999 const mcs_proto::DataMessageStanza& data_message_stanza,
1000 MessageData& message_data) {
1001 SendErrorDetails send_error_details;
1002 send_error_details.additional_data = message_data;
1003 send_error_details.result = SERVER_ERROR;
1005 MessageData::iterator iter =
1006 send_error_details.additional_data.find(kSendErrorMessageIdKey);
1007 if (iter != send_error_details.additional_data.end()) {
1008 send_error_details.message_id = iter->second;
1009 send_error_details.additional_data.erase(iter);
1012 recorder_.RecordIncomingSendError(
1013 data_message_stanza.category(),
1014 data_message_stanza.to(),
1015 data_message_stanza.id());
1016 delegate_->OnMessageSendError(data_message_stanza.category(),
1017 send_error_details);
1020 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1021 if (registrations_.empty())
1022 return false;
1023 // Note that account mapper is not counted as a standalone app since it is
1024 // automatically started when other app uses GCM.
1025 return registrations_.size() > 1 ||
1026 !registrations_.count(kGCMAccountMapperAppId);
1029 } // namespace gcm