blob: 0b5c74dc601e0143b80e6e65927317069ebc740f [file] [log] [blame]
[email protected]df84c532014-04-25 15:36:541// 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 "chrome/browser/services/gcm/gcm_service.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/location.h"
13#include "base/logging.h"
14#include "base/sequenced_task_runner.h"
15#include "base/threading/sequenced_worker_pool.h"
16#include "chrome/browser/services/gcm/gcm_app_handler.h"
17#include "chrome/browser/services/gcm/gcm_client_factory.h"
18#include "chrome/common/chrome_version_info.h"
19#include "content/public/browser/browser_thread.h"
20#include "google_apis/gaia/oauth2_token_service.h"
21#include "google_apis/gcm/protocol/android_checkin.pb.h"
22#include "net/url_request/url_request_context_getter.h"
23
24namespace gcm {
25
26namespace {
27
28checkin_proto::ChromeBuildProto_Platform GetPlatform() {
29#if defined(OS_WIN)
30 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
31#elif defined(OS_MACOSX)
32 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
33#elif defined(OS_IOS)
34 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
35#elif defined(OS_CHROMEOS)
36 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
37#elif defined(OS_LINUX)
38 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
39#else
40 // For all other platforms, return as LINUX.
41 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
42#endif
43}
44
45std::string GetVersion() {
46 chrome::VersionInfo version_info;
47 return version_info.Version();
48}
49
50checkin_proto::ChromeBuildProto_Channel GetChannel() {
51 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
52 switch (channel) {
53 case chrome::VersionInfo::CHANNEL_UNKNOWN:
54 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
55 case chrome::VersionInfo::CHANNEL_CANARY:
56 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
57 case chrome::VersionInfo::CHANNEL_DEV:
58 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
59 case chrome::VersionInfo::CHANNEL_BETA:
60 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
61 case chrome::VersionInfo::CHANNEL_STABLE:
62 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
63 default:
64 NOTREACHED();
65 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
66 };
67}
68
69} // namespace
70
71// Helper class to save tasks to run until we're ready to execute them.
72class GCMService::DelayedTaskController {
73 public:
74 DelayedTaskController();
75 ~DelayedTaskController();
76
77 // Adds a task that will be invoked once we're ready.
78 void AddTask(const base::Closure& task);
79
80 // Sets ready status. It is ready only when check-in is completed and
81 // the GCMClient is fully initialized.
82 void SetReady();
83
84 // Returns true if it is ready to perform tasks.
85 bool CanRunTaskWithoutDelay() const;
86
87 private:
88 void RunTasks();
89
90 // Flag that indicates that GCM is ready.
91 bool ready_;
92
93 std::vector<base::Closure> delayed_tasks_;
94
95 DISALLOW_COPY_AND_ASSIGN(DelayedTaskController);
96};
97
98GCMService::DelayedTaskController::DelayedTaskController() : ready_(false) {
99}
100
101GCMService::DelayedTaskController::~DelayedTaskController() {
102}
103
104void GCMService::DelayedTaskController::AddTask(const base::Closure& task) {
105 delayed_tasks_.push_back(task);
106}
107
108void GCMService::DelayedTaskController::SetReady() {
109 ready_ = true;
110 RunTasks();
111}
112
113bool GCMService::DelayedTaskController::CanRunTaskWithoutDelay() const {
114 return ready_;
115}
116
117void GCMService::DelayedTaskController::RunTasks() {
118 DCHECK(ready_);
119
120 for (size_t i = 0; i < delayed_tasks_.size(); ++i)
121 delayed_tasks_[i].Run();
122 delayed_tasks_.clear();
123}
124
125class GCMService::IOWorker : public GCMClient::Delegate {
126 public:
127 // Called on UI thread.
128 IOWorker();
129 virtual ~IOWorker();
130
131 // Overridden from GCMClient::Delegate:
132 // Called on IO thread.
133 virtual void OnRegisterFinished(const std::string& app_id,
134 const std::string& registration_id,
135 GCMClient::Result result) OVERRIDE;
136 virtual void OnUnregisterFinished(const std::string& app_id,
137 GCMClient::Result result) OVERRIDE;
138 virtual void OnSendFinished(const std::string& app_id,
139 const std::string& message_id,
140 GCMClient::Result result) OVERRIDE;
141 virtual void OnMessageReceived(
142 const std::string& app_id,
143 const GCMClient::IncomingMessage& message) OVERRIDE;
144 virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
145 virtual void OnMessageSendError(
146 const std::string& app_id,
147 const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
148 virtual void OnGCMReady() OVERRIDE;
149
150 // Called on IO thread.
151 void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory,
152 const base::FilePath& store_path,
153 const std::vector<std::string>& account_ids,
154 const scoped_refptr<net::URLRequestContextGetter>&
155 url_request_context_getter);
156 void Load(const base::WeakPtr<GCMService>& service);
157 void Stop();
158 void CheckOut();
159 void Register(const std::string& app_id,
160 const std::vector<std::string>& sender_ids);
161 void Unregister(const std::string& app_id);
162 void Send(const std::string& app_id,
163 const std::string& receiver_id,
164 const GCMClient::OutgoingMessage& message);
165 void GetGCMStatistics(bool clear_logs);
166 void SetGCMRecording(bool recording);
167
168 // For testing purpose. Can be called from UI thread. Use with care.
169 GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); }
170
171 private:
172 base::WeakPtr<GCMService> service_;
173
174 scoped_ptr<GCMClient> gcm_client_;
175
176 DISALLOW_COPY_AND_ASSIGN(IOWorker);
177};
178
179GCMService::IOWorker::IOWorker() {
180 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
181}
182
183GCMService::IOWorker::~IOWorker() {
184 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
185}
186
187void GCMService::IOWorker::Initialize(
188 scoped_ptr<GCMClientFactory> gcm_client_factory,
189 const base::FilePath& store_path,
190 const std::vector<std::string>& account_ids,
191 const scoped_refptr<net::URLRequestContextGetter>&
192 url_request_context_getter) {
193 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
194
195 gcm_client_ = gcm_client_factory->BuildInstance().Pass();
196
197 checkin_proto::ChromeBuildProto chrome_build_proto;
198 chrome_build_proto.set_platform(GetPlatform());
199 chrome_build_proto.set_chrome_version(GetVersion());
200 chrome_build_proto.set_channel(GetChannel());
201
202 scoped_refptr<base::SequencedWorkerPool> worker_pool(
203 content::BrowserThread::GetBlockingPool());
204 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
205 worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
206 worker_pool->GetSequenceToken(),
207 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
208
209 gcm_client_->Initialize(chrome_build_proto,
210 store_path,
211 account_ids,
212 blocking_task_runner,
213 url_request_context_getter,
214 this);
215}
216
217void GCMService::IOWorker::OnRegisterFinished(
218 const std::string& app_id,
219 const std::string& registration_id,
220 GCMClient::Result result) {
221 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
222
223 content::BrowserThread::PostTask(content::BrowserThread::UI,
224 FROM_HERE,
225 base::Bind(&GCMService::RegisterFinished,
226 service_,
227 app_id,
228 registration_id,
229 result));
230}
231
232void GCMService::IOWorker::OnUnregisterFinished(const std::string& app_id,
233 GCMClient::Result result) {
234 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
235
236 content::BrowserThread::PostTask(
237 content::BrowserThread::UI,
238 FROM_HERE,
239 base::Bind(&GCMService::UnregisterFinished, service_, app_id, result));
240}
241
242void GCMService::IOWorker::OnSendFinished(const std::string& app_id,
243 const std::string& message_id,
244 GCMClient::Result result) {
245 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
246
247 content::BrowserThread::PostTask(content::BrowserThread::UI,
248 FROM_HERE,
249 base::Bind(&GCMService::SendFinished,
250 service_,
251 app_id,
252 message_id,
253 result));
254}
255
256void GCMService::IOWorker::OnMessageReceived(
257 const std::string& app_id,
258 const GCMClient::IncomingMessage& message) {
259 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
260
261 content::BrowserThread::PostTask(content::BrowserThread::UI,
262 FROM_HERE,
263 base::Bind(&GCMService::MessageReceived,
264 service_,
265 app_id,
266 message));
267}
268
269void GCMService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
270 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
271
272 content::BrowserThread::PostTask(content::BrowserThread::UI,
273 FROM_HERE,
274 base::Bind(&GCMService::MessagesDeleted,
275 service_,
276 app_id));
277}
278
279void GCMService::IOWorker::OnMessageSendError(
280 const std::string& app_id,
281 const GCMClient::SendErrorDetails& send_error_details) {
282 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
283
284 content::BrowserThread::PostTask(content::BrowserThread::UI,
285 FROM_HERE,
286 base::Bind(&GCMService::MessageSendError,
287 service_,
288 app_id,
289 send_error_details));
290}
291
292void GCMService::IOWorker::OnGCMReady() {
293 content::BrowserThread::PostTask(content::BrowserThread::UI,
294 FROM_HERE,
295 base::Bind(&GCMService::GCMClientReady,
296 service_));
297}
298
299void GCMService::IOWorker::Load(const base::WeakPtr<GCMService>& service) {
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
301
302 service_ = service;
303 gcm_client_->Load();
304}
305
306void GCMService::IOWorker::Stop() {
307 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
308
309 gcm_client_->Stop();
310}
311
312void GCMService::IOWorker::CheckOut() {
313 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
314
315 gcm_client_->CheckOut();
316
317 // Note that we still need to keep GCMClient instance alive since the
318 // GCMService may check in again.
319}
320
321void GCMService::IOWorker::Register(
322 const std::string& app_id,
323 const std::vector<std::string>& sender_ids) {
324 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
325
326 gcm_client_->Register(app_id, sender_ids);
327}
328
329void GCMService::IOWorker::Unregister(const std::string& app_id) {
330 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
331
332 gcm_client_->Unregister(app_id);
333}
334
335void GCMService::IOWorker::Send(const std::string& app_id,
336 const std::string& receiver_id,
337 const GCMClient::OutgoingMessage& message) {
338 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
339
340 gcm_client_->Send(app_id, receiver_id, message);
341}
342
343void GCMService::IOWorker::GetGCMStatistics(bool clear_logs) {
344 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
345 gcm::GCMClient::GCMStatistics stats;
346
347 if (gcm_client_.get()) {
348 if (clear_logs)
349 gcm_client_->ClearActivityLogs();
350 stats = gcm_client_->GetStatistics();
351 }
352
353 content::BrowserThread::PostTask(
354 content::BrowserThread::UI,
355 FROM_HERE,
356 base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
357}
358
359void GCMService::IOWorker::SetGCMRecording(bool recording) {
360 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
361 gcm::GCMClient::GCMStatistics stats;
362
363 if (gcm_client_.get()) {
364 gcm_client_->SetRecording(recording);
365 stats = gcm_client_->GetStatistics();
366 stats.gcm_client_created = true;
367 }
368
369 content::BrowserThread::PostTask(
370 content::BrowserThread::UI,
371 FROM_HERE,
372 base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
373}
374
375GCMService::GCMService(scoped_ptr<IdentityProvider> identity_provider)
376 : identity_provider_(identity_provider.Pass()),
377 gcm_client_ready_(false),
378 weak_ptr_factory_(this) {
379}
380
381GCMService::~GCMService() {
382}
383
384void GCMService::Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory) {
385 // Get the list of available accounts.
386 std::vector<std::string> account_ids;
387#if !defined(OS_ANDROID)
388 account_ids = identity_provider_->GetTokenService()->GetAccounts();
389#endif
390
391 // Create and initialize the GCMClient. Note that this does not initiate the
392 // GCM check-in.
393 DCHECK(!io_worker_);
394 io_worker_.reset(new IOWorker());
395 content::BrowserThread::PostTask(
396 content::BrowserThread::IO,
397 FROM_HERE,
398 base::Bind(&GCMService::IOWorker::Initialize,
399 base::Unretained(io_worker_.get()),
400 base::Passed(&gcm_client_factory),
401 GetStorePath(),
402 account_ids,
403 GetURLRequestContextGetter()));
404
405 // Load from the GCM store and initiate the GCM check-in if the rollout signal
406 // indicates yes.
407 if (ShouldStartAutomatically())
408 EnsureLoaded();
409
410 identity_provider_->AddObserver(this);
411}
412
413void GCMService::Start() {
414 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
415
416 EnsureLoaded();
417}
418
419void GCMService::Stop() {
420 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
421
422 // No need to stop GCM service if not started yet.
423 if (account_id_.empty())
424 return;
425
426 RemoveCachedData();
427
428 content::BrowserThread::PostTask(
429 content::BrowserThread::IO,
430 FROM_HERE,
431 base::Bind(&GCMService::IOWorker::Stop,
432 base::Unretained(io_worker_.get())));
433}
434
435void GCMService::ShutdownService() {
436 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
437 identity_provider_->RemoveObserver(this);
438 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
439 iter != app_handlers_.end(); ++iter) {
440 iter->second->ShutdownHandler();
441 }
442 app_handlers_.clear();
443 content::BrowserThread::DeleteSoon(content::BrowserThread::IO,
444 FROM_HERE,
445 io_worker_.release());
446}
447
448void GCMService::AddAppHandler(const std::string& app_id,
449 GCMAppHandler* handler) {
450 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
451 DCHECK(!app_id.empty());
452 DCHECK(handler);
453 DCHECK(app_handlers_.find(app_id) == app_handlers_.end());
454
455 app_handlers_[app_id] = handler;
456}
457
458void GCMService::RemoveAppHandler(const std::string& app_id) {
459 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
460 DCHECK(!app_id.empty());
461
462 app_handlers_.erase(app_id);
463}
464
465void GCMService::Register(const std::string& app_id,
466 const std::vector<std::string>& sender_ids,
467 RegisterCallback callback) {
468 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
469 DCHECK(!app_id.empty());
470 DCHECK(!sender_ids.empty());
471 DCHECK(!callback.is_null());
472
473 GCMClient::Result result = EnsureAppReady(app_id);
474 if (result != GCMClient::SUCCESS) {
475 callback.Run(std::string(), result);
476 return;
477 }
478
479 // If previous un/register operation is still in progress, bail out.
480 if (IsAsyncOperationPending(app_id)) {
481 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
482 return;
483 }
484
485 register_callbacks_[app_id] = callback;
486
487 // Delay the register operation until GCMClient is ready.
488 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
489 delayed_task_controller_->AddTask(base::Bind(&GCMService::DoRegister,
490 weak_ptr_factory_.GetWeakPtr(),
491 app_id,
492 sender_ids));
493 return;
494 }
495
496 DoRegister(app_id, sender_ids);
497}
498
499void GCMService::DoRegister(const std::string& app_id,
500 const std::vector<std::string>& sender_ids) {
501 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
502 std::map<std::string, RegisterCallback>::iterator callback_iter =
503 register_callbacks_.find(app_id);
504 if (callback_iter == register_callbacks_.end()) {
505 // The callback could have been removed when the app is uninstalled.
506 return;
507 }
508
509 // Normalize the sender IDs by making them sorted.
510 std::vector<std::string> normalized_sender_ids = sender_ids;
511 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
512
513 content::BrowserThread::PostTask(
514 content::BrowserThread::IO,
515 FROM_HERE,
516 base::Bind(&GCMService::IOWorker::Register,
517 base::Unretained(io_worker_.get()),
518 app_id,
519 normalized_sender_ids));
520}
521
522void GCMService::Unregister(const std::string& app_id,
523 UnregisterCallback callback) {
524 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
525 DCHECK(!app_id.empty());
526 DCHECK(!callback.is_null());
527
528 GCMClient::Result result = EnsureAppReady(app_id);
529 if (result != GCMClient::SUCCESS) {
530 callback.Run(result);
531 return;
532 }
533
534 // If previous un/register operation is still in progress, bail out.
535 if (IsAsyncOperationPending(app_id)) {
536 callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
537 return;
538 }
539
540 unregister_callbacks_[app_id] = callback;
541
542 // Delay the unregister operation until GCMClient is ready.
543 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
544 delayed_task_controller_->AddTask(base::Bind(&GCMService::DoUnregister,
545 weak_ptr_factory_.GetWeakPtr(),
546 app_id));
547 return;
548 }
549
550 DoUnregister(app_id);
551}
552
553void GCMService::DoUnregister(const std::string& app_id) {
554 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
555
556 // Ask the server to unregister it. There could be a small chance that the
557 // unregister request fails. If this occurs, it does not bring any harm since
558 // we simply reject the messages/events received from the server.
559 content::BrowserThread::PostTask(
560 content::BrowserThread::IO,
561 FROM_HERE,
562 base::Bind(&GCMService::IOWorker::Unregister,
563 base::Unretained(io_worker_.get()),
564 app_id));
565}
566
567void GCMService::Send(const std::string& app_id,
568 const std::string& receiver_id,
569 const GCMClient::OutgoingMessage& message,
570 SendCallback callback) {
571 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
572 DCHECK(!app_id.empty());
573 DCHECK(!receiver_id.empty());
574 DCHECK(!callback.is_null());
575
576 GCMClient::Result result = EnsureAppReady(app_id);
577 if (result != GCMClient::SUCCESS) {
578 callback.Run(std::string(), result);
579 return;
580 }
581
582 // If the message with send ID is still in progress, bail out.
583 std::pair<std::string, std::string> key(app_id, message.id);
584 if (send_callbacks_.find(key) != send_callbacks_.end()) {
585 callback.Run(message.id, GCMClient::INVALID_PARAMETER);
586 return;
587 }
588
589 send_callbacks_[key] = callback;
590
591 // Delay the send operation until all GCMClient is ready.
592 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
593 delayed_task_controller_->AddTask(base::Bind(&GCMService::DoSend,
594 weak_ptr_factory_.GetWeakPtr(),
595 app_id,
596 receiver_id,
597 message));
598 return;
599 }
600
601 DoSend(app_id, receiver_id, message);
602}
603
604void GCMService::DoSend(const std::string& app_id,
605 const std::string& receiver_id,
606 const GCMClient::OutgoingMessage& message) {
607 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
608 content::BrowserThread::PostTask(
609 content::BrowserThread::IO,
610 FROM_HERE,
611 base::Bind(&GCMService::IOWorker::Send,
612 base::Unretained(io_worker_.get()),
613 app_id,
614 receiver_id,
615 message));
616}
617
618GCMClient* GCMService::GetGCMClientForTesting() const {
619 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
620 return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
621}
622
623bool GCMService::IsStarted() const {
624 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
625 return !account_id_.empty();
626}
627
628bool GCMService::IsGCMClientReady() const {
629 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
630 return gcm_client_ready_;
631}
632
633void GCMService::GetGCMStatistics(GetGCMStatisticsCallback callback,
634 bool clear_logs) {
635 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
636 DCHECK(!callback.is_null());
637
638 request_gcm_statistics_callback_ = callback;
639 content::BrowserThread::PostTask(
640 content::BrowserThread::IO,
641 FROM_HERE,
642 base::Bind(&GCMService::IOWorker::GetGCMStatistics,
643 base::Unretained(io_worker_.get()),
644 clear_logs));
645}
646
647void GCMService::SetGCMRecording(GetGCMStatisticsCallback callback,
648 bool recording) {
649 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
650
651 request_gcm_statistics_callback_ = callback;
652 content::BrowserThread::PostTask(
653 content::BrowserThread::IO,
654 FROM_HERE,
655 base::Bind(&GCMService::IOWorker::SetGCMRecording,
656 base::Unretained(io_worker_.get()),
657 recording));
658}
659
660void GCMService::OnActiveAccountLogin() {
661 if (ShouldStartAutomatically())
662 EnsureLoaded();
663}
664
665void GCMService::OnActiveAccountLogout() {
666 CheckOut();
667}
668
669void GCMService::EnsureLoaded() {
670 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
671 const std::string account_id = identity_provider_->GetActiveAccountId();
672 if (account_id.empty())
673 return;
674
675 // CheckIn could be called more than once when:
676 // 1) The password changes.
677 // 2) Register/send function calls it to ensure CheckIn is done.
678 if (account_id_ == account_id)
679 return;
680 account_id_ = account_id;
681
682 DCHECK(!delayed_task_controller_);
683 delayed_task_controller_.reset(new DelayedTaskController);
684
685 // This will load the data from the gcm store and trigger the check-in if
686 // the persisted check-in info is not found.
687 // Note that we need to pass weak pointer again since the existing weak
688 // pointer in IOWorker might have been invalidated when check-out occurs.
689 content::BrowserThread::PostTask(
690 content::BrowserThread::IO,
691 FROM_HERE,
692 base::Bind(&GCMService::IOWorker::Load,
693 base::Unretained(io_worker_.get()),
694 weak_ptr_factory_.GetWeakPtr()));
695}
696
697void GCMService::RemoveCachedData() {
698 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
699 // Remove all the queued tasks since they no longer make sense after
700 // GCM service is stopped.
701 weak_ptr_factory_.InvalidateWeakPtrs();
702
703 account_id_.clear();
704 gcm_client_ready_ = false;
705 delayed_task_controller_.reset();
706 register_callbacks_.clear();
707 send_callbacks_.clear();
708}
709
710void GCMService::CheckOut() {
711 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
712
713 // We still proceed with the check-out logic even if the check-in is not
714 // initiated in the current session. This will make sure that all the
715 // persisted data written previously will get purged.
716
717 RemoveCachedData();
718
719 content::BrowserThread::PostTask(
720 content::BrowserThread::IO,
721 FROM_HERE,
722 base::Bind(&GCMService::IOWorker::CheckOut,
723 base::Unretained(io_worker_.get())));
724}
725
726GCMClient::Result GCMService::EnsureAppReady(const std::string& app_id) {
727 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
728 // Ensure that check-in has been done.
729 EnsureLoaded();
730
731 // If the service was not started, bail out.
732 if (account_id_.empty())
733 return GCMClient::NOT_SIGNED_IN;
734
735 return GCMClient::SUCCESS;
736}
737
738bool GCMService::IsAsyncOperationPending(const std::string& app_id) const {
739 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
740 return register_callbacks_.find(app_id) != register_callbacks_.end() ||
741 unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
742}
743
744void GCMService::RegisterFinished(const std::string& app_id,
745 const std::string& registration_id,
746 GCMClient::Result result) {
747 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
748
749 std::map<std::string, RegisterCallback>::iterator callback_iter =
750 register_callbacks_.find(app_id);
751 if (callback_iter == register_callbacks_.end()) {
752 // The callback could have been removed when the app is uninstalled.
753 return;
754 }
755
756 RegisterCallback callback = callback_iter->second;
757 register_callbacks_.erase(callback_iter);
758 callback.Run(registration_id, result);
759}
760
761void GCMService::UnregisterFinished(const std::string& app_id,
762 GCMClient::Result result) {
763 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
764
765 std::map<std::string, UnregisterCallback>::iterator callback_iter =
766 unregister_callbacks_.find(app_id);
767 if (callback_iter == unregister_callbacks_.end())
768 return;
769
770 UnregisterCallback callback = callback_iter->second;
771 unregister_callbacks_.erase(callback_iter);
772 callback.Run(result);
773}
774
775void GCMService::SendFinished(const std::string& app_id,
776 const std::string& message_id,
777 GCMClient::Result result) {
778 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
779
780 std::map<std::pair<std::string, std::string>, SendCallback>::iterator
781 callback_iter = send_callbacks_.find(
782 std::pair<std::string, std::string>(app_id, message_id));
783 if (callback_iter == send_callbacks_.end()) {
784 // The callback could have been removed when the app is uninstalled.
785 return;
786 }
787
788 SendCallback callback = callback_iter->second;
789 send_callbacks_.erase(callback_iter);
790 callback.Run(message_id, result);
791}
792
793void GCMService::MessageReceived(const std::string& app_id,
794 GCMClient::IncomingMessage message) {
795 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
796
797 // Drop the event if signed out.
798 if (account_id_.empty())
799 return;
800
801 GetAppHandler(app_id)->OnMessage(app_id, message);
802}
803
804void GCMService::MessagesDeleted(const std::string& app_id) {
805 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
806
807 // Drop the event if signed out.
808 if (account_id_.empty())
809 return;
810
811 GetAppHandler(app_id)->OnMessagesDeleted(app_id);
812}
813
814void GCMService::MessageSendError(
815 const std::string& app_id,
816 const GCMClient::SendErrorDetails& send_error_details) {
817 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
818
819 // Drop the event if signed out.
820 if (account_id_.empty())
821 return;
822
823 GetAppHandler(app_id)->OnSendError(app_id, send_error_details);
824}
825
826void GCMService::GCMClientReady() {
827 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
828
829 if (gcm_client_ready_)
830 return;
831 gcm_client_ready_ = true;
832
833 delayed_task_controller_->SetReady();
834}
835
836GCMAppHandler* GCMService::GetAppHandler(const std::string& app_id) {
837 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
838
839 std::map<std::string, GCMAppHandler*>::const_iterator iter =
840 app_handlers_.find(app_id);
841 return iter == app_handlers_.end() ? &default_app_handler_ : iter->second;
842}
843
844void GCMService::GetGCMStatisticsFinished(
845 GCMClient::GCMStatistics stats) {
846 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
847 request_gcm_statistics_callback_.Run(stats);
848}
849
850} // namespace gcm