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_driver_desktop.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_path.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 "components/gcm_driver/gcm_app_handler.h"
17 #include "components/gcm_driver/gcm_client_factory.h"
18 #include "components/gcm_driver/system_encryptor.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/url_request/url_request_context_getter.h"
26 // Empty string is reserved for the default app handler.
27 const char kDefaultAppHandler
[] = "";
31 // Helper class to save tasks to run until we're ready to execute them.
32 class GCMDriverDesktop::DelayedTaskController
{
34 DelayedTaskController();
35 ~DelayedTaskController();
37 // Adds a task that will be invoked once we're ready.
38 void AddTask(const base::Closure
& task
);
40 // Sets ready status. It is ready only when check-in is completed and
41 // the GCMClient is fully initialized.
44 // Returns true if it is ready to perform tasks.
45 bool CanRunTaskWithoutDelay() const;
50 // Flag that indicates that GCM is ready.
53 std::vector
<base::Closure
> delayed_tasks_
;
55 DISALLOW_COPY_AND_ASSIGN(DelayedTaskController
);
58 GCMDriverDesktop::DelayedTaskController::DelayedTaskController()
62 GCMDriverDesktop::DelayedTaskController::~DelayedTaskController() {
65 void GCMDriverDesktop::DelayedTaskController::AddTask(
66 const base::Closure
& task
) {
67 delayed_tasks_
.push_back(task
);
70 void GCMDriverDesktop::DelayedTaskController::SetReady() {
75 bool GCMDriverDesktop::DelayedTaskController::CanRunTaskWithoutDelay() const {
79 void GCMDriverDesktop::DelayedTaskController::RunTasks() {
82 for (size_t i
= 0; i
< delayed_tasks_
.size(); ++i
)
83 delayed_tasks_
[i
].Run();
84 delayed_tasks_
.clear();
87 class GCMDriverDesktop::IOWorker
: public GCMClient::Delegate
{
89 // Called on UI thread.
90 IOWorker(const scoped_refptr
<base::SequencedTaskRunner
>& ui_thread
,
91 const scoped_refptr
<base::SequencedTaskRunner
>& io_thread
);
94 // Overridden from GCMClient::Delegate:
95 // Called on IO thread.
96 virtual void OnRegisterFinished(const std::string
& app_id
,
97 const std::string
& registration_id
,
98 GCMClient::Result result
) OVERRIDE
;
99 virtual void OnUnregisterFinished(const std::string
& app_id
,
100 GCMClient::Result result
) OVERRIDE
;
101 virtual void OnSendFinished(const std::string
& app_id
,
102 const std::string
& message_id
,
103 GCMClient::Result result
) OVERRIDE
;
104 virtual void OnMessageReceived(
105 const std::string
& app_id
,
106 const GCMClient::IncomingMessage
& message
) OVERRIDE
;
107 virtual void OnMessagesDeleted(const std::string
& app_id
) OVERRIDE
;
108 virtual void OnMessageSendError(
109 const std::string
& app_id
,
110 const GCMClient::SendErrorDetails
& send_error_details
) OVERRIDE
;
111 virtual void OnGCMReady() OVERRIDE
;
112 virtual void OnActivityRecorded() OVERRIDE
;
113 virtual void OnConnected(const net::IPEndPoint
& ip_endpoint
) OVERRIDE
;
114 virtual void OnDisconnected() OVERRIDE
;
116 // Called on IO thread.
118 scoped_ptr
<GCMClientFactory
> gcm_client_factory
,
119 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
120 const base::FilePath
& store_path
,
121 const scoped_refptr
<net::URLRequestContextGetter
>& request_context
,
122 const scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
);
123 void Start(const base::WeakPtr
<GCMDriverDesktop
>& service
);
126 void Register(const std::string
& app_id
,
127 const std::vector
<std::string
>& sender_ids
);
128 void Unregister(const std::string
& app_id
);
129 void Send(const std::string
& app_id
,
130 const std::string
& receiver_id
,
131 const GCMClient::OutgoingMessage
& message
);
132 void GetGCMStatistics(bool clear_logs
);
133 void SetGCMRecording(bool recording
);
135 // For testing purpose. Can be called from UI thread. Use with care.
136 GCMClient
* gcm_client_for_testing() const { return gcm_client_
.get(); }
139 scoped_refptr
<base::SequencedTaskRunner
> ui_thread_
;
140 scoped_refptr
<base::SequencedTaskRunner
> io_thread_
;
142 base::WeakPtr
<GCMDriverDesktop
> service_
;
144 scoped_ptr
<GCMClient
> gcm_client_
;
146 DISALLOW_COPY_AND_ASSIGN(IOWorker
);
149 GCMDriverDesktop::IOWorker::IOWorker(
150 const scoped_refptr
<base::SequencedTaskRunner
>& ui_thread
,
151 const scoped_refptr
<base::SequencedTaskRunner
>& io_thread
)
152 : ui_thread_(ui_thread
),
153 io_thread_(io_thread
) {
154 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
157 GCMDriverDesktop::IOWorker::~IOWorker() {
158 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
161 void GCMDriverDesktop::IOWorker::Initialize(
162 scoped_ptr
<GCMClientFactory
> gcm_client_factory
,
163 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
164 const base::FilePath
& store_path
,
165 const scoped_refptr
<net::URLRequestContextGetter
>& request_context
,
166 const scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
) {
167 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
169 gcm_client_
= gcm_client_factory
->BuildInstance();
171 gcm_client_
->Initialize(chrome_build_info
,
173 blocking_task_runner
,
175 make_scoped_ptr
<Encryptor
>(new SystemEncryptor
),
179 void GCMDriverDesktop::IOWorker::OnRegisterFinished(
180 const std::string
& app_id
,
181 const std::string
& registration_id
,
182 GCMClient::Result result
) {
183 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
185 ui_thread_
->PostTask(
187 base::Bind(&GCMDriverDesktop::RegisterFinished
, service_
, app_id
,
188 registration_id
, result
));
191 void GCMDriverDesktop::IOWorker::OnUnregisterFinished(
192 const std::string
& app_id
,
193 GCMClient::Result result
) {
194 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
196 ui_thread_
->PostTask(FROM_HERE
,
197 base::Bind(&GCMDriverDesktop::UnregisterFinished
,
203 void GCMDriverDesktop::IOWorker::OnSendFinished(const std::string
& app_id
,
204 const std::string
& message_id
,
205 GCMClient::Result result
) {
206 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
208 ui_thread_
->PostTask(
210 base::Bind(&GCMDriverDesktop::SendFinished
, service_
, app_id
, message_id
,
214 void GCMDriverDesktop::IOWorker::OnMessageReceived(
215 const std::string
& app_id
,
216 const GCMClient::IncomingMessage
& message
) {
217 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
219 ui_thread_
->PostTask(
221 base::Bind(&GCMDriverDesktop::MessageReceived
,
227 void GCMDriverDesktop::IOWorker::OnMessagesDeleted(const std::string
& app_id
) {
228 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
230 ui_thread_
->PostTask(
232 base::Bind(&GCMDriverDesktop::MessagesDeleted
, service_
, app_id
));
235 void GCMDriverDesktop::IOWorker::OnMessageSendError(
236 const std::string
& app_id
,
237 const GCMClient::SendErrorDetails
& send_error_details
) {
238 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
240 ui_thread_
->PostTask(
242 base::Bind(&GCMDriverDesktop::MessageSendError
, service_
, app_id
,
243 send_error_details
));
246 void GCMDriverDesktop::IOWorker::OnGCMReady() {
247 ui_thread_
->PostTask(
249 base::Bind(&GCMDriverDesktop::GCMClientReady
, service_
));
252 void GCMDriverDesktop::IOWorker::OnActivityRecorded() {
253 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
254 // When an activity is recorded, get all the stats and refresh the UI of
255 // gcm-internals page.
256 GetGCMStatistics(false);
259 void GCMDriverDesktop::IOWorker::OnConnected(
260 const net::IPEndPoint
& ip_endpoint
) {
261 ui_thread_
->PostTask(FROM_HERE
,
262 base::Bind(&GCMDriverDesktop::OnConnected
,
267 void GCMDriverDesktop::IOWorker::OnDisconnected() {
268 ui_thread_
->PostTask(FROM_HERE
,
269 base::Bind(&GCMDriverDesktop::OnDisconnected
, service_
));
272 void GCMDriverDesktop::IOWorker::Start(
273 const base::WeakPtr
<GCMDriverDesktop
>& service
) {
274 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
277 gcm_client_
->Start();
280 void GCMDriverDesktop::IOWorker::Stop() {
281 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
286 void GCMDriverDesktop::IOWorker::CheckOut() {
287 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
289 gcm_client_
->CheckOut();
291 // Note that we still need to keep GCMClient instance alive since the
292 // GCMDriverDesktop may check in again.
295 void GCMDriverDesktop::IOWorker::Register(
296 const std::string
& app_id
,
297 const std::vector
<std::string
>& sender_ids
) {
298 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
300 gcm_client_
->Register(app_id
, sender_ids
);
303 void GCMDriverDesktop::IOWorker::Unregister(const std::string
& app_id
) {
304 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
306 gcm_client_
->Unregister(app_id
);
309 void GCMDriverDesktop::IOWorker::Send(
310 const std::string
& app_id
,
311 const std::string
& receiver_id
,
312 const GCMClient::OutgoingMessage
& message
) {
313 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
315 gcm_client_
->Send(app_id
, receiver_id
, message
);
318 void GCMDriverDesktop::IOWorker::GetGCMStatistics(bool clear_logs
) {
319 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
320 gcm::GCMClient::GCMStatistics stats
;
322 if (gcm_client_
.get()) {
324 gcm_client_
->ClearActivityLogs();
325 stats
= gcm_client_
->GetStatistics();
328 ui_thread_
->PostTask(
330 base::Bind(&GCMDriverDesktop::GetGCMStatisticsFinished
, service_
, stats
));
333 void GCMDriverDesktop::IOWorker::SetGCMRecording(bool recording
) {
334 DCHECK(io_thread_
->RunsTasksOnCurrentThread());
335 gcm::GCMClient::GCMStatistics stats
;
337 if (gcm_client_
.get()) {
338 gcm_client_
->SetRecording(recording
);
339 stats
= gcm_client_
->GetStatistics();
340 stats
.gcm_client_created
= true;
343 ui_thread_
->PostTask(
345 base::Bind(&GCMDriverDesktop::GetGCMStatisticsFinished
, service_
, stats
));
348 GCMDriverDesktop::GCMDriverDesktop(
349 scoped_ptr
<GCMClientFactory
> gcm_client_factory
,
350 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
351 const base::FilePath
& store_path
,
352 const scoped_refptr
<net::URLRequestContextGetter
>& request_context
,
353 const scoped_refptr
<base::SequencedTaskRunner
>& ui_thread
,
354 const scoped_refptr
<base::SequencedTaskRunner
>& io_thread
,
355 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
)
360 ui_thread_(ui_thread
),
361 io_thread_(io_thread
),
362 weak_ptr_factory_(this) {
363 // Create and initialize the GCMClient. Note that this does not initiate the
365 io_worker_
.reset(new IOWorker(ui_thread
, io_thread
));
366 io_thread_
->PostTask(
368 base::Bind(&GCMDriverDesktop::IOWorker::Initialize
,
369 base::Unretained(io_worker_
.get()),
370 base::Passed(&gcm_client_factory
),
374 blocking_task_runner
));
377 GCMDriverDesktop::~GCMDriverDesktop() {
380 void GCMDriverDesktop::Shutdown() {
381 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
382 GCMDriver::Shutdown();
383 io_thread_
->DeleteSoon(FROM_HERE
, io_worker_
.release());
386 void GCMDriverDesktop::OnSignedIn() {
391 void GCMDriverDesktop::Purge() {
392 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
394 // We still proceed with the check-out logic even if the check-in is not
395 // initiated in the current session. This will make sure that all the
396 // persisted data written previously will get purged.
400 io_thread_
->PostTask(FROM_HERE
,
401 base::Bind(&GCMDriverDesktop::IOWorker::CheckOut
,
402 base::Unretained(io_worker_
.get())));
405 void GCMDriverDesktop::AddAppHandler(const std::string
& app_id
,
406 GCMAppHandler
* handler
) {
407 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
408 GCMDriver::AddAppHandler(app_id
, handler
);
410 // Ensures that the GCM service is started when there is an interest.
414 void GCMDriverDesktop::RemoveAppHandler(const std::string
& app_id
) {
415 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
416 GCMDriver::RemoveAppHandler(app_id
);
418 // Stops the GCM service when no app intends to consume it.
419 if (app_handlers().empty())
423 void GCMDriverDesktop::Enable() {
424 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
433 void GCMDriverDesktop::Disable() {
434 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
438 gcm_enabled_
= false;
443 void GCMDriverDesktop::Stop() {
444 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
446 // No need to stop GCM service if not started yet.
452 io_thread_
->PostTask(
454 base::Bind(&GCMDriverDesktop::IOWorker::Stop
,
455 base::Unretained(io_worker_
.get())));
458 void GCMDriverDesktop::RegisterImpl(
459 const std::string
& app_id
,
460 const std::vector
<std::string
>& sender_ids
) {
461 // Delay the register operation until GCMClient is ready.
462 if (!delayed_task_controller_
->CanRunTaskWithoutDelay()) {
463 delayed_task_controller_
->AddTask(base::Bind(&GCMDriverDesktop::DoRegister
,
464 weak_ptr_factory_
.GetWeakPtr(),
470 DoRegister(app_id
, sender_ids
);
473 void GCMDriverDesktop::DoRegister(const std::string
& app_id
,
474 const std::vector
<std::string
>& sender_ids
) {
475 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
476 if (!HasRegisterCallback(app_id
)) {
477 // The callback could have been removed when the app is uninstalled.
481 io_thread_
->PostTask(
483 base::Bind(&GCMDriverDesktop::IOWorker::Register
,
484 base::Unretained(io_worker_
.get()),
489 void GCMDriverDesktop::UnregisterImpl(const std::string
& app_id
) {
490 // Delay the unregister operation until GCMClient is ready.
491 if (!delayed_task_controller_
->CanRunTaskWithoutDelay()) {
492 delayed_task_controller_
->AddTask(
493 base::Bind(&GCMDriverDesktop::DoUnregister
,
494 weak_ptr_factory_
.GetWeakPtr(),
499 DoUnregister(app_id
);
502 void GCMDriverDesktop::DoUnregister(const std::string
& app_id
) {
503 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
505 // Ask the server to unregister it. There could be a small chance that the
506 // unregister request fails. If this occurs, it does not bring any harm since
507 // we simply reject the messages/events received from the server.
508 io_thread_
->PostTask(
510 base::Bind(&GCMDriverDesktop::IOWorker::Unregister
,
511 base::Unretained(io_worker_
.get()),
515 void GCMDriverDesktop::SendImpl(const std::string
& app_id
,
516 const std::string
& receiver_id
,
517 const GCMClient::OutgoingMessage
& message
) {
518 // Delay the send operation until all GCMClient is ready.
519 if (!delayed_task_controller_
->CanRunTaskWithoutDelay()) {
520 delayed_task_controller_
->AddTask(base::Bind(&GCMDriverDesktop::DoSend
,
521 weak_ptr_factory_
.GetWeakPtr(),
528 DoSend(app_id
, receiver_id
, message
);
531 void GCMDriverDesktop::DoSend(const std::string
& app_id
,
532 const std::string
& receiver_id
,
533 const GCMClient::OutgoingMessage
& message
) {
534 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
535 io_thread_
->PostTask(
537 base::Bind(&GCMDriverDesktop::IOWorker::Send
,
538 base::Unretained(io_worker_
.get()),
544 GCMClient
* GCMDriverDesktop::GetGCMClientForTesting() const {
545 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
546 return io_worker_
? io_worker_
->gcm_client_for_testing() : NULL
;
549 bool GCMDriverDesktop::IsStarted() const {
550 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
554 bool GCMDriverDesktop::IsConnected() const {
558 void GCMDriverDesktop::GetGCMStatistics(
559 const GetGCMStatisticsCallback
& callback
,
561 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
562 DCHECK(!callback
.is_null());
564 request_gcm_statistics_callback_
= callback
;
565 io_thread_
->PostTask(
567 base::Bind(&GCMDriverDesktop::IOWorker::GetGCMStatistics
,
568 base::Unretained(io_worker_
.get()),
572 void GCMDriverDesktop::SetGCMRecording(const GetGCMStatisticsCallback
& callback
,
574 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
576 request_gcm_statistics_callback_
= callback
;
577 io_thread_
->PostTask(
579 base::Bind(&GCMDriverDesktop::IOWorker::SetGCMRecording
,
580 base::Unretained(io_worker_
.get()),
584 GCMClient::Result
GCMDriverDesktop::EnsureStarted() {
585 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
588 return GCMClient::SUCCESS
;
591 return GCMClient::GCM_DISABLED
;
593 // Have any app requested the service?
594 if (app_handlers().empty())
595 return GCMClient::UNKNOWN_ERROR
;
597 // TODO(jianli): To be removed when sign-in enforcement is dropped.
598 if (!signed_in_
&& !GCMDriver::IsAllowedForAllUsers())
599 return GCMClient::NOT_SIGNED_IN
;
601 DCHECK(!delayed_task_controller_
);
602 delayed_task_controller_
.reset(new DelayedTaskController
);
604 // Note that we need to pass weak pointer again since the existing weak
605 // pointer in IOWorker might have been invalidated when check-out occurs.
606 io_thread_
->PostTask(
608 base::Bind(&GCMDriverDesktop::IOWorker::Start
,
609 base::Unretained(io_worker_
.get()),
610 weak_ptr_factory_
.GetWeakPtr()));
613 return GCMClient::SUCCESS
;
616 void GCMDriverDesktop::RemoveCachedData() {
617 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
618 // Remove all the queued tasks since they no longer make sense after
619 // GCM service is stopped.
620 weak_ptr_factory_
.InvalidateWeakPtrs();
622 gcm_started_
= false;
623 delayed_task_controller_
.reset();
627 void GCMDriverDesktop::MessageReceived(
628 const std::string
& app_id
,
629 const GCMClient::IncomingMessage
& message
) {
630 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
632 // Drop the event if the service has been stopped.
636 GetAppHandler(app_id
)->OnMessage(app_id
, message
);
639 void GCMDriverDesktop::MessagesDeleted(const std::string
& app_id
) {
640 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
642 // Drop the event if the service has been stopped.
646 GetAppHandler(app_id
)->OnMessagesDeleted(app_id
);
649 void GCMDriverDesktop::MessageSendError(
650 const std::string
& app_id
,
651 const GCMClient::SendErrorDetails
& send_error_details
) {
652 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
654 // Drop the event if the service has been stopped.
658 GetAppHandler(app_id
)->OnSendError(app_id
, send_error_details
);
661 void GCMDriverDesktop::GCMClientReady() {
662 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
664 delayed_task_controller_
->SetReady();
667 void GCMDriverDesktop::OnConnected(const net::IPEndPoint
& ip_endpoint
) {
668 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
672 // Drop the event if the service has been stopped.
676 const GCMAppHandlerMap
& app_handler_map
= app_handlers();
677 for (GCMAppHandlerMap::const_iterator iter
= app_handler_map
.begin();
678 iter
!= app_handler_map
.end(); ++iter
) {
679 iter
->second
->OnConnected(ip_endpoint
);
682 GetAppHandler(kDefaultAppHandler
)->OnConnected(ip_endpoint
);
685 void GCMDriverDesktop::OnDisconnected() {
686 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
690 // Drop the event if the service has been stopped.
694 const GCMAppHandlerMap
& app_handler_map
= app_handlers();
695 for (GCMAppHandlerMap::const_iterator iter
= app_handler_map
.begin();
696 iter
!= app_handler_map
.end(); ++iter
) {
697 iter
->second
->OnDisconnected();
700 GetAppHandler(kDefaultAppHandler
)->OnDisconnected();
703 void GCMDriverDesktop::GetGCMStatisticsFinished(
704 const GCMClient::GCMStatistics
& stats
) {
705 DCHECK(ui_thread_
->RunsTasksOnCurrentThread());
707 // Normally request_gcm_statistics_callback_ would not be null.
708 if (!request_gcm_statistics_callback_
.is_null())
709 request_gcm_statistics_callback_
.Run(stats
);
711 LOG(WARNING
) << "request_gcm_statistics_callback_ is NULL.";