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.
4
5 #include "components/gcm_driver/gcm_client_impl.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/default_clock.h"
17 #include "google_apis/gcm/base/encryptor.h"
18 #include "google_apis/gcm/base/mcs_message.h"
19 #include "google_apis/gcm/base/mcs_util.h"
20 #include "google_apis/gcm/engine/checkin_request.h"
21 #include "google_apis/gcm/engine/connection_factory_impl.h"
22 #include "google_apis/gcm/engine/gcm_store_impl.h"
23 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
24 #include "google_apis/gcm/protocol/checkin.pb.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "net/http/http_network_session.h"
27 #include "net/url_request/url_request_context.h"
28 #include "url/gurl.h"
29
30 namespace gcm {
31
32 namespace {
33
34 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
35 // retries.
36 // Note: In order to ensure a minimum of 20 seconds between server errors (for
37 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
38 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
39 // themselves.
40 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
41 // Number of initial errors (in sequence) to ignore before applying
42 // exponential back-off rules.
43 0,
44
45 // Initial delay for exponential back-off in ms.
46 30 * 1000, // 30 seconds.
47
48 // Factor by which the waiting time will be multiplied.
49 2,
50
51 // Fuzzing percentage. ex: 10% will spread requests randomly
52 // between 90%-100% of the calculated time.
53 0.33, // 33%.
54
55 // Maximum amount of time we are willing to delay our request in ms.
56 10 * 60 * 1000, // 10 minutes.
57
58 // Time to keep an entry from being discarded even when it
59 // has no significant state, -1 to never discard.
60 -1,
61
62 // Don't use initial delay unless the last request was an error.
63 false,
64 };
65
66 // Indicates a message type of the received message.
67 enum MessageType {
68 UNKNOWN, // Undetermined type.
69 DATA_MESSAGE, // Regular data message.
70 DELETED_MESSAGES, // Messages were deleted on the server.
71 SEND_ERROR, // Error sending a message.
72 };
73
74 enum OutgoingMessageTTLCategory {
75 TTL_ZERO,
76 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
78 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
79 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
80 TTL_MORE_THAN_ONE_WEEK,
81 TTL_MAXIMUM,
82 // NOTE: always keep this entry at the end. Add new TTL category only
83 // immediately above this line. Make sure to update the corresponding
84 // histogram enum accordingly.
85 TTL_CATEGORY_COUNT
86 };
87
88 const int kMaxRegistrationRetries = 5;
89 const char kMessageTypeDataMessage[] = "gcm";
90 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
91 const char kMessageTypeKey[] = "message_type";
92 const char kMessageTypeSendErrorKey[] = "send_error";
93 const char kSendErrorMessageIdKey[] = "google.message_id";
94 const char kSendMessageFromValue[] = "gcm@chrome.com";
95 const int64 kDefaultUserSerialNumber = 0LL;
96
ToGCMClientResult(MCSClient::MessageSendStatus status)97 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
98 switch (status) {
99 case MCSClient::QUEUED:
100 return GCMClient::SUCCESS;
101 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
102 return GCMClient::NETWORK_ERROR;
103 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
104 return GCMClient::NETWORK_ERROR;
105 case MCSClient::MESSAGE_TOO_LARGE:
106 return GCMClient::INVALID_PARAMETER;
107 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
108 return GCMClient::NETWORK_ERROR;
109 case MCSClient::TTL_EXCEEDED:
110 return GCMClient::NETWORK_ERROR;
111 case MCSClient::SENT:
112 default:
113 NOTREACHED();
114 break;
115 }
116 return GCMClientImpl::UNKNOWN_ERROR;
117 }
118
ToCheckinProtoVersion(const GCMClient::ChromeBuildInfo & chrome_build_info,checkin_proto::ChromeBuildProto * android_build_info)119 void ToCheckinProtoVersion(
120 const GCMClient::ChromeBuildInfo& chrome_build_info,
121 checkin_proto::ChromeBuildProto* android_build_info) {
122 checkin_proto::ChromeBuildProto_Platform platform;
123 switch (chrome_build_info.platform) {
124 case GCMClient::PLATFORM_WIN:
125 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
126 break;
127 case GCMClient::PLATFORM_MAC:
128 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
129 break;
130 case GCMClient::PLATFORM_LINUX:
131 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
132 break;
133 case GCMClient::PLATFORM_IOS:
134 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
135 break;
136 case GCMClient::PLATFORM_ANDROID:
137 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
138 break;
139 case GCMClient::PLATFORM_CROS:
140 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
141 break;
142 case GCMClient::PLATFORM_UNKNOWN:
143 // For unknown platform, return as LINUX.
144 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
145 break;
146 default:
147 NOTREACHED();
148 platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
149 break;
150 }
151 android_build_info->set_platform(platform);
152
153 checkin_proto::ChromeBuildProto_Channel channel;
154 switch (chrome_build_info.channel) {
155 case GCMClient::CHANNEL_STABLE:
156 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
157 break;
158 case GCMClient::CHANNEL_BETA:
159 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
160 break;
161 case GCMClient::CHANNEL_DEV:
162 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
163 break;
164 case GCMClient::CHANNEL_CANARY:
165 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
166 break;
167 case GCMClient::CHANNEL_UNKNOWN:
168 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
169 break;
170 default:
171 NOTREACHED();
172 channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
173 break;
174 }
175 android_build_info->set_channel(channel);
176
177 android_build_info->set_chrome_version(chrome_build_info.version);
178 }
179
DecodeMessageType(const std::string & value)180 MessageType DecodeMessageType(const std::string& value) {
181 if (kMessageTypeDeletedMessagesKey == value)
182 return DELETED_MESSAGES;
183 if (kMessageTypeSendErrorKey == value)
184 return SEND_ERROR;
185 if (kMessageTypeDataMessage == value)
186 return DATA_MESSAGE;
187 return UNKNOWN;
188 }
189
RecordOutgoingMessageToUMA(const gcm::GCMClient::OutgoingMessage & message)190 void RecordOutgoingMessageToUMA(
191 const gcm::GCMClient::OutgoingMessage& message) {
192 OutgoingMessageTTLCategory ttl_category;
193 if (message.time_to_live == 0)
194 ttl_category = TTL_ZERO;
195 else if (message.time_to_live <= 60 )
196 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
197 else if (message.time_to_live <= 60 * 60)
198 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
199 else if (message.time_to_live <= 24 * 60 * 60)
200 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
201 else if (message.time_to_live <= 7 * 24 * 60 * 60)
202 ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
203 else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
204 ttl_category = TTL_MORE_THAN_ONE_WEEK;
205 else
206 ttl_category = TTL_MAXIMUM;
207
208 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
209 ttl_category,
210 TTL_CATEGORY_COUNT);
211 }
212
213 } // namespace
214
GCMInternalsBuilder()215 GCMInternalsBuilder::GCMInternalsBuilder() {}
~GCMInternalsBuilder()216 GCMInternalsBuilder::~GCMInternalsBuilder() {}
217
BuildClock()218 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
219 return make_scoped_ptr<base::Clock>(new base::DefaultClock());
220 }
221
BuildMCSClient(const std::string & version,base::Clock * clock,ConnectionFactory * connection_factory,GCMStore * gcm_store,GCMStatsRecorder * recorder)222 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
223 const std::string& version,
224 base::Clock* clock,
225 ConnectionFactory* connection_factory,
226 GCMStore* gcm_store,
227 GCMStatsRecorder* recorder) {
228 return make_scoped_ptr<MCSClient>(
229 new MCSClient(version,
230 clock,
231 connection_factory,
232 gcm_store,
233 recorder));
234 }
235
BuildConnectionFactory(const std::vector<GURL> & endpoints,const net::BackoffEntry::Policy & backoff_policy,scoped_refptr<net::HttpNetworkSession> network_session,net::NetLog * net_log,GCMStatsRecorder * recorder)236 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
237 const std::vector<GURL>& endpoints,
238 const net::BackoffEntry::Policy& backoff_policy,
239 scoped_refptr<net::HttpNetworkSession> network_session,
240 net::NetLog* net_log,
241 GCMStatsRecorder* recorder) {
242 return make_scoped_ptr<ConnectionFactory>(
243 new ConnectionFactoryImpl(endpoints,
244 backoff_policy,
245 network_session,
246 net_log,
247 recorder));
248 }
249
GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)250 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
251 : internals_builder_(internals_builder.Pass()),
252 state_(UNINITIALIZED),
253 delegate_(NULL),
254 clock_(internals_builder_->BuildClock()),
255 url_request_context_getter_(NULL),
256 pending_registration_requests_deleter_(&pending_registration_requests_),
257 pending_unregistration_requests_deleter_(
258 &pending_unregistration_requests_),
259 periodic_checkin_ptr_factory_(this),
260 weak_ptr_factory_(this) {
261 }
262
~GCMClientImpl()263 GCMClientImpl::~GCMClientImpl() {
264 }
265
Initialize(const ChromeBuildInfo & chrome_build_info,const base::FilePath & path,const scoped_refptr<base::SequencedTaskRunner> & blocking_task_runner,const scoped_refptr<net::URLRequestContextGetter> & url_request_context_getter,scoped_ptr<Encryptor> encryptor,GCMClient::Delegate * delegate)266 void GCMClientImpl::Initialize(
267 const ChromeBuildInfo& chrome_build_info,
268 const base::FilePath& path,
269 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
270 const scoped_refptr<net::URLRequestContextGetter>&
271 url_request_context_getter,
272 scoped_ptr<Encryptor> encryptor,
273 GCMClient::Delegate* delegate) {
274 DCHECK_EQ(UNINITIALIZED, state_);
275 DCHECK(url_request_context_getter);
276 DCHECK(delegate);
277
278 url_request_context_getter_ = url_request_context_getter;
279 const net::HttpNetworkSession::Params* network_session_params =
280 url_request_context_getter_->GetURLRequestContext()->
281 GetNetworkSessionParams();
282 DCHECK(network_session_params);
283 network_session_ = new net::HttpNetworkSession(*network_session_params);
284
285 chrome_build_info_ = chrome_build_info;
286
287 gcm_store_.reset(
288 new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
289
290 delegate_ = delegate;
291
292 recorder_.SetDelegate(this);
293
294 state_ = INITIALIZED;
295 }
296
Start()297 void GCMClientImpl::Start() {
298 DCHECK_EQ(INITIALIZED, state_);
299
300 // Once the loading is completed, the check-in will be initiated.
301 gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
302 weak_ptr_factory_.GetWeakPtr()));
303 state_ = LOADING;
304 }
305
OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result)306 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
307 DCHECK_EQ(LOADING, state_);
308
309 if (!result->success) {
310 ResetState();
311 return;
312 }
313
314 registrations_ = result->registrations;
315 device_checkin_info_.android_id = result->device_android_id;
316 device_checkin_info_.secret = result->device_security_token;
317 last_checkin_time_ = result->last_checkin_time;
318 gservices_settings_.UpdateFromLoadResult(*result);
319 InitializeMCSClient(result.Pass());
320
321 if (device_checkin_info_.IsValid()) {
322 SchedulePeriodicCheckin();
323 OnReady();
324 return;
325 }
326
327 state_ = INITIAL_DEVICE_CHECKIN;
328 device_checkin_info_.Reset();
329 StartCheckin();
330 }
331
InitializeMCSClient(scoped_ptr<GCMStore::LoadResult> result)332 void GCMClientImpl::InitializeMCSClient(
333 scoped_ptr<GCMStore::LoadResult> result) {
334 std::vector<GURL> endpoints;
335 endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
336 endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
337 connection_factory_ = internals_builder_->BuildConnectionFactory(
338 endpoints,
339 kDefaultBackoffPolicy,
340 network_session_,
341 net_log_.net_log(),
342 &recorder_);
343 connection_factory_->SetConnectionListener(this);
344 mcs_client_ = internals_builder_->BuildMCSClient(
345 chrome_build_info_.version,
346 clock_.get(),
347 connection_factory_.get(),
348 gcm_store_.get(),
349 &recorder_).Pass();
350
351 mcs_client_->Initialize(
352 base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
353 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
354 weak_ptr_factory_.GetWeakPtr()),
355 base::Bind(&GCMClientImpl::OnMessageSentToMCS,
356 weak_ptr_factory_.GetWeakPtr()),
357 result.Pass());
358 }
359
OnFirstTimeDeviceCheckinCompleted(const CheckinInfo & checkin_info)360 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
361 const CheckinInfo& checkin_info) {
362 DCHECK(!device_checkin_info_.IsValid());
363
364 device_checkin_info_.android_id = checkin_info.android_id;
365 device_checkin_info_.secret = checkin_info.secret;
366 gcm_store_->SetDeviceCredentials(
367 checkin_info.android_id, checkin_info.secret,
368 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
369 weak_ptr_factory_.GetWeakPtr()));
370
371 OnReady();
372 }
373
OnReady()374 void GCMClientImpl::OnReady() {
375 state_ = READY;
376 StartMCSLogin();
377
378 delegate_->OnGCMReady();
379 }
380
StartMCSLogin()381 void GCMClientImpl::StartMCSLogin() {
382 DCHECK_EQ(READY, state_);
383 DCHECK(device_checkin_info_.IsValid());
384 mcs_client_->Login(device_checkin_info_.android_id,
385 device_checkin_info_.secret);
386 }
387
ResetState()388 void GCMClientImpl::ResetState() {
389 state_ = UNINITIALIZED;
390 // TODO(fgorski): reset all of the necessart objects and start over.
391 }
392
StartCheckin()393 void GCMClientImpl::StartCheckin() {
394 // Make sure no checkin is in progress.
395 if (checkin_request_.get())
396 return;
397
398 checkin_proto::ChromeBuildProto chrome_build_proto;
399 ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
400 CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
401 device_checkin_info_.secret,
402 gservices_settings_.digest(),
403 chrome_build_proto);
404 checkin_request_.reset(
405 new CheckinRequest(gservices_settings_.GetCheckinURL(),
406 request_info,
407 kDefaultBackoffPolicy,
408 base::Bind(&GCMClientImpl::OnCheckinCompleted,
409 weak_ptr_factory_.GetWeakPtr()),
410 url_request_context_getter_,
411 &recorder_));
412 checkin_request_->Start();
413 }
414
OnCheckinCompleted(const checkin_proto::AndroidCheckinResponse & checkin_response)415 void GCMClientImpl::OnCheckinCompleted(
416 const checkin_proto::AndroidCheckinResponse& checkin_response) {
417 checkin_request_.reset();
418
419 if (!checkin_response.has_android_id() ||
420 !checkin_response.has_security_token()) {
421 // TODO(fgorski): I don't think a retry here will help, we should probably
422 // start over. By checking in with (0, 0).
423 return;
424 }
425
426 CheckinInfo checkin_info;
427 checkin_info.android_id = checkin_response.android_id();
428 checkin_info.secret = checkin_response.security_token();
429
430 if (state_ == INITIAL_DEVICE_CHECKIN) {
431 OnFirstTimeDeviceCheckinCompleted(checkin_info);
432 } else {
433 // checkin_info is not expected to change after a periodic checkin as it
434 // would invalidate the registratoin IDs.
435 DCHECK_EQ(READY, state_);
436 DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
437 DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
438 }
439
440 if (device_checkin_info_.IsValid()) {
441 // First update G-services settings, as something might have changed.
442 if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
443 gcm_store_->SetGServicesSettings(
444 gservices_settings_.settings_map(),
445 gservices_settings_.digest(),
446 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
447 weak_ptr_factory_.GetWeakPtr()));
448 }
449
450 last_checkin_time_ = clock_->Now();
451 gcm_store_->SetLastCheckinTime(
452 last_checkin_time_,
453 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
454 weak_ptr_factory_.GetWeakPtr()));
455 SchedulePeriodicCheckin();
456 }
457 }
458
SetGServicesSettingsCallback(bool success)459 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
460 DCHECK(success);
461 }
462
SchedulePeriodicCheckin()463 void GCMClientImpl::SchedulePeriodicCheckin() {
464 // Make sure no checkin is in progress.
465 if (checkin_request_.get())
466 return;
467
468 // There should be only one periodic checkin pending at a time. Removing
469 // pending periodic checkin to schedule a new one.
470 periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
471
472 base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
473 if (time_to_next_checkin < base::TimeDelta())
474 time_to_next_checkin = base::TimeDelta();
475
476 base::MessageLoop::current()->PostDelayedTask(
477 FROM_HERE,
478 base::Bind(&GCMClientImpl::StartCheckin,
479 periodic_checkin_ptr_factory_.GetWeakPtr()),
480 time_to_next_checkin);
481 }
482
GetTimeToNextCheckin() const483 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
484 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
485 clock_->Now();
486 }
487
SetLastCheckinTimeCallback(bool success)488 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
489 // TODO(fgorski): This is one of the signals that store needs a rebuild.
490 DCHECK(success);
491 }
492
SetDeviceCredentialsCallback(bool success)493 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
494 // TODO(fgorski): This is one of the signals that store needs a rebuild.
495 DCHECK(success);
496 }
497
UpdateRegistrationCallback(bool success)498 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
499 // TODO(fgorski): This is one of the signals that store needs a rebuild.
500 DCHECK(success);
501 }
502
Stop()503 void GCMClientImpl::Stop() {
504 weak_ptr_factory_.InvalidateWeakPtrs();
505 device_checkin_info_.Reset();
506 connection_factory_.reset();
507 delegate_->OnDisconnected();
508 mcs_client_.reset();
509 checkin_request_.reset();
510 pending_registration_requests_.clear();
511 state_ = INITIALIZED;
512 gcm_store_->Close();
513 }
514
CheckOut()515 void GCMClientImpl::CheckOut() {
516 Stop();
517 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
518 weak_ptr_factory_.GetWeakPtr()));
519 }
520
Register(const std::string & app_id,const std::vector<std::string> & sender_ids)521 void GCMClientImpl::Register(const std::string& app_id,
522 const std::vector<std::string>& sender_ids) {
523 DCHECK_EQ(state_, READY);
524
525 // If the same sender ids is provided, return the cached registration ID
526 // directly.
527 RegistrationInfoMap::const_iterator registrations_iter =
528 registrations_.find(app_id);
529 if (registrations_iter != registrations_.end() &&
530 registrations_iter->second->sender_ids == sender_ids) {
531 delegate_->OnRegisterFinished(
532 app_id, registrations_iter->second->registration_id, SUCCESS);
533 return;
534 }
535
536 RegistrationRequest::RequestInfo request_info(
537 device_checkin_info_.android_id,
538 device_checkin_info_.secret,
539 app_id,
540 sender_ids);
541 DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
542
543 RegistrationRequest* registration_request =
544 new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
545 request_info,
546 kDefaultBackoffPolicy,
547 base::Bind(&GCMClientImpl::OnRegisterCompleted,
548 weak_ptr_factory_.GetWeakPtr(),
549 app_id,
550 sender_ids),
551 kMaxRegistrationRetries,
552 url_request_context_getter_,
553 &recorder_);
554 pending_registration_requests_[app_id] = registration_request;
555 registration_request->Start();
556 }
557
OnRegisterCompleted(const std::string & app_id,const std::vector<std::string> & sender_ids,RegistrationRequest::Status status,const std::string & registration_id)558 void GCMClientImpl::OnRegisterCompleted(
559 const std::string& app_id,
560 const std::vector<std::string>& sender_ids,
561 RegistrationRequest::Status status,
562 const std::string& registration_id) {
563 DCHECK(delegate_);
564
565 Result result;
566 PendingRegistrationRequests::iterator iter =
567 pending_registration_requests_.find(app_id);
568 if (iter == pending_registration_requests_.end())
569 result = UNKNOWN_ERROR;
570 else if (status == RegistrationRequest::INVALID_SENDER)
571 result = INVALID_PARAMETER;
572 else if (registration_id.empty())
573 result = SERVER_ERROR;
574 else
575 result = SUCCESS;
576
577 if (result == SUCCESS) {
578 // Cache it.
579 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
580 registration->sender_ids = sender_ids;
581 registration->registration_id = registration_id;
582 registrations_[app_id] = registration;
583
584 // Save it in the persistent store.
585 gcm_store_->AddRegistration(
586 app_id,
587 registration,
588 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
589 weak_ptr_factory_.GetWeakPtr()));
590 }
591
592 delegate_->OnRegisterFinished(
593 app_id, result == SUCCESS ? registration_id : std::string(), result);
594
595 if (iter != pending_registration_requests_.end()) {
596 delete iter->second;
597 pending_registration_requests_.erase(iter);
598 }
599 }
600
Unregister(const std::string & app_id)601 void GCMClientImpl::Unregister(const std::string& app_id) {
602 DCHECK_EQ(state_, READY);
603 if (pending_unregistration_requests_.count(app_id) == 1)
604 return;
605
606 // Remove from the cache and persistent store.
607 registrations_.erase(app_id);
608 gcm_store_->RemoveRegistration(
609 app_id,
610 base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
611 weak_ptr_factory_.GetWeakPtr()));
612
613 UnregistrationRequest::RequestInfo request_info(
614 device_checkin_info_.android_id,
615 device_checkin_info_.secret,
616 app_id);
617
618 UnregistrationRequest* unregistration_request = new UnregistrationRequest(
619 gservices_settings_.GetRegistrationURL(),
620 request_info,
621 kDefaultBackoffPolicy,
622 base::Bind(&GCMClientImpl::OnUnregisterCompleted,
623 weak_ptr_factory_.GetWeakPtr(),
624 app_id),
625 url_request_context_getter_,
626 &recorder_);
627 pending_unregistration_requests_[app_id] = unregistration_request;
628 unregistration_request->Start();
629 }
630
OnUnregisterCompleted(const std::string & app_id,UnregistrationRequest::Status status)631 void GCMClientImpl::OnUnregisterCompleted(
632 const std::string& app_id,
633 UnregistrationRequest::Status status) {
634 DVLOG(1) << "Unregister completed for app: " << app_id
635 << " with " << (status ? "success." : "failure.");
636 delegate_->OnUnregisterFinished(
637 app_id,
638 status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
639
640 PendingUnregistrationRequests::iterator iter =
641 pending_unregistration_requests_.find(app_id);
642 if (iter == pending_unregistration_requests_.end())
643 return;
644
645 delete iter->second;
646 pending_unregistration_requests_.erase(iter);
647 }
648
OnGCMStoreDestroyed(bool success)649 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
650 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
651 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
652 }
653
Send(const std::string & app_id,const std::string & receiver_id,const OutgoingMessage & message)654 void GCMClientImpl::Send(const std::string& app_id,
655 const std::string& receiver_id,
656 const OutgoingMessage& message) {
657 DCHECK_EQ(state_, READY);
658
659 RecordOutgoingMessageToUMA(message);
660
661 mcs_proto::DataMessageStanza stanza;
662 stanza.set_ttl(message.time_to_live);
663 stanza.set_sent(clock_->Now().ToInternalValue() /
664 base::Time::kMicrosecondsPerSecond);
665 stanza.set_id(message.id);
666 stanza.set_from(kSendMessageFromValue);
667 stanza.set_to(receiver_id);
668 stanza.set_category(app_id);
669
670 for (MessageData::const_iterator iter = message.data.begin();
671 iter != message.data.end();
672 ++iter) {
673 mcs_proto::AppData* app_data = stanza.add_app_data();
674 app_data->set_key(iter->first);
675 app_data->set_value(iter->second);
676 }
677
678 MCSMessage mcs_message(stanza);
679 DVLOG(1) << "MCS message size: " << mcs_message.size();
680 mcs_client_->SendMessage(mcs_message);
681 }
682
GetStateString() const683 std::string GCMClientImpl::GetStateString() const {
684 switch(state_) {
685 case GCMClientImpl::INITIALIZED:
686 return "INITIALIZED";
687 case GCMClientImpl::UNINITIALIZED:
688 return "UNINITIALIZED";
689 case GCMClientImpl::LOADING:
690 return "LOADING";
691 case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
692 return "INITIAL_DEVICE_CHECKIN";
693 case GCMClientImpl::READY:
694 return "READY";
695 default:
696 NOTREACHED();
697 return std::string();
698 }
699 }
700
SetRecording(bool recording)701 void GCMClientImpl::SetRecording(bool recording) {
702 recorder_.SetRecording(recording);
703 }
704
ClearActivityLogs()705 void GCMClientImpl::ClearActivityLogs() {
706 recorder_.Clear();
707 }
708
GetStatistics() const709 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
710 GCMClient::GCMStatistics stats;
711 stats.gcm_client_created = true;
712 stats.is_recording = recorder_.is_recording();
713 stats.gcm_client_state = GetStateString();
714 stats.connection_client_created = mcs_client_.get() != NULL;
715 if (connection_factory_.get())
716 stats.connection_state = connection_factory_->GetConnectionStateString();
717 if (mcs_client_.get()) {
718 stats.send_queue_size = mcs_client_->GetSendQueueSize();
719 stats.resend_queue_size = mcs_client_->GetResendQueueSize();
720 }
721 if (device_checkin_info_.android_id > 0)
722 stats.android_id = device_checkin_info_.android_id;
723 recorder_.CollectActivities(&stats.recorded_activities);
724
725 for (RegistrationInfoMap::const_iterator it = registrations_.begin();
726 it != registrations_.end(); ++it) {
727 stats.registered_app_ids.push_back(it->first);
728 }
729 return stats;
730 }
731
OnActivityRecorded()732 void GCMClientImpl::OnActivityRecorded() {
733 delegate_->OnActivityRecorded();
734 }
735
OnConnected(const GURL & current_server,const net::IPEndPoint & ip_endpoint)736 void GCMClientImpl::OnConnected(const GURL& current_server,
737 const net::IPEndPoint& ip_endpoint) {
738 // TODO(gcm): expose current server in debug page.
739 delegate_->OnActivityRecorded();
740 delegate_->OnConnected(ip_endpoint);
741 }
742
OnDisconnected()743 void GCMClientImpl::OnDisconnected() {
744 delegate_->OnActivityRecorded();
745 delegate_->OnDisconnected();
746 }
747
OnMessageReceivedFromMCS(const gcm::MCSMessage & message)748 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
749 switch (message.tag()) {
750 case kLoginResponseTag:
751 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
752 return;
753 case kDataMessageStanzaTag:
754 DVLOG(1) << "A downstream message received. Processing...";
755 HandleIncomingMessage(message);
756 return;
757 default:
758 NOTREACHED() << "Message with unexpected tag received by GCMClient";
759 return;
760 }
761 }
762
OnMessageSentToMCS(int64 user_serial_number,const std::string & app_id,const std::string & message_id,MCSClient::MessageSendStatus status)763 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
764 const std::string& app_id,
765 const std::string& message_id,
766 MCSClient::MessageSendStatus status) {
767 DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
768 DCHECK(delegate_);
769
770 // TTL_EXCEEDED is singled out here, because it can happen long time after the
771 // message was sent. That is why it comes as |OnMessageSendError| event rather
772 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
773 // All other errors will be raised immediately, through asynchronous callback.
774 // It is expected that TTL_EXCEEDED will be issued for a message that was
775 // previously issued |OnSendFinished| with status SUCCESS.
776 // For now, we do not report that the message has been sent and acked
777 // successfully.
778 // TODO(jianli): Consider adding UMA for this status.
779 if (status == MCSClient::TTL_EXCEEDED) {
780 SendErrorDetails send_error_details;
781 send_error_details.message_id = message_id;
782 send_error_details.result = GCMClient::TTL_EXCEEDED;
783 delegate_->OnMessageSendError(app_id, send_error_details);
784 } else if (status != MCSClient::SENT) {
785 delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
786 }
787 }
788
OnMCSError()789 void GCMClientImpl::OnMCSError() {
790 // TODO(fgorski): For now it replaces the initialization method. Long term it
791 // should have an error or status passed in.
792 }
793
HandleIncomingMessage(const gcm::MCSMessage & message)794 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
795 DCHECK(delegate_);
796
797 const mcs_proto::DataMessageStanza& data_message_stanza =
798 reinterpret_cast<const mcs_proto::DataMessageStanza&>(
799 message.GetProtobuf());
800 DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
801
802 // Copying all the data from the stanza to a MessageData object. When present,
803 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
804 // later.
805 MessageData message_data;
806 for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
807 std::string key = data_message_stanza.app_data(i).key();
808 message_data[key] = data_message_stanza.app_data(i).value();
809 }
810
811 MessageType message_type = DATA_MESSAGE;
812 MessageData::iterator iter = message_data.find(kMessageTypeKey);
813 if (iter != message_data.end()) {
814 message_type = DecodeMessageType(iter->second);
815 message_data.erase(iter);
816 }
817
818 switch (message_type) {
819 case DATA_MESSAGE:
820 HandleIncomingDataMessage(data_message_stanza, message_data);
821 break;
822 case DELETED_MESSAGES:
823 recorder_.RecordDataMessageReceived(data_message_stanza.category(),
824 data_message_stanza.from(),
825 data_message_stanza.ByteSize(),
826 true,
827 GCMStatsRecorder::DELETED_MESSAGES);
828 delegate_->OnMessagesDeleted(data_message_stanza.category());
829 break;
830 case SEND_ERROR:
831 HandleIncomingSendError(data_message_stanza, message_data);
832 break;
833 case UNKNOWN:
834 default: // Treat default the same as UNKNOWN.
835 DVLOG(1) << "Unknown message_type received. Message ignored. "
836 << "App ID: " << data_message_stanza.category() << ".";
837 break;
838 }
839 }
840
HandleIncomingDataMessage(const mcs_proto::DataMessageStanza & data_message_stanza,MessageData & message_data)841 void GCMClientImpl::HandleIncomingDataMessage(
842 const mcs_proto::DataMessageStanza& data_message_stanza,
843 MessageData& message_data) {
844 std::string app_id = data_message_stanza.category();
845
846 // Drop the message when the app is not registered for the sender of the
847 // message.
848 RegistrationInfoMap::iterator iter = registrations_.find(app_id);
849 bool not_registered =
850 iter == registrations_.end() ||
851 std::find(iter->second->sender_ids.begin(),
852 iter->second->sender_ids.end(),
853 data_message_stanza.from()) == iter->second->sender_ids.end();
854 recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
855 data_message_stanza.ByteSize(), !not_registered,
856 GCMStatsRecorder::DATA_MESSAGE);
857 if (not_registered) {
858 return;
859 }
860
861 IncomingMessage incoming_message;
862 incoming_message.sender_id = data_message_stanza.from();
863 if (data_message_stanza.has_token())
864 incoming_message.collapse_key = data_message_stanza.token();
865 incoming_message.data = message_data;
866 delegate_->OnMessageReceived(app_id, incoming_message);
867 }
868
HandleIncomingSendError(const mcs_proto::DataMessageStanza & data_message_stanza,MessageData & message_data)869 void GCMClientImpl::HandleIncomingSendError(
870 const mcs_proto::DataMessageStanza& data_message_stanza,
871 MessageData& message_data) {
872 SendErrorDetails send_error_details;
873 send_error_details.additional_data = message_data;
874 send_error_details.result = SERVER_ERROR;
875
876 MessageData::iterator iter =
877 send_error_details.additional_data.find(kSendErrorMessageIdKey);
878 if (iter != send_error_details.additional_data.end()) {
879 send_error_details.message_id = iter->second;
880 send_error_details.additional_data.erase(iter);
881 }
882
883 recorder_.RecordIncomingSendError(
884 data_message_stanza.category(),
885 data_message_stanza.to(),
886 data_message_stanza.id());
887 delegate_->OnMessageSendError(data_message_stanza.category(),
888 send_error_details);
889 }
890
891 } // namespace gcm
892