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
3// found in the LICENSE file.
4
5#include "chrome/browser/services/gcm/gcm_profile_service.h"
6
7#include <map>
8
9#include "base/logging.h"
10#include "base/prefs/pref_service.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/common/pref_names.h"
13#include "components/pref_registry/pref_registry_syncable.h"
14
15#if defined(OS_ANDROID)
16#include "components/gcm_driver/gcm_driver_android.h"
17#else
18#include "base/bind.h"
19#if defined(OS_CHROMEOS)
20#include "chrome/browser/services/gcm/chromeos_gcm_connection_observer.h"
21#endif
22#include "base/files/file_path.h"
23#include "base/memory/weak_ptr.h"
24#include "chrome/browser/services/gcm/gcm_account_tracker.h"
25#include "chrome/browser/services/gcm/gcm_desktop_utils.h"
26#include "chrome/browser/signin/profile_identity_provider.h"
27#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28#include "chrome/browser/signin/signin_manager_factory.h"
29#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
30#include "chrome/common/chrome_constants.h"
31#include "components/gcm_driver/gcm_client_factory.h"
32#include "components/gcm_driver/gcm_driver_desktop.h"
33#include "components/signin/core/browser/signin_manager.h"
34#include "google_apis/gaia/account_tracker.h"
35#include "google_apis/gaia/identity_provider.h"
36#include "net/url_request/url_request_context_getter.h"
37#endif
38
39namespace gcm {
40
41#if !defined(OS_ANDROID)
42// Identity observer only has actual work to do when the user is actually signed
43// in. It ensures that account tracker is taking
44class GCMProfileService::IdentityObserver : public IdentityProvider::Observer {
45 public:
46  IdentityObserver(Profile* profile, GCMDriverDesktop* driver);
47  virtual ~IdentityObserver();
48
49  // IdentityProvider::Observer:
50  virtual void OnActiveAccountLogin() OVERRIDE;
51  virtual void OnActiveAccountLogout() OVERRIDE;
52
53  std::string SignedInUserName() const;
54
55  // Called to inform IdentityObserver that a list of accounts was updated.
56  // |account_tokens| maps email addresses to OAuth2 access tokens.
57  void AccountsUpdated(
58      const std::map<std::string, std::string>& account_tokens);
59
60 private:
61  Profile* profile_;
62  GCMDriverDesktop* driver_;
63  scoped_ptr<IdentityProvider> identity_provider_;
64  scoped_ptr<GCMAccountTracker> gcm_account_tracker_;
65
66  // The account ID that this service is responsible for. Empty when the service
67  // is not running.
68  std::string account_id_;
69
70  base::WeakPtrFactory<GCMProfileService::IdentityObserver> weak_ptr_factory_;
71
72  DISALLOW_COPY_AND_ASSIGN(IdentityObserver);
73};
74
75GCMProfileService::IdentityObserver::IdentityObserver(Profile* profile,
76                                                      GCMDriverDesktop* driver)
77    : profile_(profile), driver_(driver), weak_ptr_factory_(this) {
78  identity_provider_.reset(new ProfileIdentityProvider(
79      SigninManagerFactory::GetForProfile(profile),
80      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
81      LoginUIServiceFactory::GetForProfile(profile)));
82  identity_provider_->AddObserver(this);
83
84  OnActiveAccountLogin();
85}
86
87GCMProfileService::IdentityObserver::~IdentityObserver() {
88  if (gcm_account_tracker_)
89    gcm_account_tracker_->Shutdown();
90  identity_provider_->RemoveObserver(this);
91}
92
93void GCMProfileService::IdentityObserver::OnActiveAccountLogin() {
94  // This might be called multiple times when the password changes.
95  const std::string account_id = identity_provider_->GetActiveAccountId();
96  if (account_id == account_id_)
97    return;
98  account_id_ = account_id;
99
100  driver_->OnSignedIn();
101
102  if (!gcm_account_tracker_) {
103    scoped_ptr<gaia::AccountTracker> gaia_account_tracker(
104        new gaia::AccountTracker(identity_provider_.get(),
105                                 profile_->GetRequestContext()));
106
107    gcm_account_tracker_.reset(new GCMAccountTracker(
108        gaia_account_tracker.Pass(),
109        base::Bind(&GCMProfileService::IdentityObserver::AccountsUpdated,
110                   weak_ptr_factory_.GetWeakPtr())));
111  }
112
113  gcm_account_tracker_->Start();
114}
115
116void GCMProfileService::IdentityObserver::OnActiveAccountLogout() {
117  account_id_.clear();
118
119  // Check is necessary to not crash browser_tests.
120  if (gcm_account_tracker_)
121    gcm_account_tracker_->Stop();
122  // When sign-in enforcement is not dropped, OnSignedOut will also clear all
123  // the GCM data and a new GCM ID will be retrieved after the user signs in
124  // again. Otherwise, the user sign-out will not affect the existing GCM
125  // data.
126  driver_->OnSignedOut();
127}
128
129std::string GCMProfileService::IdentityObserver::SignedInUserName() const {
130  return driver_->IsStarted() ? account_id_ : std::string();
131}
132
133void GCMProfileService::IdentityObserver::AccountsUpdated(
134    const std::map<std::string, std::string>& account_tokens) {
135  driver_->SetAccountsForCheckin(account_tokens);
136}
137#endif  // !defined(OS_ANDROID)
138
139// static
140bool GCMProfileService::IsGCMEnabled(Profile* profile) {
141  return profile->GetPrefs()->GetBoolean(prefs::kGCMChannelEnabled);
142}
143
144// static
145void GCMProfileService::RegisterProfilePrefs(
146    user_prefs::PrefRegistrySyncable* registry) {
147  registry->RegisterBooleanPref(
148      prefs::kGCMChannelEnabled,
149      true,
150      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
151  PushMessagingServiceImpl::RegisterProfilePrefs(registry);
152}
153
154#if defined(OS_ANDROID)
155GCMProfileService::GCMProfileService(Profile* profile)
156    : profile_(profile),
157      push_messaging_service_(this, profile) {
158  DCHECK(!profile->IsOffTheRecord());
159
160  driver_.reset(new GCMDriverAndroid);
161}
162#else
163GCMProfileService::GCMProfileService(
164    Profile* profile,
165    scoped_ptr<GCMClientFactory> gcm_client_factory)
166    : profile_(profile),
167      push_messaging_service_(this, profile) {
168  DCHECK(!profile->IsOffTheRecord());
169
170  driver_ = CreateGCMDriverDesktop(
171      gcm_client_factory.Pass(),
172      profile_->GetPrefs(),
173      profile_->GetPath().Append(chrome::kGCMStoreDirname),
174      profile_->GetRequestContext());
175
176#if defined(OS_CHROMEOS)
177  chromeos_connection_observer_.reset(new gcm::ChromeOSGCMConnectionObserver);
178  driver_->AddConnectionObserver(chromeos_connection_observer_.get());
179#endif
180
181  identity_observer_.reset(new IdentityObserver(
182      profile, static_cast<gcm::GCMDriverDesktop*>(driver_.get())));
183}
184#endif  // defined(OS_ANDROID)
185
186GCMProfileService::GCMProfileService()
187    : profile_(NULL),
188      push_messaging_service_(this, NULL) {
189}
190
191GCMProfileService::~GCMProfileService() {
192}
193
194void GCMProfileService::AddAppHandler(const std::string& app_id,
195                                      GCMAppHandler* handler) {
196  if (driver_)
197    driver_->AddAppHandler(app_id, handler);
198}
199
200void GCMProfileService::RemoveAppHandler(const std::string& app_id) {
201  if (driver_)
202    driver_->RemoveAppHandler(app_id);
203}
204
205void GCMProfileService::Register(const std::string& app_id,
206                                 const std::vector<std::string>& sender_ids,
207                                 const GCMDriver::RegisterCallback& callback) {
208  if (driver_)
209    driver_->Register(app_id, sender_ids, callback);
210}
211
212void GCMProfileService::Shutdown() {
213#if !defined(OS_ANDROID)
214  identity_observer_.reset();
215#endif  // !defined(OS_ANDROID)
216  if (driver_) {
217#if defined(OS_CHROMEOS)
218    driver_->RemoveConnectionObserver(chromeos_connection_observer_.get());
219    chromeos_connection_observer_.reset();
220#endif
221    driver_->Shutdown();
222    driver_.reset();
223  }
224}
225
226std::string GCMProfileService::SignedInUserName() const {
227#if defined(OS_ANDROID)
228  return std::string();
229#else
230  return identity_observer_ ? identity_observer_->SignedInUserName()
231                            : std::string();
232#endif  // defined(OS_ANDROID)
233}
234
235void GCMProfileService::SetDriverForTesting(GCMDriver* driver) {
236  driver_.reset(driver);
237}
238
239}  // namespace gcm
240