1// Copyright (c) 2012 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/profiles/gaia_info_update_service.h"
6
7#include "base/prefs/pref_service.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/profiles/profile_info_cache.h"
12#include "chrome/browser/profiles/profile_manager.h"
13#include "chrome/browser/signin/signin_manager_factory.h"
14#include "chrome/browser/sync/profile_sync_service.h"
15#include "chrome/common/pref_names.h"
16#include "components/signin/core/common/profile_management_switches.h"
17#include "content/public/browser/notification_details.h"
18#include "third_party/skia/include/core/SkBitmap.h"
19#include "ui/gfx/image/image.h"
20
21namespace {
22
23// Update the user's GAIA info every 24 hours.
24const int kUpdateIntervalHours = 24;
25
26// If the users's GAIA info is very out of date then wait at least this long
27// before starting an update. This avoids slowdown during startup.
28const int kMinUpdateIntervalSeconds = 5;
29
30}  // namespace
31
32GAIAInfoUpdateService::GAIAInfoUpdateService(Profile* profile)
33    : profile_(profile) {
34  SigninManagerBase* signin_manager =
35      SigninManagerFactory::GetForProfile(profile_);
36  signin_manager->AddObserver(this);
37
38  PrefService* prefs = profile_->GetPrefs();
39  last_updated_ = base::Time::FromInternalValue(
40      prefs->GetInt64(prefs::kProfileGAIAInfoUpdateTime));
41  ScheduleNextUpdate();
42}
43
44GAIAInfoUpdateService::~GAIAInfoUpdateService() {
45  DCHECK(!profile_) << "Shutdown not called before dtor";
46}
47
48void GAIAInfoUpdateService::Update() {
49  // The user must be logged in.
50  SigninManagerBase* signin_manager =
51      SigninManagerFactory::GetForProfile(profile_);
52  if (!signin_manager->IsAuthenticated())
53    return;
54
55  if (profile_image_downloader_)
56    return;
57  profile_image_downloader_.reset(new ProfileDownloader(this));
58  profile_image_downloader_->Start();
59}
60
61// static
62bool GAIAInfoUpdateService::ShouldUseGAIAProfileInfo(Profile* profile) {
63#if defined(OS_CHROMEOS)
64  return false;
65#endif
66
67  // Sync must be allowed.
68  if (!profile->GetOriginalProfile()->IsSyncAccessible())
69    return false;
70
71  // To enable this feature for testing pass "--google-profile-info".
72  if (switches::IsGoogleProfileInfo())
73    return true;
74
75  // This feature is disable by default.
76  return false;
77}
78
79bool GAIAInfoUpdateService::NeedsProfilePicture() const {
80  return true;
81}
82
83int GAIAInfoUpdateService::GetDesiredImageSideLength() const {
84  return 256;
85}
86
87Profile* GAIAInfoUpdateService::GetBrowserProfile() {
88  return profile_;
89}
90
91std::string GAIAInfoUpdateService::GetCachedPictureURL() const {
92  return profile_->GetPrefs()->GetString(prefs::kProfileGAIAInfoPictureURL);
93}
94
95void GAIAInfoUpdateService::OnProfileDownloadSuccess(
96    ProfileDownloader* downloader) {
97  // Make sure that |ProfileDownloader| gets deleted after return.
98  scoped_ptr<ProfileDownloader> profile_image_downloader(
99      profile_image_downloader_.release());
100
101  // Save the last updated time.
102  last_updated_ = base::Time::Now();
103  profile_->GetPrefs()->SetInt64(prefs::kProfileGAIAInfoUpdateTime,
104                                 last_updated_.ToInternalValue());
105  ScheduleNextUpdate();
106
107  base::string16 full_name = downloader->GetProfileFullName();
108  base::string16 given_name = downloader->GetProfileGivenName();
109  SkBitmap bitmap = downloader->GetProfilePicture();
110  ProfileDownloader::PictureStatus picture_status =
111      downloader->GetProfilePictureStatus();
112  std::string picture_url = downloader->GetProfilePictureURL();
113
114  ProfileInfoCache& cache =
115      g_browser_process->profile_manager()->GetProfileInfoCache();
116  size_t profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
117  if (profile_index == std::string::npos)
118    return;
119
120  cache.SetGAIANameOfProfileAtIndex(profile_index, full_name);
121  // The profile index may have changed.
122  profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
123  DCHECK_NE(profile_index, std::string::npos);
124
125  cache.SetGAIAGivenNameOfProfileAtIndex(profile_index, given_name);
126  // The profile index may have changed.
127  profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
128  DCHECK_NE(profile_index, std::string::npos);
129
130  if (picture_status == ProfileDownloader::PICTURE_SUCCESS) {
131    profile_->GetPrefs()->SetString(prefs::kProfileGAIAInfoPictureURL,
132                                    picture_url);
133    gfx::Image gfx_image = gfx::Image::CreateFrom1xBitmap(bitmap);
134    cache.SetGAIAPictureOfProfileAtIndex(profile_index, &gfx_image);
135  } else if (picture_status == ProfileDownloader::PICTURE_DEFAULT) {
136    cache.SetGAIAPictureOfProfileAtIndex(profile_index, NULL);
137  }
138}
139
140void GAIAInfoUpdateService::OnProfileDownloadFailure(
141    ProfileDownloader* downloader,
142    ProfileDownloaderDelegate::FailureReason reason) {
143  profile_image_downloader_.reset();
144
145  // Save the last updated time.
146  last_updated_ = base::Time::Now();
147  profile_->GetPrefs()->SetInt64(prefs::kProfileGAIAInfoUpdateTime,
148                                 last_updated_.ToInternalValue());
149  ScheduleNextUpdate();
150}
151
152void GAIAInfoUpdateService::OnUsernameChanged(const std::string& username) {
153  ProfileInfoCache& cache =
154      g_browser_process->profile_manager()->GetProfileInfoCache();
155  size_t profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
156  if (profile_index == std::string::npos)
157    return;
158
159  if (username.empty()) {
160    // Unset the old user's GAIA info.
161    cache.SetGAIANameOfProfileAtIndex(profile_index, base::string16());
162    cache.SetGAIAGivenNameOfProfileAtIndex(profile_index, base::string16());
163    // The profile index may have changed.
164    profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
165    if (profile_index == std::string::npos)
166      return;
167    cache.SetGAIAPictureOfProfileAtIndex(profile_index, NULL);
168    // Unset the cached URL.
169    profile_->GetPrefs()->ClearPref(prefs::kProfileGAIAInfoPictureURL);
170  } else {
171    // Update the new user's GAIA info.
172    Update();
173  }
174}
175
176void GAIAInfoUpdateService::Shutdown() {
177  timer_.Stop();
178  profile_image_downloader_.reset();
179  SigninManagerBase* signin_manager =
180      SigninManagerFactory::GetForProfile(profile_);
181  signin_manager->RemoveObserver(this);
182
183  // OK to reset |profile_| pointer here because GAIAInfoUpdateService will not
184  // access it again.  This pointer is also used to implement the delegate for
185  // |profile_image_downloader_|.  However that object was destroyed above.
186  profile_ = NULL;
187}
188
189void GAIAInfoUpdateService::ScheduleNextUpdate() {
190  if (timer_.IsRunning())
191    return;
192
193  const base::TimeDelta desired_delta =
194      base::TimeDelta::FromHours(kUpdateIntervalHours);
195  const base::TimeDelta update_delta = base::Time::Now() - last_updated_;
196
197  base::TimeDelta delta;
198  if (update_delta < base::TimeDelta() || update_delta > desired_delta)
199    delta = base::TimeDelta::FromSeconds(kMinUpdateIntervalSeconds);
200  else
201    delta = desired_delta - update_delta;
202
203  timer_.Start(FROM_HERE, delta, this, &GAIAInfoUpdateService::Update);
204}
205
206void GAIAInfoUpdateService::GoogleSigninSucceeded(
207    const std::string& account_id,
208    const std::string& username,
209    const std::string& password) {
210  OnUsernameChanged(username);
211}
212
213void GAIAInfoUpdateService::GoogleSignedOut(const std::string& account_id,
214                                            const std::string& username) {
215  OnUsernameChanged(std::string());
216}
217