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

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

Powered by Google App Engine
This is Rietveld 408576698