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> | |
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 |
OLD | NEW |