OLD | NEW |
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> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/base64.h" | 11 #include "base/base64.h" |
12 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
15 #include "base/prefs/pref_service.h" | 15 #include "base/prefs/pref_service.h" |
16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
17 #include "base/threading/sequenced_worker_pool.h" | 17 #include "base/threading/sequenced_worker_pool.h" |
18 #include "chrome/browser/chrome_notification_types.h" | 18 #include "chrome/browser/chrome_notification_types.h" |
19 #include "chrome/browser/extensions/state_store.h" | |
20 #include "chrome/browser/services/gcm/gcm_app_handler.h" | 19 #include "chrome/browser/services/gcm/gcm_app_handler.h" |
21 #include "chrome/browser/services/gcm/gcm_client_factory.h" | 20 #include "chrome/browser/services/gcm/gcm_client_factory.h" |
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" | 21 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
23 #include "chrome/browser/signin/signin_manager.h" | 22 #include "chrome/browser/signin/signin_manager.h" |
24 #include "chrome/browser/signin/signin_manager_factory.h" | 23 #include "chrome/browser/signin/signin_manager_factory.h" |
25 #include "chrome/common/chrome_constants.h" | 24 #include "chrome/common/chrome_constants.h" |
26 #include "chrome/common/chrome_paths.h" | 25 #include "chrome/common/chrome_paths.h" |
27 #include "chrome/common/chrome_version_info.h" | 26 #include "chrome/common/chrome_version_info.h" |
28 #include "chrome/common/pref_names.h" | 27 #include "chrome/common/pref_names.h" |
29 #include "components/signin/core/profile_oauth2_token_service.h" | 28 #include "components/signin/core/profile_oauth2_token_service.h" |
30 #include "components/user_prefs/pref_registry_syncable.h" | 29 #include "components/user_prefs/pref_registry_syncable.h" |
31 #include "content/public/browser/browser_thread.h" | 30 #include "content/public/browser/browser_thread.h" |
32 #include "content/public/browser/notification_details.h" | 31 #include "content/public/browser/notification_details.h" |
33 #include "content/public/browser/notification_source.h" | 32 #include "content/public/browser/notification_source.h" |
34 #include "extensions/browser/extension_system.h" | |
35 #include "google_apis/gcm/protocol/android_checkin.pb.h" | 33 #include "google_apis/gcm/protocol/android_checkin.pb.h" |
36 #include "net/url_request/url_request_context_getter.h" | 34 #include "net/url_request/url_request_context_getter.h" |
37 | 35 |
38 namespace gcm { | 36 namespace gcm { |
39 | 37 |
40 namespace { | 38 namespace { |
41 | 39 |
42 const char kRegistrationKey[] = "gcm.registration"; | |
43 const char kSendersKey[] = "senders"; | |
44 const char kRegistrationIDKey[] = "reg_id"; | |
45 | |
46 checkin_proto::ChromeBuildProto_Platform GetPlatform() { | 40 checkin_proto::ChromeBuildProto_Platform GetPlatform() { |
47 #if defined(OS_WIN) | 41 #if defined(OS_WIN) |
48 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN; | 42 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN; |
49 #elif defined(OS_MACOSX) | 43 #elif defined(OS_MACOSX) |
50 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC; | 44 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC; |
51 #elif defined(OS_CHROMEOS) | 45 #elif defined(OS_CHROMEOS) |
52 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS; | 46 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS; |
53 #elif defined(OS_LINUX) | 47 #elif defined(OS_LINUX) |
54 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX; | 48 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX; |
55 #else | 49 #else |
(...skipping 27 matching lines...) Expand all Loading... |
83 } | 77 } |
84 | 78 |
85 } // namespace | 79 } // namespace |
86 | 80 |
87 // Helper class to save tasks to run until we're ready to execute them. | 81 // Helper class to save tasks to run until we're ready to execute them. |
88 class GCMProfileService::DelayedTaskController { | 82 class GCMProfileService::DelayedTaskController { |
89 public: | 83 public: |
90 DelayedTaskController(); | 84 DelayedTaskController(); |
91 ~DelayedTaskController(); | 85 ~DelayedTaskController(); |
92 | 86 |
93 // Adds an app to the tracking list. It will be first marked as not ready. | 87 // Adds a task that will be invoked once we're ready. |
94 // Tasks will be queued for delay execution until the app is marked as ready. | 88 void AddTask(base::Closure task); |
95 void AddApp(const std::string& app_id); | |
96 | 89 |
97 // Removes the app from the tracking list. | 90 // Sets ready status. It is ready only when check-in is completed and |
98 void RemoveApp(const std::string& app_id); | 91 // the GCMClient is fully initialized. |
| 92 void SetReady(); |
99 | 93 |
100 // Adds a task that will be invoked once we're ready. | 94 // Returns true if it is ready to perform tasks. |
101 void AddTask(const std::string& app_id, base::Closure task); | 95 bool CanRunTaskWithoutDelay() const; |
102 | |
103 // Sets GCM ready status. GCM is ready only when check-in is completed and | |
104 // the GCMClient is fully initialized. If it is set to ready for the first | |
105 // time, all the pending tasks for any ready apps will be run. | |
106 void SetGCMReady(); | |
107 | |
108 // Sets ready status for the app. If GCM is already ready, all the pending | |
109 // tasks for this app will be run. | |
110 void SetAppReady(const std::string& app_id); | |
111 | |
112 // Returns true if it is ready to perform operations for an app. | |
113 bool CanRunTaskWithoutDelay(const std::string& app_id) const; | |
114 | |
115 // Returns true if the app has been tracked for readiness. | |
116 bool IsAppTracked(const std::string& app_id) const; | |
117 | 96 |
118 private: | 97 private: |
119 struct AppTaskQueue { | 98 void RunTasks(); |
120 AppTaskQueue(); | |
121 ~AppTaskQueue(); | |
122 | 99 |
123 // The flag that indicates if GCMClient is ready. | 100 // Flag that indicates that GCM is ready. |
124 bool ready; | 101 bool ready_; |
125 | 102 |
126 // Tasks to be invoked upon ready. | 103 std::vector<base::Closure> delayed_tasks_; |
127 std::vector<base::Closure> tasks; | |
128 }; | |
129 | |
130 void RunTasks(AppTaskQueue* task_queue); | |
131 | |
132 // Flag that indicates that GCM is done. | |
133 bool gcm_ready_; | |
134 | |
135 // Map from app_id to AppTaskQueue storing the tasks that will be invoked | |
136 // when both GCM and the app get ready. | |
137 typedef std::map<std::string, AppTaskQueue*> DelayedTaskMap; | |
138 DelayedTaskMap delayed_task_map_; | |
139 }; | 104 }; |
140 | 105 |
141 GCMProfileService::DelayedTaskController::AppTaskQueue::AppTaskQueue() | |
142 : ready(false) { | |
143 } | |
144 | |
145 GCMProfileService::DelayedTaskController::AppTaskQueue::~AppTaskQueue() { | |
146 } | |
147 | |
148 GCMProfileService::DelayedTaskController::DelayedTaskController() | 106 GCMProfileService::DelayedTaskController::DelayedTaskController() |
149 : gcm_ready_(false) { | 107 : ready_(false) { |
150 } | 108 } |
151 | 109 |
152 GCMProfileService::DelayedTaskController::~DelayedTaskController() { | 110 GCMProfileService::DelayedTaskController::~DelayedTaskController() { |
153 for (DelayedTaskMap::const_iterator iter = delayed_task_map_.begin(); | |
154 iter != delayed_task_map_.end(); ++iter) { | |
155 delete iter->second; | |
156 } | |
157 } | 111 } |
158 | 112 |
159 void GCMProfileService::DelayedTaskController::AddApp( | 113 void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) { |
160 const std::string& app_id) { | 114 delayed_tasks_.push_back(task); |
161 DCHECK(delayed_task_map_.find(app_id) == delayed_task_map_.end()); | |
162 delayed_task_map_[app_id] = new AppTaskQueue; | |
163 } | 115 } |
164 | 116 |
165 void GCMProfileService::DelayedTaskController::RemoveApp( | 117 void GCMProfileService::DelayedTaskController::SetReady() { |
166 const std::string& app_id) { | 118 ready_ = true; |
167 DelayedTaskMap::iterator iter = delayed_task_map_.find(app_id); | 119 RunTasks(); |
168 if (iter == delayed_task_map_.end()) | |
169 return; | |
170 delete iter->second; | |
171 delayed_task_map_.erase(iter); | |
172 } | 120 } |
173 | 121 |
174 void GCMProfileService::DelayedTaskController::AddTask( | 122 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const { |
175 const std::string& app_id, base::Closure task) { | 123 return ready_; |
176 DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id); | |
177 DCHECK(iter != delayed_task_map_.end()); | |
178 iter->second->tasks.push_back(task); | |
179 } | 124 } |
180 | 125 |
181 void GCMProfileService::DelayedTaskController::SetGCMReady() { | 126 void GCMProfileService::DelayedTaskController::RunTasks() { |
182 gcm_ready_ = true; | 127 DCHECK(ready_); |
183 | 128 |
184 for (DelayedTaskMap::iterator iter = delayed_task_map_.begin(); | 129 for (size_t i = 0; i < delayed_tasks_.size(); ++i) |
185 iter != delayed_task_map_.end(); ++iter) { | 130 delayed_tasks_[i].Run(); |
186 if (iter->second->ready) | 131 delayed_tasks_.clear(); |
187 RunTasks(iter->second); | |
188 } | |
189 } | |
190 | |
191 void GCMProfileService::DelayedTaskController::SetAppReady( | |
192 const std::string& app_id) { | |
193 DelayedTaskMap::iterator iter = delayed_task_map_.find(app_id); | |
194 DCHECK(iter != delayed_task_map_.end()); | |
195 | |
196 AppTaskQueue* task_queue = iter->second; | |
197 DCHECK(task_queue); | |
198 task_queue->ready = true; | |
199 | |
200 if (gcm_ready_) | |
201 RunTasks(task_queue); | |
202 } | |
203 | |
204 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay( | |
205 const std::string& app_id) const { | |
206 if (!gcm_ready_) | |
207 return false; | |
208 DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id); | |
209 if (iter == delayed_task_map_.end()) | |
210 return true; | |
211 return iter->second->ready; | |
212 } | |
213 | |
214 bool GCMProfileService::DelayedTaskController::IsAppTracked( | |
215 const std::string& app_id) const { | |
216 return delayed_task_map_.find(app_id) != delayed_task_map_.end(); | |
217 } | |
218 | |
219 void GCMProfileService::DelayedTaskController::RunTasks( | |
220 AppTaskQueue* task_queue) { | |
221 DCHECK(gcm_ready_ && task_queue->ready); | |
222 | |
223 for (size_t i = 0; i < task_queue->tasks.size(); ++i) | |
224 task_queue->tasks[i].Run(); | |
225 task_queue->tasks.clear(); | |
226 } | 132 } |
227 | 133 |
228 class GCMProfileService::IOWorker | 134 class GCMProfileService::IOWorker |
229 : public GCMClient::Delegate, | 135 : public GCMClient::Delegate, |
230 public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{ | 136 public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{ |
231 public: | 137 public: |
232 // Called on UI thread. | 138 // Called on UI thread. |
233 IOWorker(); | 139 IOWorker(); |
234 | 140 |
235 // Overridden from GCMClient::Delegate: | 141 // Overridden from GCMClient::Delegate: |
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
487 case GCMProfileService::ENABLED_FOR_APPS: | 393 case GCMProfileService::ENABLED_FOR_APPS: |
488 return "ENABLED_FOR_APPS"; | 394 return "ENABLED_FOR_APPS"; |
489 case GCMProfileService::ALWAYS_DISABLED: | 395 case GCMProfileService::ALWAYS_DISABLED: |
490 return "ALWAYS_DISABLED"; | 396 return "ALWAYS_DISABLED"; |
491 default: | 397 default: |
492 NOTREACHED(); | 398 NOTREACHED(); |
493 return std::string(); | 399 return std::string(); |
494 } | 400 } |
495 } | 401 } |
496 | 402 |
497 GCMProfileService::RegistrationInfo::RegistrationInfo() { | |
498 } | |
499 | |
500 GCMProfileService::RegistrationInfo::~RegistrationInfo() { | |
501 } | |
502 | |
503 bool GCMProfileService::RegistrationInfo::IsValid() const { | |
504 return !sender_ids.empty() && !registration_id.empty(); | |
505 } | |
506 | |
507 // static | 403 // static |
508 GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState( | 404 GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState( |
509 Profile* profile) { | 405 Profile* profile) { |
510 const base::Value* gcm_enabled_value = | 406 const base::Value* gcm_enabled_value = |
511 profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled); | 407 profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled); |
512 if (!gcm_enabled_value) | 408 if (!gcm_enabled_value) |
513 return ENABLED_FOR_APPS; | 409 return ENABLED_FOR_APPS; |
514 | 410 |
515 bool gcm_enabled = false; | 411 bool gcm_enabled = false; |
516 if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled)) | 412 if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled)) |
(...skipping 10 matching lines...) Expand all Loading... |
527 bool on_by_default = false; | 423 bool on_by_default = false; |
528 if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || | 424 if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || |
529 channel == chrome::VersionInfo::CHANNEL_CANARY || | 425 channel == chrome::VersionInfo::CHANNEL_CANARY || |
530 channel == chrome::VersionInfo::CHANNEL_DEV) { | 426 channel == chrome::VersionInfo::CHANNEL_DEV) { |
531 on_by_default = true; | 427 on_by_default = true; |
532 } | 428 } |
533 registry->RegisterBooleanPref( | 429 registry->RegisterBooleanPref( |
534 prefs::kGCMChannelEnabled, | 430 prefs::kGCMChannelEnabled, |
535 on_by_default, | 431 on_by_default, |
536 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | 432 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
537 registry->RegisterListPref( | |
538 prefs::kGCMRegisteredAppIDs, | |
539 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
540 } | 433 } |
541 | 434 |
542 GCMProfileService::GCMProfileService(Profile* profile) | 435 GCMProfileService::GCMProfileService(Profile* profile) |
543 : profile_(profile), | 436 : profile_(profile), |
544 gcm_client_ready_(false), | 437 gcm_client_ready_(false), |
545 weak_ptr_factory_(this) { | 438 weak_ptr_factory_(this) { |
546 DCHECK(!profile->IsOffTheRecord()); | 439 DCHECK(!profile->IsOffTheRecord()); |
547 } | 440 } |
548 | 441 |
549 GCMProfileService::~GCMProfileService() { | 442 GCMProfileService::~GCMProfileService() { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 | 538 |
646 // If previous un/register operation is still in progress, bail out. | 539 // If previous un/register operation is still in progress, bail out. |
647 if (IsAsyncOperationPending(app_id)) { | 540 if (IsAsyncOperationPending(app_id)) { |
648 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); | 541 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); |
649 return; | 542 return; |
650 } | 543 } |
651 | 544 |
652 register_callbacks_[app_id] = callback; | 545 register_callbacks_[app_id] = callback; |
653 | 546 |
654 // Delay the register operation until GCMClient is ready. | 547 // Delay the register operation until GCMClient is ready. |
655 if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) { | 548 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { |
656 delayed_task_controller_->AddTask( | 549 delayed_task_controller_->AddTask( |
657 app_id, | |
658 base::Bind(&GCMProfileService::DoRegister, | 550 base::Bind(&GCMProfileService::DoRegister, |
659 weak_ptr_factory_.GetWeakPtr(), | 551 weak_ptr_factory_.GetWeakPtr(), |
660 app_id, | 552 app_id, |
661 sender_ids)); | 553 sender_ids)); |
662 return; | 554 return; |
663 } | 555 } |
664 | 556 |
665 DoRegister(app_id, sender_ids); | 557 DoRegister(app_id, sender_ids); |
666 } | 558 } |
667 | 559 |
668 void GCMProfileService::DoRegister(const std::string& app_id, | 560 void GCMProfileService::DoRegister(const std::string& app_id, |
669 const std::vector<std::string>& sender_ids) { | 561 const std::vector<std::string>& sender_ids) { |
670 std::map<std::string, RegisterCallback>::iterator callback_iter = | 562 std::map<std::string, RegisterCallback>::iterator callback_iter = |
671 register_callbacks_.find(app_id); | 563 register_callbacks_.find(app_id); |
672 if (callback_iter == register_callbacks_.end()) { | 564 if (callback_iter == register_callbacks_.end()) { |
673 // The callback could have been removed when the app is uninstalled. | 565 // The callback could have been removed when the app is uninstalled. |
674 return; | 566 return; |
675 } | 567 } |
676 | 568 |
677 // Normalize the sender IDs by making them sorted. | 569 // Normalize the sender IDs by making them sorted. |
678 std::vector<std::string> normalized_sender_ids = sender_ids; | 570 std::vector<std::string> normalized_sender_ids = sender_ids; |
679 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); | 571 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); |
680 | 572 |
681 // If the same sender ids is provided, return the cached registration ID | |
682 // directly. | |
683 RegistrationInfoMap::const_iterator registration_info_iter = | |
684 registration_info_map_.find(app_id); | |
685 if (registration_info_iter != registration_info_map_.end() && | |
686 registration_info_iter->second.sender_ids == normalized_sender_ids) { | |
687 RegisterCallback callback = callback_iter->second; | |
688 register_callbacks_.erase(callback_iter); | |
689 callback.Run(registration_info_iter->second.registration_id, | |
690 GCMClient::SUCCESS); | |
691 return; | |
692 } | |
693 | |
694 // Cache the sender IDs. The registration ID will be filled when the | |
695 // registration completes. | |
696 RegistrationInfo registration_info; | |
697 registration_info.sender_ids = normalized_sender_ids; | |
698 registration_info_map_[app_id] = registration_info; | |
699 | |
700 // Save the IDs of all registered apps such that we know what to remove from | |
701 // the the app's state store when the profile is signed out. | |
702 WriteRegisteredAppIDs(); | |
703 | |
704 content::BrowserThread::PostTask( | 573 content::BrowserThread::PostTask( |
705 content::BrowserThread::IO, | 574 content::BrowserThread::IO, |
706 FROM_HERE, | 575 FROM_HERE, |
707 base::Bind(&GCMProfileService::IOWorker::Register, | 576 base::Bind(&GCMProfileService::IOWorker::Register, |
708 io_worker_, | 577 io_worker_, |
709 app_id, | 578 app_id, |
710 normalized_sender_ids)); | 579 normalized_sender_ids)); |
711 } | 580 } |
712 | 581 |
713 void GCMProfileService::Unregister(const std::string& app_id, | 582 void GCMProfileService::Unregister(const std::string& app_id, |
714 UnregisterCallback callback) { | 583 UnregisterCallback callback) { |
715 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 584 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
716 DCHECK(!app_id.empty() && !callback.is_null()); | 585 DCHECK(!app_id.empty() && !callback.is_null()); |
717 | 586 |
718 GCMClient::Result result = EnsureAppReady(app_id); | 587 GCMClient::Result result = EnsureAppReady(app_id); |
719 if (result != GCMClient::SUCCESS) { | 588 if (result != GCMClient::SUCCESS) { |
720 callback.Run(result); | 589 callback.Run(result); |
721 return; | 590 return; |
722 } | 591 } |
723 | 592 |
724 // If previous un/register operation is still in progress, bail out. | 593 // If previous un/register operation is still in progress, bail out. |
725 if (IsAsyncOperationPending(app_id)) { | 594 if (IsAsyncOperationPending(app_id)) { |
726 callback.Run(GCMClient::ASYNC_OPERATION_PENDING); | 595 callback.Run(GCMClient::ASYNC_OPERATION_PENDING); |
727 return; | 596 return; |
728 } | 597 } |
729 | 598 |
730 unregister_callbacks_[app_id] = callback; | 599 unregister_callbacks_[app_id] = callback; |
731 | 600 |
732 // Delay the unregister operation until GCMClient is ready. | 601 // Delay the unregister operation until GCMClient is ready. |
733 if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) { | 602 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { |
734 delayed_task_controller_->AddTask( | 603 delayed_task_controller_->AddTask( |
735 app_id, | |
736 base::Bind(&GCMProfileService::DoUnregister, | 604 base::Bind(&GCMProfileService::DoUnregister, |
737 weak_ptr_factory_.GetWeakPtr(), | 605 weak_ptr_factory_.GetWeakPtr(), |
738 app_id)); | 606 app_id)); |
739 return; | 607 return; |
740 } | 608 } |
741 | 609 |
742 DoUnregister(app_id); | 610 DoUnregister(app_id); |
743 } | 611 } |
744 | 612 |
745 void GCMProfileService::DoUnregister(const std::string& app_id) { | 613 void GCMProfileService::DoUnregister(const std::string& app_id) { |
746 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 614 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
747 | 615 |
748 // Unregister might be triggered in response to a message that is missing | |
749 // recipient and in that case there will not be an entry in the map. | |
750 RegistrationInfoMap::iterator registration_info_iter = | |
751 registration_info_map_.find(app_id); | |
752 if (registration_info_iter != registration_info_map_.end()) { | |
753 registration_info_map_.erase(registration_info_iter); | |
754 | |
755 // Update the persisted IDs of registered apps. | |
756 WriteRegisteredAppIDs(); | |
757 | |
758 // Remove the persisted registration info. | |
759 DeleteRegistrationInfo(app_id); | |
760 | |
761 // No need to track the app any more. | |
762 delayed_task_controller_->RemoveApp(app_id); | |
763 } | |
764 | |
765 // Ask the server to unregister it. There could be a small chance that the | 616 // Ask the server to unregister it. There could be a small chance that the |
766 // unregister request fails. If this occurs, it does not bring any harm since | 617 // unregister request fails. If this occurs, it does not bring any harm since |
767 // we simply reject the messages/events received from the server. | 618 // we simply reject the messages/events received from the server. |
768 content::BrowserThread::PostTask( | 619 content::BrowserThread::PostTask( |
769 content::BrowserThread::IO, | 620 content::BrowserThread::IO, |
770 FROM_HERE, | 621 FROM_HERE, |
771 base::Bind(&GCMProfileService::IOWorker::Unregister, | 622 base::Bind(&GCMProfileService::IOWorker::Unregister, |
772 io_worker_, | 623 io_worker_, |
773 app_id)); | 624 app_id)); |
774 } | 625 } |
(...skipping 14 matching lines...) Expand all Loading... |
789 // If the message with send ID is still in progress, bail out. | 640 // If the message with send ID is still in progress, bail out. |
790 std::pair<std::string, std::string> key(app_id, message.id); | 641 std::pair<std::string, std::string> key(app_id, message.id); |
791 if (send_callbacks_.find(key) != send_callbacks_.end()) { | 642 if (send_callbacks_.find(key) != send_callbacks_.end()) { |
792 callback.Run(message.id, GCMClient::INVALID_PARAMETER); | 643 callback.Run(message.id, GCMClient::INVALID_PARAMETER); |
793 return; | 644 return; |
794 } | 645 } |
795 | 646 |
796 send_callbacks_[key] = callback; | 647 send_callbacks_[key] = callback; |
797 | 648 |
798 // Delay the send operation until all GCMClient is ready. | 649 // Delay the send operation until all GCMClient is ready. |
799 if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) { | 650 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { |
800 delayed_task_controller_->AddTask( | 651 delayed_task_controller_->AddTask( |
801 app_id, | |
802 base::Bind(&GCMProfileService::DoSend, | 652 base::Bind(&GCMProfileService::DoSend, |
803 weak_ptr_factory_.GetWeakPtr(), | 653 weak_ptr_factory_.GetWeakPtr(), |
804 app_id, | 654 app_id, |
805 receiver_id, | 655 receiver_id, |
806 message)); | 656 message)); |
807 return; | 657 return; |
808 } | 658 } |
809 | 659 |
810 DoSend(app_id, receiver_id, message); | 660 DoSend(app_id, receiver_id, message); |
811 } | 661 } |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
883 // CheckIn could be called more than once when: | 733 // CheckIn could be called more than once when: |
884 // 1) The password changes. | 734 // 1) The password changes. |
885 // 2) Register/send function calls it to ensure CheckIn is done. | 735 // 2) Register/send function calls it to ensure CheckIn is done. |
886 if (username_ == username) | 736 if (username_ == username) |
887 return; | 737 return; |
888 username_ = username; | 738 username_ = username; |
889 | 739 |
890 DCHECK(!delayed_task_controller_); | 740 DCHECK(!delayed_task_controller_); |
891 delayed_task_controller_.reset(new DelayedTaskController); | 741 delayed_task_controller_.reset(new DelayedTaskController); |
892 | 742 |
893 // Load all the registered apps. | |
894 ReadRegisteredAppIDs(); | |
895 | |
896 // This will load the data from the gcm store and trigger the check-in if | 743 // This will load the data from the gcm store and trigger the check-in if |
897 // the persisted check-in info is not found. | 744 // the persisted check-in info is not found. |
898 // Note that we need to pass weak pointer again since the existing weak | 745 // Note that we need to pass weak pointer again since the existing weak |
899 // pointer in IOWorker might have been invalidated when check-out occurs. | 746 // pointer in IOWorker might have been invalidated when check-out occurs. |
900 content::BrowserThread::PostTask( | 747 content::BrowserThread::PostTask( |
901 content::BrowserThread::IO, | 748 content::BrowserThread::IO, |
902 FROM_HERE, | 749 FROM_HERE, |
903 base::Bind(&GCMProfileService::IOWorker::Load, | 750 base::Bind(&GCMProfileService::IOWorker::Load, |
904 io_worker_, | 751 io_worker_, |
905 weak_ptr_factory_.GetWeakPtr())); | 752 weak_ptr_factory_.GetWeakPtr())); |
906 } | 753 } |
907 | 754 |
908 void GCMProfileService::RemoveCachedData() { | 755 void GCMProfileService::RemoveCachedData() { |
909 // Remove all the queued tasks since they no longer make sense after | 756 // Remove all the queued tasks since they no longer make sense after |
910 // GCM service is stopped. | 757 // GCM service is stopped. |
911 weak_ptr_factory_.InvalidateWeakPtrs(); | 758 weak_ptr_factory_.InvalidateWeakPtrs(); |
912 | 759 |
913 username_.clear(); | 760 username_.clear(); |
914 gcm_client_ready_ = false; | 761 gcm_client_ready_ = false; |
915 delayed_task_controller_.reset(); | 762 delayed_task_controller_.reset(); |
916 register_callbacks_.clear(); | 763 register_callbacks_.clear(); |
917 send_callbacks_.clear(); | 764 send_callbacks_.clear(); |
918 registration_info_map_.clear(); | |
919 } | |
920 | |
921 void GCMProfileService::RemovePersistedData() { | |
922 // Remove persisted data from app's state store. | |
923 for (RegistrationInfoMap::const_iterator iter = | |
924 registration_info_map_.begin(); | |
925 iter != registration_info_map_.end(); ++iter) { | |
926 DeleteRegistrationInfo(iter->first); | |
927 } | |
928 | |
929 // Remove persisted data from prefs store. | |
930 profile_->GetPrefs()->ClearPref(prefs::kGCMRegisteredAppIDs); | |
931 } | 765 } |
932 | 766 |
933 void GCMProfileService::CheckOut() { | 767 void GCMProfileService::CheckOut() { |
934 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 768 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
935 | 769 |
936 // We still proceed with the check-out logic even if the check-in is not | 770 // We still proceed with the check-out logic even if the check-in is not |
937 // initiated in the current session. This will make sure that all the | 771 // initiated in the current session. This will make sure that all the |
938 // persisted data written previously will get purged. | 772 // persisted data written previously will get purged. |
939 | 773 |
940 // This has to be done before removing the cached data since we need to do | |
941 // the lookup based on the cached data. | |
942 RemovePersistedData(); | |
943 | |
944 RemoveCachedData(); | 774 RemoveCachedData(); |
945 | 775 |
946 content::BrowserThread::PostTask( | 776 content::BrowserThread::PostTask( |
947 content::BrowserThread::IO, | 777 content::BrowserThread::IO, |
948 FROM_HERE, | 778 FROM_HERE, |
949 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); | 779 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); |
950 } | 780 } |
951 | 781 |
952 void GCMProfileService::ResetGCMClient() { | 782 void GCMProfileService::ResetGCMClient() { |
953 content::BrowserThread::PostTask( | 783 content::BrowserThread::PostTask( |
954 content::BrowserThread::IO, | 784 content::BrowserThread::IO, |
955 FROM_HERE, | 785 FROM_HERE, |
956 base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_)); | 786 base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_)); |
957 } | 787 } |
958 | 788 |
959 GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) { | 789 GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) { |
960 // Ensure that check-in has been done. | 790 // Ensure that check-in has been done. |
961 EnsureLoaded(); | 791 EnsureLoaded(); |
962 | 792 |
963 // If the profile was not signed in, bail out. | 793 // If the profile was not signed in, bail out. |
964 if (username_.empty()) | 794 if (username_.empty()) |
965 return GCMClient::NOT_SIGNED_IN; | 795 return GCMClient::NOT_SIGNED_IN; |
966 | 796 |
967 // Ensure that app registration information was read. | |
968 if (!delayed_task_controller_->IsAppTracked(app_id)) | |
969 ReadRegistrationInfo(app_id); | |
970 | |
971 return GCMClient::SUCCESS; | 797 return GCMClient::SUCCESS; |
972 } | 798 } |
973 | 799 |
974 bool GCMProfileService::IsAsyncOperationPending( | 800 bool GCMProfileService::IsAsyncOperationPending( |
975 const std::string& app_id) const { | 801 const std::string& app_id) const { |
976 return register_callbacks_.find(app_id) != register_callbacks_.end() || | 802 return register_callbacks_.find(app_id) != register_callbacks_.end() || |
977 unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); | 803 unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); |
978 } | 804 } |
979 | 805 |
980 void GCMProfileService::RegisterFinished(const std::string& app_id, | 806 void GCMProfileService::RegisterFinished(const std::string& app_id, |
981 const std::string& registration_id, | 807 const std::string& registration_id, |
982 GCMClient::Result result) { | 808 GCMClient::Result result) { |
983 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 809 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
984 | 810 |
985 std::map<std::string, RegisterCallback>::iterator callback_iter = | 811 std::map<std::string, RegisterCallback>::iterator callback_iter = |
986 register_callbacks_.find(app_id); | 812 register_callbacks_.find(app_id); |
987 if (callback_iter == register_callbacks_.end()) { | 813 if (callback_iter == register_callbacks_.end()) { |
988 // The callback could have been removed when the app is uninstalled. | 814 // The callback could have been removed when the app is uninstalled. |
989 return; | 815 return; |
990 } | 816 } |
991 | 817 |
992 // Cache the registration ID if the registration succeeds. Otherwise, | |
993 // removed the cached info. | |
994 RegistrationInfoMap::iterator registration_info_iter = | |
995 registration_info_map_.find(app_id); | |
996 // This is unlikely to happen because the app will not be uninstalled before | |
997 // the asynchronous extension function completes. | |
998 DCHECK(registration_info_iter != registration_info_map_.end()); | |
999 if (result == GCMClient::SUCCESS) { | |
1000 registration_info_iter->second.registration_id = registration_id; | |
1001 WriteRegistrationInfo(app_id); | |
1002 } else { | |
1003 registration_info_map_.erase(registration_info_iter); | |
1004 } | |
1005 | |
1006 RegisterCallback callback = callback_iter->second; | 818 RegisterCallback callback = callback_iter->second; |
1007 register_callbacks_.erase(callback_iter); | 819 register_callbacks_.erase(callback_iter); |
1008 callback.Run(registration_id, result); | 820 callback.Run(registration_id, result); |
1009 } | 821 } |
1010 | 822 |
1011 void GCMProfileService::UnregisterFinished(const std::string& app_id, | 823 void GCMProfileService::UnregisterFinished(const std::string& app_id, |
1012 GCMClient::Result result) { | 824 GCMClient::Result result) { |
1013 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 825 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
1014 | 826 |
1015 std::map<std::string, UnregisterCallback>::iterator callback_iter = | 827 std::map<std::string, UnregisterCallback>::iterator callback_iter = |
(...skipping 25 matching lines...) Expand all Loading... |
1041 } | 853 } |
1042 | 854 |
1043 void GCMProfileService::MessageReceived(const std::string& app_id, | 855 void GCMProfileService::MessageReceived(const std::string& app_id, |
1044 GCMClient::IncomingMessage message) { | 856 GCMClient::IncomingMessage message) { |
1045 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 857 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
1046 | 858 |
1047 // Drop the event if signed out. | 859 // Drop the event if signed out. |
1048 if (username_.empty()) | 860 if (username_.empty()) |
1049 return; | 861 return; |
1050 | 862 |
1051 // Dropping the message when application does not have a registration entry | |
1052 // or the app is not registered for the sender of the message. | |
1053 RegistrationInfoMap::iterator iter = registration_info_map_.find(app_id); | |
1054 if (iter == registration_info_map_.end() || | |
1055 std::find(iter->second.sender_ids.begin(), | |
1056 iter->second.sender_ids.end(), | |
1057 message.sender_id) == iter->second.sender_ids.end()) { | |
1058 return; | |
1059 } | |
1060 | |
1061 GetAppHandler(app_id)->OnMessage(app_id, message); | 863 GetAppHandler(app_id)->OnMessage(app_id, message); |
1062 } | 864 } |
1063 | 865 |
1064 void GCMProfileService::MessagesDeleted(const std::string& app_id) { | 866 void GCMProfileService::MessagesDeleted(const std::string& app_id) { |
1065 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 867 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
1066 | 868 |
1067 // Drop the event if signed out. | 869 // Drop the event if signed out. |
1068 if (username_.empty()) | 870 if (username_.empty()) |
1069 return; | 871 return; |
1070 | 872 |
(...skipping 12 matching lines...) Expand all Loading... |
1083 GetAppHandler(app_id)->OnSendError(app_id, send_error_details); | 885 GetAppHandler(app_id)->OnSendError(app_id, send_error_details); |
1084 } | 886 } |
1085 | 887 |
1086 void GCMProfileService::GCMClientReady() { | 888 void GCMProfileService::GCMClientReady() { |
1087 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 889 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
1088 | 890 |
1089 if (gcm_client_ready_) | 891 if (gcm_client_ready_) |
1090 return; | 892 return; |
1091 gcm_client_ready_ = true; | 893 gcm_client_ready_ = true; |
1092 | 894 |
1093 delayed_task_controller_->SetGCMReady(); | 895 delayed_task_controller_->SetReady(); |
1094 } | 896 } |
1095 | 897 |
1096 GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { | 898 GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { |
1097 std::map<std::string, GCMAppHandler*>::const_iterator iter = | 899 std::map<std::string, GCMAppHandler*>::const_iterator iter = |
1098 app_handlers_.find(app_id); | 900 app_handlers_.find(app_id); |
1099 return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; | 901 return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; |
1100 } | 902 } |
1101 | 903 |
1102 void GCMProfileService::ReadRegisteredAppIDs() { | |
1103 const base::ListValue* app_id_list = | |
1104 profile_->GetPrefs()->GetList(prefs::kGCMRegisteredAppIDs); | |
1105 for (size_t i = 0; i < app_id_list->GetSize(); ++i) { | |
1106 std::string app_id; | |
1107 if (!app_id_list->GetString(i, &app_id)) | |
1108 continue; | |
1109 ReadRegistrationInfo(app_id); | |
1110 } | |
1111 } | |
1112 | |
1113 void GCMProfileService::WriteRegisteredAppIDs() { | |
1114 base::ListValue apps; | |
1115 for (RegistrationInfoMap::const_iterator iter = | |
1116 registration_info_map_.begin(); | |
1117 iter != registration_info_map_.end(); ++iter) { | |
1118 apps.Append(new base::StringValue(iter->first)); | |
1119 } | |
1120 profile_->GetPrefs()->Set(prefs::kGCMRegisteredAppIDs, apps); | |
1121 } | |
1122 | |
1123 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) { | |
1124 extensions::StateStore* storage = | |
1125 extensions::ExtensionSystem::Get(profile_)->state_store(); | |
1126 DCHECK(storage); | |
1127 | |
1128 storage->RemoveExtensionValue(app_id, kRegistrationKey); | |
1129 } | |
1130 | |
1131 void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) { | |
1132 extensions::StateStore* storage = | |
1133 extensions::ExtensionSystem::Get(profile_)->state_store(); | |
1134 DCHECK(storage); | |
1135 | |
1136 RegistrationInfoMap::const_iterator registration_info_iter = | |
1137 registration_info_map_.find(app_id); | |
1138 if (registration_info_iter == registration_info_map_.end()) | |
1139 return; | |
1140 const RegistrationInfo& registration_info = registration_info_iter->second; | |
1141 | |
1142 scoped_ptr<base::ListValue> senders_list(new base::ListValue()); | |
1143 for (std::vector<std::string>::const_iterator senders_iter = | |
1144 registration_info.sender_ids.begin(); | |
1145 senders_iter != registration_info.sender_ids.end(); | |
1146 ++senders_iter) { | |
1147 senders_list->AppendString(*senders_iter); | |
1148 } | |
1149 | |
1150 scoped_ptr<base::DictionaryValue> registration_info_dict( | |
1151 new base::DictionaryValue()); | |
1152 registration_info_dict->Set(kSendersKey, senders_list.release()); | |
1153 registration_info_dict->SetString(kRegistrationIDKey, | |
1154 registration_info.registration_id); | |
1155 | |
1156 storage->SetExtensionValue( | |
1157 app_id, kRegistrationKey, registration_info_dict.PassAs<base::Value>()); | |
1158 } | |
1159 | |
1160 void GCMProfileService::ReadRegistrationInfo(const std::string& app_id) { | |
1161 delayed_task_controller_->AddApp(app_id); | |
1162 | |
1163 extensions::StateStore* storage = | |
1164 extensions::ExtensionSystem::Get(profile_)->state_store(); | |
1165 DCHECK(storage); | |
1166 storage->GetExtensionValue( | |
1167 app_id, | |
1168 kRegistrationKey, | |
1169 base::Bind( | |
1170 &GCMProfileService::ReadRegistrationInfoFinished, | |
1171 weak_ptr_factory_.GetWeakPtr(), | |
1172 app_id)); | |
1173 } | |
1174 | |
1175 void GCMProfileService::ReadRegistrationInfoFinished( | |
1176 const std::string& app_id, | |
1177 scoped_ptr<base::Value> value) { | |
1178 RegistrationInfo registration_info; | |
1179 if (value && | |
1180 !ParsePersistedRegistrationInfo(value.Pass(), ®istration_info)) { | |
1181 // Delete the persisted data if it is corrupted. | |
1182 DeleteRegistrationInfo(app_id); | |
1183 } | |
1184 | |
1185 if (registration_info.IsValid()) | |
1186 registration_info_map_[app_id] = registration_info; | |
1187 | |
1188 delayed_task_controller_->SetAppReady(app_id); | |
1189 } | |
1190 | |
1191 bool GCMProfileService::ParsePersistedRegistrationInfo( | |
1192 scoped_ptr<base::Value> value, | |
1193 RegistrationInfo* registration_info) { | |
1194 base::DictionaryValue* dict = NULL; | |
1195 if (!value.get() || !value->GetAsDictionary(&dict)) | |
1196 return false; | |
1197 | |
1198 if (!dict->GetString(kRegistrationIDKey, ®istration_info->registration_id)) | |
1199 return false; | |
1200 | |
1201 const base::ListValue* senders_list = NULL; | |
1202 if (!dict->GetList(kSendersKey, &senders_list) || !senders_list->GetSize()) | |
1203 return false; | |
1204 for (size_t i = 0; i < senders_list->GetSize(); ++i) { | |
1205 std::string sender; | |
1206 if (!senders_list->GetString(i, &sender)) | |
1207 return false; | |
1208 registration_info->sender_ids.push_back(sender); | |
1209 } | |
1210 | |
1211 return true; | |
1212 } | |
1213 | |
1214 void GCMProfileService::RequestGCMStatisticsFinished( | 904 void GCMProfileService::RequestGCMStatisticsFinished( |
1215 GCMClient::GCMStatistics stats) { | 905 GCMClient::GCMStatistics stats) { |
1216 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 906 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
1217 | 907 |
1218 request_gcm_statistics_callback_.Run(stats); | 908 request_gcm_statistics_callback_.Run(stats); |
1219 } | 909 } |
1220 | 910 |
1221 // static | |
1222 const char* GCMProfileService::GetPersistentRegisterKeyForTesting() { | |
1223 return kRegistrationKey; | |
1224 } | |
1225 | |
1226 } // namespace gcm | 911 } // namespace gcm |
OLD | NEW |