Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: chrome/browser/services/gcm/gcm_profile_service.cc

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

Powered by Google App Engine
This is Rietveld 408576698