user_image_manager_impl.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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/chromeos/login/users/avatar/user_image_manager_impl.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/file_util.h"
10#include "base/files/file_path.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/metrics/histogram.h"
14#include "base/path_service.h"
15#include "base/prefs/pref_registry_simple.h"
16#include "base/prefs/pref_service.h"
17#include "base/prefs/scoped_user_pref_update.h"
18#include "base/rand_util.h"
19#include "base/sequenced_task_runner.h"
20#include "base/task_runner_util.h"
21#include "base/threading/sequenced_worker_pool.h"
22#include "base/time/time.h"
23#include "base/values.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/chrome_notification_types.h"
26#include "chrome/browser/chromeos/login/helper.h"
27#include "chrome/browser/chromeos/login/users/avatar/user_image_sync_observer.h"
28#include "chrome/browser/chromeos/login/users/user_manager.h"
29#include "chrome/browser/chromeos/profiles/profile_helper.h"
30#include "chrome/browser/profiles/profile_downloader.h"
31#include "chrome/browser/profiles/profile_manager.h"
32#include "chrome/common/chrome_paths.h"
33#include "components/user_manager/user_image/default_user_images.h"
34#include "components/user_manager/user_image/user_image.h"
35#include "components/user_manager/user_type.h"
36#include "content/public/browser/browser_thread.h"
37#include "content/public/browser/notification_service.h"
38#include "grit/theme_resources.h"
39#include "policy/policy_constants.h"
40#include "ui/base/resource/resource_bundle.h"
41#include "ui/gfx/image/image_skia.h"
42
43namespace chromeos {
44
45namespace {
46
47// A dictionary that maps user_ids to old user image data with images stored in
48// PNG format. Deprecated.
49// TODO(ivankr): remove this const char after migration is gone.
50const char kUserImages[] = "UserImages";
51
52// A dictionary that maps user_ids to user image data with images stored in
53// JPEG format.
54const char kUserImageProperties[] = "user_image_info";
55
56// Names of user image properties.
57const char kImagePathNodeName[] = "path";
58const char kImageIndexNodeName[] = "index";
59const char kImageURLNodeName[] = "url";
60
61// Delay betweeen user login and attempt to update user's profile data.
62const int kProfileDataDownloadDelaySec = 10;
63
64// Interval betweeen retries to update user's profile data.
65const int kProfileDataDownloadRetryIntervalSec = 300;
66
67// Delay betweeen subsequent profile refresh attempts (24 hrs).
68const int kProfileRefreshIntervalSec = 24 * 3600;
69
70const char kSafeImagePathExtension[] = ".jpg";
71
72// Enum for reporting histograms about profile picture download.
73enum ProfileDownloadResult {
74  kDownloadSuccessChanged,
75  kDownloadSuccess,
76  kDownloadFailure,
77  kDownloadDefault,
78  kDownloadCached,
79
80  // Must be the last, convenient count.
81  kDownloadResultsCount
82};
83
84// Time histogram prefix for a cached profile image download.
85const char kProfileDownloadCachedTime[] =
86    "UserImage.ProfileDownloadTime.Cached";
87// Time histogram prefix for the default profile image download.
88const char kProfileDownloadDefaultTime[] =
89    "UserImage.ProfileDownloadTime.Default";
90// Time histogram prefix for a failed profile image download.
91const char kProfileDownloadFailureTime[] =
92    "UserImage.ProfileDownloadTime.Failure";
93// Time histogram prefix for a successful profile image download.
94const char kProfileDownloadSuccessTime[] =
95    "UserImage.ProfileDownloadTime.Success";
96// Time histogram suffix for a profile image download after login.
97const char kProfileDownloadReasonLoggedIn[] = "LoggedIn";
98// Time histogram suffix for a profile image download when the user chooses the
99// profile image but it has not been downloaded yet.
100const char kProfileDownloadReasonProfileImageChosen[] = "ProfileImageChosen";
101// Time histogram suffix for a scheduled profile image download.
102const char kProfileDownloadReasonScheduled[] = "Scheduled";
103// Time histogram suffix for a profile image download retry.
104const char kProfileDownloadReasonRetry[] = "Retry";
105
106static bool g_ignore_profile_data_download_delay_ = false;
107
108// Add a histogram showing the time it takes to download profile image.
109// Separate histograms are reported for each download |reason| and |result|.
110void AddProfileImageTimeHistogram(ProfileDownloadResult result,
111                                  const std::string& download_reason,
112                                  const base::TimeDelta& time_delta) {
113  std::string histogram_name;
114  switch (result) {
115    case kDownloadFailure:
116      histogram_name = kProfileDownloadFailureTime;
117      break;
118    case kDownloadDefault:
119      histogram_name = kProfileDownloadDefaultTime;
120      break;
121    case kDownloadSuccess:
122      histogram_name = kProfileDownloadSuccessTime;
123      break;
124    case kDownloadCached:
125      histogram_name = kProfileDownloadCachedTime;
126      break;
127    default:
128      NOTREACHED();
129  }
130  if (!download_reason.empty()) {
131    histogram_name += ".";
132    histogram_name += download_reason;
133  }
134
135  static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1);
136  static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50);
137  const size_t bucket_count(50);
138
139  base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
140      histogram_name, min_time, max_time, bucket_count,
141      base::HistogramBase::kUmaTargetedHistogramFlag);
142  counter->AddTime(time_delta);
143
144  DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF();
145}
146
147// Converts |image_index| to UMA histogram value.
148int ImageIndexToHistogramIndex(int image_index) {
149  switch (image_index) {
150    case user_manager::User::USER_IMAGE_EXTERNAL:
151      // TODO(ivankr): Distinguish this from selected from file.
152      return user_manager::kHistogramImageFromCamera;
153    case user_manager::User::USER_IMAGE_PROFILE:
154      return user_manager::kHistogramImageFromProfile;
155    default:
156      return image_index;
157  }
158}
159
160bool SaveImage(const user_manager::UserImage& user_image,
161               const base::FilePath& image_path) {
162  user_manager::UserImage safe_image;
163  const user_manager::UserImage::RawImage* encoded_image = NULL;
164  if (!user_image.is_safe_format()) {
165    safe_image = user_manager::UserImage::CreateAndEncode(user_image.image());
166    encoded_image = &safe_image.raw_image();
167    UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size());
168  } else if (user_image.has_raw_image()) {
169    encoded_image = &user_image.raw_image();
170  } else {
171    NOTREACHED() << "Raw image missing.";
172    return false;
173  }
174
175  if (!encoded_image->size() ||
176      base::WriteFile(image_path,
177                      reinterpret_cast<const char*>(&(*encoded_image)[0]),
178                      encoded_image->size()) == -1) {
179    LOG(ERROR) << "Failed to save image to file.";
180    return false;
181  }
182
183  return true;
184}
185
186}  // namespace
187
188// static
189void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) {
190  registry->RegisterDictionaryPref(kUserImages);
191  registry->RegisterDictionaryPref(kUserImageProperties);
192}
193
194// Every image load or update is encapsulated by a Job. The Job is allowed to
195// perform tasks on background threads or in helper processes but:
196// * Changes to User objects and local state as well as any calls to the
197//   |parent_| must be performed on the thread that the Job is created on only.
198// * File writes and deletions must be performed via the |parent_|'s
199//   |background_task_runner_| only.
200//
201// Only one of the Load*() and Set*() methods may be called per Job.
202class UserImageManagerImpl::Job {
203 public:
204  // The |Job| will update the user object corresponding to |parent|.
205  explicit Job(UserImageManagerImpl* parent);
206  ~Job();
207
208  // Loads the image at |image_path| or one of the default images,
209  // depending on |image_index|, and updates the user object with the
210  // new image.
211  void LoadImage(base::FilePath image_path,
212                 const int image_index,
213                 const GURL& image_url);
214
215  // Sets the user image in local state to the default image indicated
216  // by |default_image_index|. Also updates the user object with the
217  // new image.
218  void SetToDefaultImage(int default_image_index);
219
220  // Saves the |user_image| to disk and sets the user image in local
221  // state to that image. Also updates the user with the new image.
222  void SetToImage(int image_index, const user_manager::UserImage& user_image);
223
224  // Decodes the JPEG image |data|, crops and resizes the image, saves
225  // it to disk and sets the user image in local state to that image.
226  // Also updates the user object with the new image.
227  void SetToImageData(scoped_ptr<std::string> data);
228
229  // Loads the image at |path|, transcodes it to JPEG format, saves
230  // the image to disk and sets the user image in local state to that
231  // image.  If |resize| is true, the image is cropped and resized
232  // before transcoding.  Also updates the user object with the new
233  // image.
234  void SetToPath(const base::FilePath& path,
235                 int image_index,
236                 const GURL& image_url,
237                 bool resize);
238
239 private:
240  // Called back after an image has been loaded from disk.
241  void OnLoadImageDone(bool save, const user_manager::UserImage& user_image);
242
243  // Updates the user object with |user_image_|.
244  void UpdateUser();
245
246  // Saves |user_image_| to disk in JPEG format. Local state will be updated
247  // when a callback indicates that the image has been saved.
248  void SaveImageAndUpdateLocalState();
249
250  // Called back after the |user_image_| has been saved to
251  // disk. Updates the user image information in local state. The
252  // information is only updated if |success| is true (indicating that
253  // the image was saved successfully) or the user image is the
254  // profile image (indicating that even if the image could not be
255  // saved because it is not available right now, it will be
256  // downloaded eventually).
257  void OnSaveImageDone(bool success);
258
259  // Updates the user image in local state, setting it to one of the
260  // default images or the saved |user_image_|, depending on
261  // |image_index_|.
262  void UpdateLocalState();
263
264  // Notifies the |parent_| that the Job is done.
265  void NotifyJobDone();
266
267  const std::string& user_id() const { return parent_->user_id(); }
268
269  UserImageManagerImpl* parent_;
270
271  // Whether one of the Load*() or Set*() methods has been run already.
272  bool run_;
273
274  int image_index_;
275  GURL image_url_;
276  base::FilePath image_path_;
277
278  user_manager::UserImage user_image_;
279
280  base::WeakPtrFactory<Job> weak_factory_;
281
282  DISALLOW_COPY_AND_ASSIGN(Job);
283};
284
285UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent)
286    : parent_(parent),
287      run_(false),
288      weak_factory_(this) {
289}
290
291UserImageManagerImpl::Job::~Job() {
292}
293
294void UserImageManagerImpl::Job::LoadImage(base::FilePath image_path,
295                                          const int image_index,
296                                          const GURL& image_url) {
297  DCHECK(!run_);
298  run_ = true;
299
300  image_index_ = image_index;
301  image_url_ = image_url;
302  image_path_ = image_path;
303
304  if (image_index_ >= 0 && image_index_ < user_manager::kDefaultImagesCount) {
305    // Load one of the default images. This happens synchronously.
306    user_image_ =
307        user_manager::UserImage(user_manager::GetDefaultImage(image_index_));
308    UpdateUser();
309    NotifyJobDone();
310  } else if (image_index_ == user_manager::User::USER_IMAGE_EXTERNAL ||
311             image_index_ == user_manager::User::USER_IMAGE_PROFILE) {
312    // Load the user image from a file referenced by |image_path|. This happens
313    // asynchronously. The JPEG image loader can be used here because
314    // LoadImage() is called only for users whose user image has previously
315    // been set by one of the Set*() methods, which transcode to JPEG format.
316    DCHECK(!image_path_.empty());
317    parent_->image_loader_->Start(image_path_.value(),
318                                  0,
319                                  base::Bind(&Job::OnLoadImageDone,
320                                             weak_factory_.GetWeakPtr(),
321                                             false));
322  } else {
323    NOTREACHED();
324    NotifyJobDone();
325  }
326}
327
328void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index) {
329  DCHECK(!run_);
330  run_ = true;
331
332  DCHECK_LE(0, default_image_index);
333  DCHECK_GT(user_manager::kDefaultImagesCount, default_image_index);
334
335  image_index_ = default_image_index;
336  user_image_ =
337      user_manager::UserImage(user_manager::GetDefaultImage(image_index_));
338
339  UpdateUser();
340  UpdateLocalState();
341  NotifyJobDone();
342}
343
344void UserImageManagerImpl::Job::SetToImage(
345    int image_index,
346    const user_manager::UserImage& user_image) {
347  DCHECK(!run_);
348  run_ = true;
349
350  DCHECK(image_index == user_manager::User::USER_IMAGE_EXTERNAL ||
351         image_index == user_manager::User::USER_IMAGE_PROFILE);
352
353  image_index_ = image_index;
354  user_image_ = user_image;
355
356  UpdateUser();
357  SaveImageAndUpdateLocalState();
358}
359
360void UserImageManagerImpl::Job::SetToImageData(scoped_ptr<std::string> data) {
361  DCHECK(!run_);
362  run_ = true;
363
364  image_index_ = user_manager::User::USER_IMAGE_EXTERNAL;
365
366  // This method uses the image_loader_, not the unsafe_image_loader_:
367  // * This is necessary because the method is used to update the user image
368  //   whenever the policy for a user is set. In the case of device-local
369  //   accounts, policy may change at any time, even if the user is not
370  //   currently logged in (and thus, the unsafe_image_loader_ may not be used).
371  // * This is possible because only JPEG |data| is accepted. No support for
372  //   other image file formats is needed.
373  // * This is safe because the image_loader_ employs a hardened JPEG decoder
374  //   that protects against malicious invalid image data being used to attack
375  //   the login screen or another user session currently in progress.
376  parent_->image_loader_->Start(data.Pass(),
377                                login::kMaxUserImageSize,
378                                base::Bind(&Job::OnLoadImageDone,
379                                           weak_factory_.GetWeakPtr(),
380                                           true));
381}
382
383void UserImageManagerImpl::Job::SetToPath(const base::FilePath& path,
384                                          int image_index,
385                                          const GURL& image_url,
386                                          bool resize) {
387  DCHECK(!run_);
388  run_ = true;
389
390  image_index_ = image_index;
391  image_url_ = image_url;
392
393  DCHECK(!path.empty());
394  parent_->unsafe_image_loader_->Start(path.value(),
395                                       resize ? login::kMaxUserImageSize : 0,
396                                       base::Bind(&Job::OnLoadImageDone,
397                                                  weak_factory_.GetWeakPtr(),
398                                                  true));
399}
400
401void UserImageManagerImpl::Job::OnLoadImageDone(
402    bool save,
403    const user_manager::UserImage& user_image) {
404  user_image_ = user_image;
405  UpdateUser();
406  if (save)
407    SaveImageAndUpdateLocalState();
408  else
409    NotifyJobDone();
410}
411
412void UserImageManagerImpl::Job::UpdateUser() {
413  user_manager::User* user =
414      parent_->user_manager_->FindUserAndModify(user_id());
415  if (!user)
416    return;
417
418  if (!user_image_.image().isNull()) {
419    user->SetImage(user_image_, image_index_);
420  } else {
421    user->SetStubImage(
422        user_manager::UserImage(
423            *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
424                IDR_PROFILE_PICTURE_LOADING)),
425        image_index_,
426        false);
427  }
428  user->SetImageURL(image_url_);
429
430  parent_->OnJobChangedUserImage();
431}
432
433void UserImageManagerImpl::Job::SaveImageAndUpdateLocalState() {
434  base::FilePath user_data_dir;
435  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
436  image_path_ = user_data_dir.Append(user_id() + kSafeImagePathExtension);
437
438  base::PostTaskAndReplyWithResult(
439      parent_->background_task_runner_,
440      FROM_HERE,
441      base::Bind(&SaveImage, user_image_, image_path_),
442      base::Bind(&Job::OnSaveImageDone, weak_factory_.GetWeakPtr()));
443}
444
445void UserImageManagerImpl::Job::OnSaveImageDone(bool success) {
446  if (success || image_index_ == user_manager::User::USER_IMAGE_PROFILE)
447    UpdateLocalState();
448  NotifyJobDone();
449}
450
451void UserImageManagerImpl::Job::UpdateLocalState() {
452  // Ignore if data stored or cached outside the user's cryptohome is to be
453  // treated as ephemeral.
454  if (parent_->user_manager_->IsUserNonCryptohomeDataEphemeral(user_id()))
455    return;
456
457  scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
458  entry->Set(kImagePathNodeName, new base::StringValue(image_path_.value()));
459  entry->Set(kImageIndexNodeName, new base::FundamentalValue(image_index_));
460  if (!image_url_.is_empty())
461    entry->Set(kImageURLNodeName, new base::StringValue(image_url_.spec()));
462  DictionaryPrefUpdate update(g_browser_process->local_state(),
463                              kUserImageProperties);
464  update->SetWithoutPathExpansion(user_id(), entry.release());
465
466  parent_->user_manager_->NotifyLocalStateChanged();
467}
468
469void UserImageManagerImpl::Job::NotifyJobDone() {
470  parent_->OnJobDone();
471}
472
473UserImageManagerImpl::UserImageManagerImpl(const std::string& user_id,
474                                           UserManager* user_manager)
475    : UserImageManager(user_id),
476      user_manager_(user_manager),
477      downloading_profile_image_(false),
478      profile_image_requested_(false),
479      has_managed_image_(false),
480      user_needs_migration_(false),
481      weak_factory_(this) {
482  base::SequencedWorkerPool* blocking_pool =
483      content::BrowserThread::GetBlockingPool();
484  background_task_runner_ =
485      blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior(
486          blocking_pool->GetSequenceToken(),
487          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
488  image_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
489                                      background_task_runner_);
490  unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC,
491                                             background_task_runner_);
492}
493
494UserImageManagerImpl::~UserImageManagerImpl() {}
495
496void UserImageManagerImpl::LoadUserImage() {
497  PrefService* local_state = g_browser_process->local_state();
498  const base::DictionaryValue* prefs_images_unsafe =
499      local_state->GetDictionary(kUserImages);
500  const base::DictionaryValue* prefs_images =
501      local_state->GetDictionary(kUserImageProperties);
502  if (!prefs_images && !prefs_images_unsafe)
503    return;
504  user_manager::User* user = GetUserAndModify();
505  bool needs_migration = false;
506
507  // If entries are found in both |prefs_images_unsafe| and |prefs_images|,
508  // |prefs_images| is honored for now but |prefs_images_unsafe| will be
509  // migrated, overwriting the |prefs_images| entry, when the user logs in.
510  const base::DictionaryValue* image_properties = NULL;
511  if (prefs_images_unsafe) {
512    needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
513        user_id(), &image_properties);
514    if (needs_migration)
515      user_needs_migration_ = true;
516  }
517  if (prefs_images) {
518    prefs_images->GetDictionaryWithoutPathExpansion(user_id(),
519                                                    &image_properties);
520  }
521
522  // If the user image for |user_id| is managed by policy and the policy-set
523  // image is being loaded and persisted right now, let that job continue. It
524  // will update the user image when done.
525  if (IsUserImageManaged() && job_.get())
526    return;
527
528  if (!image_properties) {
529    SetInitialUserImage();
530    return;
531  }
532
533  int image_index = user_manager::User::USER_IMAGE_INVALID;
534  image_properties->GetInteger(kImageIndexNodeName, &image_index);
535  if (image_index >= 0 && image_index < user_manager::kDefaultImagesCount) {
536    user->SetImage(
537        user_manager::UserImage(user_manager::GetDefaultImage(image_index)),
538        image_index);
539    return;
540  }
541
542  if (image_index != user_manager::User::USER_IMAGE_EXTERNAL &&
543      image_index != user_manager::User::USER_IMAGE_PROFILE) {
544    NOTREACHED();
545    return;
546  }
547
548  std::string image_url_string;
549  image_properties->GetString(kImageURLNodeName, &image_url_string);
550  GURL image_url(image_url_string);
551  std::string image_path;
552  image_properties->GetString(kImagePathNodeName, &image_path);
553
554  user->SetImageURL(image_url);
555  user->SetStubImage(user_manager::UserImage(
556                         *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
557                             IDR_PROFILE_PICTURE_LOADING)),
558                     image_index,
559                     true);
560  DCHECK(!image_path.empty() ||
561         image_index == user_manager::User::USER_IMAGE_PROFILE);
562  if (image_path.empty() || needs_migration) {
563    // Return if either of the following is true:
564    // * The profile image is to be used but has not been downloaded yet. The
565    //   profile image will be downloaded after login.
566    // * The image needs migration. Migration will be performed after login.
567    return;
568  }
569
570  job_.reset(new Job(this));
571  job_->LoadImage(base::FilePath(image_path), image_index, image_url);
572}
573
574void UserImageManagerImpl::UserLoggedIn(bool user_is_new,
575                                        bool user_is_local) {
576  const user_manager::User* user = GetUser();
577  if (user_is_new) {
578    if (!user_is_local)
579      SetInitialUserImage();
580  } else {
581    UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn",
582                              ImageIndexToHistogramIndex(user->image_index()),
583                              user_manager::kHistogramImagesCount);
584
585    if (!IsUserImageManaged() && user_needs_migration_) {
586      const base::DictionaryValue* prefs_images_unsafe =
587          g_browser_process->local_state()->GetDictionary(kUserImages);
588      const base::DictionaryValue* image_properties = NULL;
589      if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
590              user_id(), &image_properties)) {
591        std::string image_path;
592        image_properties->GetString(kImagePathNodeName, &image_path);
593        job_.reset(new Job(this));
594        if (!image_path.empty()) {
595          VLOG(0) << "Loading old user image, then migrating it.";
596          job_->SetToPath(base::FilePath(image_path),
597                          user->image_index(),
598                          user->image_url(),
599                          false);
600        } else {
601          job_->SetToDefaultImage(user->image_index());
602        }
603      }
604    }
605  }
606
607  // Reset the downloaded profile image as a new user logged in.
608  downloaded_profile_image_ = gfx::ImageSkia();
609  profile_image_url_ = GURL();
610  profile_image_requested_ = false;
611
612  if (IsUserLoggedInAndRegular()) {
613    TryToInitDownloadedProfileImage();
614
615    // Schedule an initial download of the profile data (full name and
616    // optionally image).
617    profile_download_one_shot_timer_.Start(
618        FROM_HERE,
619        g_ignore_profile_data_download_delay_ ?
620            base::TimeDelta() :
621            base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec),
622        base::Bind(&UserImageManagerImpl::DownloadProfileData,
623                   base::Unretained(this),
624                   kProfileDownloadReasonLoggedIn));
625    // Schedule periodic refreshes of the profile data.
626    profile_download_periodic_timer_.Start(
627        FROM_HERE,
628        base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec),
629        base::Bind(&UserImageManagerImpl::DownloadProfileData,
630                   base::Unretained(this),
631                   kProfileDownloadReasonScheduled));
632  } else {
633    profile_download_one_shot_timer_.Stop();
634    profile_download_periodic_timer_.Stop();
635  }
636
637  user_image_sync_observer_.reset();
638  TryToCreateImageSyncObserver();
639}
640
641void UserImageManagerImpl::SaveUserDefaultImageIndex(int default_image_index) {
642  if (IsUserImageManaged())
643    return;
644  job_.reset(new Job(this));
645  job_->SetToDefaultImage(default_image_index);
646}
647
648void UserImageManagerImpl::SaveUserImage(
649    const user_manager::UserImage& user_image) {
650  if (IsUserImageManaged())
651    return;
652  job_.reset(new Job(this));
653  job_->SetToImage(user_manager::User::USER_IMAGE_EXTERNAL, user_image);
654}
655
656void UserImageManagerImpl::SaveUserImageFromFile(const base::FilePath& path) {
657  if (IsUserImageManaged())
658    return;
659  job_.reset(new Job(this));
660  job_->SetToPath(path, user_manager::User::USER_IMAGE_EXTERNAL, GURL(), true);
661}
662
663void UserImageManagerImpl::SaveUserImageFromProfileImage() {
664  if (IsUserImageManaged())
665    return;
666  // Use the profile image if it has been downloaded already. Otherwise, use a
667  // stub image (gray avatar).
668  job_.reset(new Job(this));
669  job_->SetToImage(user_manager::User::USER_IMAGE_PROFILE,
670                   downloaded_profile_image_.isNull()
671                       ? user_manager::UserImage()
672                       : user_manager::UserImage::CreateAndEncode(
673                             downloaded_profile_image_));
674  // If no profile image has been downloaded yet, ensure that a download is
675  // started.
676  if (downloaded_profile_image_.isNull())
677    DownloadProfileData(kProfileDownloadReasonProfileImageChosen);
678}
679
680void UserImageManagerImpl::DeleteUserImage() {
681  job_.reset();
682  DeleteUserImageAndLocalStateEntry(kUserImages);
683  DeleteUserImageAndLocalStateEntry(kUserImageProperties);
684}
685
686void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
687  profile_image_requested_ = true;
688  DownloadProfileData(reason);
689}
690
691const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
692  return downloaded_profile_image_;
693}
694
695UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
696  return user_image_sync_observer_.get();
697}
698
699void UserImageManagerImpl::Shutdown() {
700  profile_downloader_.reset();
701  user_image_sync_observer_.reset();
702}
703
704void UserImageManagerImpl::OnExternalDataSet(const std::string& policy) {
705  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
706  if (IsUserImageManaged())
707    return;
708
709  has_managed_image_ = true;
710  job_.reset();
711
712  const user_manager::User* user = GetUser();
713  // If the user image for the currently logged-in user became managed, stop the
714  // sync observer so that the policy-set image does not get synced out.
715  if (user && user->is_logged_in())
716    user_image_sync_observer_.reset();
717}
718
719void UserImageManagerImpl::OnExternalDataCleared(const std::string& policy) {
720  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
721  has_managed_image_ = false;
722  SetInitialUserImage();
723  TryToCreateImageSyncObserver();
724}
725
726void UserImageManagerImpl::OnExternalDataFetched(const std::string& policy,
727                                                 scoped_ptr<std::string> data) {
728  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
729  DCHECK(IsUserImageManaged());
730  if (data) {
731    job_.reset(new Job(this));
732    job_->SetToImageData(data.Pass());
733  }
734}
735
736// static
737void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() {
738  g_ignore_profile_data_download_delay_ = true;
739}
740
741bool UserImageManagerImpl::NeedsProfilePicture() const {
742  return downloading_profile_image_;
743}
744
745int UserImageManagerImpl::GetDesiredImageSideLength() const {
746  return GetCurrentUserImageSize();
747}
748
749Profile* UserImageManagerImpl::GetBrowserProfile() {
750  return ProfileHelper::Get()->GetProfileByUser(GetUser());
751}
752
753std::string UserImageManagerImpl::GetCachedPictureURL() const {
754  return profile_image_url_.spec();
755}
756
757void UserImageManagerImpl::OnProfileDownloadSuccess(
758    ProfileDownloader* downloader) {
759  // Ensure that the |profile_downloader_| is deleted when this method returns.
760  scoped_ptr<ProfileDownloader> profile_downloader(
761      profile_downloader_.release());
762  DCHECK_EQ(downloader, profile_downloader.get());
763
764  user_manager_->UpdateUserAccountData(
765      user_id(),
766      UserManager::UserAccountData(downloader->GetProfileFullName(),
767                                   downloader->GetProfileGivenName(),
768                                   downloader->GetProfileLocale()));
769  if (!downloading_profile_image_)
770    return;
771
772  ProfileDownloadResult result = kDownloadFailure;
773  switch (downloader->GetProfilePictureStatus()) {
774    case ProfileDownloader::PICTURE_SUCCESS:
775      result = kDownloadSuccess;
776      break;
777    case ProfileDownloader::PICTURE_CACHED:
778      result = kDownloadCached;
779      break;
780    case ProfileDownloader::PICTURE_DEFAULT:
781      result = kDownloadDefault;
782      break;
783    default:
784      NOTREACHED();
785  }
786
787  UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
788                            result,
789                            kDownloadResultsCount);
790  DCHECK(!profile_image_load_start_time_.is_null());
791  AddProfileImageTimeHistogram(
792      result,
793      profile_image_download_reason_,
794      base::TimeTicks::Now() - profile_image_load_start_time_);
795
796  // Ignore the image if it is no longer needed.
797  if (!NeedProfileImage())
798    return;
799
800  if (result == kDownloadDefault) {
801    content::NotificationService::current()->Notify(
802        chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
803        content::Source<UserImageManager>(this),
804        content::NotificationService::NoDetails());
805  } else {
806    profile_image_requested_ = false;
807  }
808
809  // Nothing to do if the picture is cached or is the default avatar.
810  if (result != kDownloadSuccess)
811    return;
812
813  downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap(
814      downloader->GetProfilePicture());
815  profile_image_url_ = GURL(downloader->GetProfilePictureURL());
816
817  const user_manager::User* user = GetUser();
818  if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE) {
819    VLOG(1) << "Updating profile image for logged-in user.";
820    UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
821                              kDownloadSuccessChanged,
822                              kDownloadResultsCount);
823    // This will persist |downloaded_profile_image_| to disk.
824    SaveUserImageFromProfileImage();
825  }
826
827  content::NotificationService::current()->Notify(
828      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
829      content::Source<UserImageManager>(this),
830      content::Details<const gfx::ImageSkia>(&downloaded_profile_image_));
831}
832
833void UserImageManagerImpl::OnProfileDownloadFailure(
834    ProfileDownloader* downloader,
835    ProfileDownloaderDelegate::FailureReason reason) {
836  DCHECK_EQ(downloader, profile_downloader_.get());
837  profile_downloader_.reset();
838
839  if (downloading_profile_image_) {
840    UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
841                              kDownloadFailure,
842                              kDownloadResultsCount);
843    DCHECK(!profile_image_load_start_time_.is_null());
844    AddProfileImageTimeHistogram(
845        kDownloadFailure,
846        profile_image_download_reason_,
847        base::TimeTicks::Now() - profile_image_load_start_time_);
848  }
849
850  if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) {
851    // Retry download after a delay if a network error occurred.
852    profile_download_one_shot_timer_.Start(
853        FROM_HERE,
854        base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec),
855        base::Bind(&UserImageManagerImpl::DownloadProfileData,
856                   base::Unretained(this),
857                   kProfileDownloadReasonRetry));
858  }
859
860  content::NotificationService::current()->Notify(
861      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
862      content::Source<UserImageManager>(this),
863      content::NotificationService::NoDetails());
864}
865
866bool UserImageManagerImpl::IsUserImageManaged() const {
867  return has_managed_image_;
868}
869
870void UserImageManagerImpl::SetInitialUserImage() {
871  // Choose a random default image.
872  SaveUserDefaultImageIndex(
873      base::RandInt(user_manager::kFirstDefaultImageIndex,
874                    user_manager::kDefaultImagesCount - 1));
875}
876
877void UserImageManagerImpl::TryToInitDownloadedProfileImage() {
878  const user_manager::User* user = GetUser();
879  if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE &&
880      downloaded_profile_image_.isNull() && !user->image_is_stub()) {
881    // Initialize the |downloaded_profile_image_| for the currently logged-in
882    // user if it has not been initialized already, the user image is the
883    // profile image and the user image has been loaded successfully.
884    VLOG(1) << "Profile image initialized from disk.";
885    downloaded_profile_image_ = user->GetImage();
886    profile_image_url_ = user->image_url();
887  }
888}
889
890bool UserImageManagerImpl::NeedProfileImage() const {
891  const user_manager::User* user = GetUser();
892  return IsUserLoggedInAndRegular() &&
893         (user->image_index() == user_manager::User::USER_IMAGE_PROFILE ||
894          profile_image_requested_);
895}
896
897void UserImageManagerImpl::DownloadProfileData(const std::string& reason) {
898  // GAIA profiles exist for regular users only.
899  if (!IsUserLoggedInAndRegular())
900    return;
901
902  // If a download is already in progress, allow it to continue, with one
903  // exception: If the current download does not include the profile image but
904  // the image has since become necessary, start a new download that includes
905  // the profile image.
906  if (profile_downloader_ &&
907      (downloading_profile_image_ || !NeedProfileImage())) {
908    return;
909  }
910
911  downloading_profile_image_ = NeedProfileImage();
912  profile_image_download_reason_ = reason;
913  profile_image_load_start_time_ = base::TimeTicks::Now();
914  profile_downloader_.reset(new ProfileDownloader(this));
915  profile_downloader_->Start();
916}
917
918void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry(
919    const char* prefs_dict_root) {
920  DictionaryPrefUpdate update(g_browser_process->local_state(),
921                              prefs_dict_root);
922  const base::DictionaryValue* image_properties;
923  if (!update->GetDictionaryWithoutPathExpansion(user_id(), &image_properties))
924    return;
925
926  std::string image_path;
927  image_properties->GetString(kImagePathNodeName, &image_path);
928  if (!image_path.empty()) {
929    background_task_runner_->PostTask(
930        FROM_HERE,
931        base::Bind(base::IgnoreResult(&base::DeleteFile),
932                   base::FilePath(image_path),
933                   false));
934  }
935  update->RemoveWithoutPathExpansion(user_id(), NULL);
936}
937
938void UserImageManagerImpl::OnJobChangedUserImage() {
939  if (GetUser()->is_logged_in())
940    TryToInitDownloadedProfileImage();
941
942  content::NotificationService::current()->Notify(
943      chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
944      content::Source<UserImageManagerImpl>(this),
945      content::Details<const user_manager::User>(GetUser()));
946}
947
948void UserImageManagerImpl::OnJobDone() {
949  if (job_.get())
950    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, job_.release());
951  else
952    NOTREACHED();
953
954  if (!user_needs_migration_)
955    return;
956  // Migration completed for |user_id|.
957  user_needs_migration_ = false;
958
959  const base::DictionaryValue* prefs_images_unsafe =
960      g_browser_process->local_state()->GetDictionary(kUserImages);
961  const base::DictionaryValue* image_properties = NULL;
962  if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
963          user_id(), &image_properties)) {
964    NOTREACHED();
965    return;
966  }
967
968  int image_index = user_manager::User::USER_IMAGE_INVALID;
969  image_properties->GetInteger(kImageIndexNodeName, &image_index);
970  UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
971                            ImageIndexToHistogramIndex(image_index),
972                            user_manager::kHistogramImagesCount);
973
974  std::string image_path;
975  image_properties->GetString(kImagePathNodeName, &image_path);
976  if (!image_path.empty()) {
977    // If an old image exists, delete it and remove |user_id| from the old prefs
978    // dictionary only after the deletion has completed. This ensures that no
979    // orphaned image is left behind if the browser crashes before the deletion
980    // has been performed: In that case, local state will be unchanged and the
981    // migration will be run again on the user's next login.
982    background_task_runner_->PostTaskAndReply(
983        FROM_HERE,
984        base::Bind(base::IgnoreResult(&base::DeleteFile),
985                   base::FilePath(image_path),
986                   false),
987        base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration,
988                   weak_factory_.GetWeakPtr()));
989  } else {
990    // If no old image exists, remove |user_id| from the old prefs dictionary.
991    UpdateLocalStateAfterMigration();
992  }
993}
994
995void UserImageManagerImpl::UpdateLocalStateAfterMigration() {
996  DictionaryPrefUpdate update(g_browser_process->local_state(),
997                              kUserImages);
998  update->RemoveWithoutPathExpansion(user_id(), NULL);
999}
1000
1001void UserImageManagerImpl::TryToCreateImageSyncObserver() {
1002  const user_manager::User* user = GetUser();
1003  // If the currently logged-in user's user image is managed, the sync observer
1004  // must not be started so that the policy-set image does not get synced out.
1005  if (!user_image_sync_observer_ &&
1006      user && user->CanSyncImage() &&
1007      !IsUserImageManaged()) {
1008    user_image_sync_observer_.reset(new UserImageSyncObserver(user));
1009  }
1010}
1011
1012const user_manager::User* UserImageManagerImpl::GetUser() const {
1013  return user_manager_->FindUser(user_id());
1014}
1015
1016user_manager::User* UserImageManagerImpl::GetUserAndModify() const {
1017  return user_manager_->FindUserAndModify(user_id());
1018}
1019
1020bool UserImageManagerImpl::IsUserLoggedInAndRegular() const {
1021  const user_manager::User* user = GetUser();
1022  if (!user)
1023    return false;
1024  return user->is_logged_in() &&
1025         user->GetType() == user_manager::USER_TYPE_REGULAR;
1026}
1027
1028}  // namespace chromeos
1029