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/files/file_path.h"
10#include "base/files/file_util.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/profiles/profile_helper.h"
29#include "chrome/browser/profiles/profile_downloader.h"
30#include "chrome/browser/profiles/profile_manager.h"
31#include "chrome/common/chrome_paths.h"
32#include "chrome/grit/theme_resources.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_manager.h"
36#include "components/user_manager/user_type.h"
37#include "content/public/browser/browser_thread.h"
38#include "content/public/browser/notification_service.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_.get(),
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(
474    const std::string& user_id,
475    user_manager::UserManager* user_manager)
476    : UserImageManager(user_id),
477      user_manager_(user_manager),
478      downloading_profile_image_(false),
479      profile_image_requested_(false),
480      has_managed_image_(false),
481      user_needs_migration_(false),
482      weak_factory_(this) {
483  base::SequencedWorkerPool* blocking_pool =
484      content::BrowserThread::GetBlockingPool();
485  background_task_runner_ =
486      blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior(
487          blocking_pool->GetSequenceToken(),
488          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
489  image_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
490                                      background_task_runner_);
491  unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC,
492                                             background_task_runner_);
493}
494
495UserImageManagerImpl::~UserImageManagerImpl() {}
496
497void UserImageManagerImpl::LoadUserImage() {
498  PrefService* local_state = g_browser_process->local_state();
499  const base::DictionaryValue* prefs_images_unsafe =
500      local_state->GetDictionary(kUserImages);
501  const base::DictionaryValue* prefs_images =
502      local_state->GetDictionary(kUserImageProperties);
503  if (!prefs_images && !prefs_images_unsafe)
504    return;
505  user_manager::User* user = GetUserAndModify();
506  bool needs_migration = false;
507
508  // If entries are found in both |prefs_images_unsafe| and |prefs_images|,
509  // |prefs_images| is honored for now but |prefs_images_unsafe| will be
510  // migrated, overwriting the |prefs_images| entry, when the user logs in.
511  const base::DictionaryValue* image_properties = NULL;
512  if (prefs_images_unsafe) {
513    needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
514        user_id(), &image_properties);
515    if (needs_migration)
516      user_needs_migration_ = true;
517  }
518  if (prefs_images) {
519    prefs_images->GetDictionaryWithoutPathExpansion(user_id(),
520                                                    &image_properties);
521  }
522
523  // If the user image for |user_id| is managed by policy and the policy-set
524  // image is being loaded and persisted right now, let that job continue. It
525  // will update the user image when done.
526  if (IsUserImageManaged() && job_.get())
527    return;
528
529  if (!image_properties) {
530    SetInitialUserImage();
531    return;
532  }
533
534  int image_index = user_manager::User::USER_IMAGE_INVALID;
535  image_properties->GetInteger(kImageIndexNodeName, &image_index);
536  if (image_index >= 0 && image_index < user_manager::kDefaultImagesCount) {
537    user->SetImage(
538        user_manager::UserImage(user_manager::GetDefaultImage(image_index)),
539        image_index);
540    return;
541  }
542
543  if (image_index != user_manager::User::USER_IMAGE_EXTERNAL &&
544      image_index != user_manager::User::USER_IMAGE_PROFILE) {
545    NOTREACHED();
546    return;
547  }
548
549  std::string image_url_string;
550  image_properties->GetString(kImageURLNodeName, &image_url_string);
551  GURL image_url(image_url_string);
552  std::string image_path;
553  image_properties->GetString(kImagePathNodeName, &image_path);
554
555  user->SetImageURL(image_url);
556  user->SetStubImage(user_manager::UserImage(
557                         *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
558                             IDR_PROFILE_PICTURE_LOADING)),
559                     image_index,
560                     true);
561  DCHECK(!image_path.empty() ||
562         image_index == user_manager::User::USER_IMAGE_PROFILE);
563  if (image_path.empty() || needs_migration) {
564    // Return if either of the following is true:
565    // * The profile image is to be used but has not been downloaded yet. The
566    //   profile image will be downloaded after login.
567    // * The image needs migration. Migration will be performed after login.
568    return;
569  }
570
571  job_.reset(new Job(this));
572  job_->LoadImage(base::FilePath(image_path), image_index, image_url);
573}
574
575void UserImageManagerImpl::UserLoggedIn(bool user_is_new,
576                                        bool user_is_local) {
577  const user_manager::User* user = GetUser();
578  if (user_is_new) {
579    if (!user_is_local)
580      SetInitialUserImage();
581  } else {
582    UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn",
583                              ImageIndexToHistogramIndex(user->image_index()),
584                              user_manager::kHistogramImagesCount);
585
586    if (!IsUserImageManaged() && user_needs_migration_) {
587      const base::DictionaryValue* prefs_images_unsafe =
588          g_browser_process->local_state()->GetDictionary(kUserImages);
589      const base::DictionaryValue* image_properties = NULL;
590      if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
591              user_id(), &image_properties)) {
592        std::string image_path;
593        image_properties->GetString(kImagePathNodeName, &image_path);
594        job_.reset(new Job(this));
595        if (!image_path.empty()) {
596          VLOG(0) << "Loading old user image, then migrating it.";
597          job_->SetToPath(base::FilePath(image_path),
598                          user->image_index(),
599                          user->image_url(),
600                          false);
601        } else {
602          job_->SetToDefaultImage(user->image_index());
603        }
604      }
605    }
606  }
607
608  // Reset the downloaded profile image as a new user logged in.
609  downloaded_profile_image_ = gfx::ImageSkia();
610  profile_image_url_ = GURL();
611  profile_image_requested_ = false;
612
613  if (IsUserLoggedInAndRegular()) {
614    TryToInitDownloadedProfileImage();
615
616    // Schedule an initial download of the profile data (full name and
617    // optionally image).
618    profile_download_one_shot_timer_.Start(
619        FROM_HERE,
620        g_ignore_profile_data_download_delay_ ?
621            base::TimeDelta() :
622            base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec),
623        base::Bind(&UserImageManagerImpl::DownloadProfileData,
624                   base::Unretained(this),
625                   kProfileDownloadReasonLoggedIn));
626    // Schedule periodic refreshes of the profile data.
627    profile_download_periodic_timer_.Start(
628        FROM_HERE,
629        base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec),
630        base::Bind(&UserImageManagerImpl::DownloadProfileData,
631                   base::Unretained(this),
632                   kProfileDownloadReasonScheduled));
633  } else {
634    profile_download_one_shot_timer_.Stop();
635    profile_download_periodic_timer_.Stop();
636  }
637
638  user_image_sync_observer_.reset();
639  TryToCreateImageSyncObserver();
640}
641
642void UserImageManagerImpl::SaveUserDefaultImageIndex(int default_image_index) {
643  if (IsUserImageManaged())
644    return;
645  job_.reset(new Job(this));
646  job_->SetToDefaultImage(default_image_index);
647}
648
649void UserImageManagerImpl::SaveUserImage(
650    const user_manager::UserImage& user_image) {
651  if (IsUserImageManaged())
652    return;
653  job_.reset(new Job(this));
654  job_->SetToImage(user_manager::User::USER_IMAGE_EXTERNAL, user_image);
655}
656
657void UserImageManagerImpl::SaveUserImageFromFile(const base::FilePath& path) {
658  if (IsUserImageManaged())
659    return;
660  job_.reset(new Job(this));
661  job_->SetToPath(path, user_manager::User::USER_IMAGE_EXTERNAL, GURL(), true);
662}
663
664void UserImageManagerImpl::SaveUserImageFromProfileImage() {
665  if (IsUserImageManaged())
666    return;
667  // Use the profile image if it has been downloaded already. Otherwise, use a
668  // stub image (gray avatar).
669  job_.reset(new Job(this));
670  job_->SetToImage(user_manager::User::USER_IMAGE_PROFILE,
671                   downloaded_profile_image_.isNull()
672                       ? user_manager::UserImage()
673                       : user_manager::UserImage::CreateAndEncode(
674                             downloaded_profile_image_));
675  // If no profile image has been downloaded yet, ensure that a download is
676  // started.
677  if (downloaded_profile_image_.isNull())
678    DownloadProfileData(kProfileDownloadReasonProfileImageChosen);
679}
680
681void UserImageManagerImpl::DeleteUserImage() {
682  job_.reset();
683  DeleteUserImageAndLocalStateEntry(kUserImages);
684  DeleteUserImageAndLocalStateEntry(kUserImageProperties);
685}
686
687void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
688  profile_image_requested_ = true;
689  DownloadProfileData(reason);
690}
691
692const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
693  return downloaded_profile_image_;
694}
695
696UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
697  return user_image_sync_observer_.get();
698}
699
700void UserImageManagerImpl::Shutdown() {
701  profile_downloader_.reset();
702  user_image_sync_observer_.reset();
703}
704
705void UserImageManagerImpl::OnExternalDataSet(const std::string& policy) {
706  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
707  if (IsUserImageManaged())
708    return;
709
710  has_managed_image_ = true;
711  job_.reset();
712
713  const user_manager::User* user = GetUser();
714  // If the user image for the currently logged-in user became managed, stop the
715  // sync observer so that the policy-set image does not get synced out.
716  if (user && user->is_logged_in())
717    user_image_sync_observer_.reset();
718}
719
720void UserImageManagerImpl::OnExternalDataCleared(const std::string& policy) {
721  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
722  has_managed_image_ = false;
723  SetInitialUserImage();
724  TryToCreateImageSyncObserver();
725}
726
727void UserImageManagerImpl::OnExternalDataFetched(const std::string& policy,
728                                                 scoped_ptr<std::string> data) {
729  DCHECK_EQ(policy::key::kUserAvatarImage, policy);
730  DCHECK(IsUserImageManaged());
731  if (data) {
732    job_.reset(new Job(this));
733    job_->SetToImageData(data.Pass());
734  }
735}
736
737// static
738void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() {
739  g_ignore_profile_data_download_delay_ = true;
740}
741
742bool UserImageManagerImpl::NeedsProfilePicture() const {
743  return downloading_profile_image_;
744}
745
746int UserImageManagerImpl::GetDesiredImageSideLength() const {
747  return GetCurrentUserImageSize();
748}
749
750Profile* UserImageManagerImpl::GetBrowserProfile() {
751  return ProfileHelper::Get()->GetProfileByUserUnsafe(GetUser());
752}
753
754std::string UserImageManagerImpl::GetCachedPictureURL() const {
755  return profile_image_url_.spec();
756}
757
758void UserImageManagerImpl::OnProfileDownloadSuccess(
759    ProfileDownloader* downloader) {
760  // Ensure that the |profile_downloader_| is deleted when this method returns.
761  scoped_ptr<ProfileDownloader> profile_downloader(
762      profile_downloader_.release());
763  DCHECK_EQ(downloader, profile_downloader.get());
764
765  user_manager_->UpdateUserAccountData(
766      user_id(),
767      user_manager::UserManager::UserAccountData(
768          downloader->GetProfileFullName(),
769          downloader->GetProfileGivenName(),
770          downloader->GetProfileLocale()));
771  if (!downloading_profile_image_)
772    return;
773
774  ProfileDownloadResult result = kDownloadFailure;
775  switch (downloader->GetProfilePictureStatus()) {
776    case ProfileDownloader::PICTURE_SUCCESS:
777      result = kDownloadSuccess;
778      break;
779    case ProfileDownloader::PICTURE_CACHED:
780      result = kDownloadCached;
781      break;
782    case ProfileDownloader::PICTURE_DEFAULT:
783      result = kDownloadDefault;
784      break;
785    default:
786      NOTREACHED();
787  }
788
789  UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
790                            result,
791                            kDownloadResultsCount);
792  DCHECK(!profile_image_load_start_time_.is_null());
793  AddProfileImageTimeHistogram(
794      result,
795      profile_image_download_reason_,
796      base::TimeTicks::Now() - profile_image_load_start_time_);
797
798  // Ignore the image if it is no longer needed.
799  if (!NeedProfileImage())
800    return;
801
802  if (result == kDownloadDefault) {
803    content::NotificationService::current()->Notify(
804        chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
805        content::Source<UserImageManager>(this),
806        content::NotificationService::NoDetails());
807  } else {
808    profile_image_requested_ = false;
809  }
810
811  // Nothing to do if the picture is cached or is the default avatar.
812  if (result != kDownloadSuccess)
813    return;
814
815  downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap(
816      downloader->GetProfilePicture());
817  profile_image_url_ = GURL(downloader->GetProfilePictureURL());
818
819  const user_manager::User* user = GetUser();
820  if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE) {
821    VLOG(1) << "Updating profile image for logged-in user.";
822    UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
823                              kDownloadSuccessChanged,
824                              kDownloadResultsCount);
825    // This will persist |downloaded_profile_image_| to disk.
826    SaveUserImageFromProfileImage();
827  }
828
829  content::NotificationService::current()->Notify(
830      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
831      content::Source<UserImageManager>(this),
832      content::Details<const gfx::ImageSkia>(&downloaded_profile_image_));
833}
834
835void UserImageManagerImpl::OnProfileDownloadFailure(
836    ProfileDownloader* downloader,
837    ProfileDownloaderDelegate::FailureReason reason) {
838  DCHECK_EQ(downloader, profile_downloader_.get());
839  profile_downloader_.reset();
840
841  if (downloading_profile_image_) {
842    UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
843                              kDownloadFailure,
844                              kDownloadResultsCount);
845    DCHECK(!profile_image_load_start_time_.is_null());
846    AddProfileImageTimeHistogram(
847        kDownloadFailure,
848        profile_image_download_reason_,
849        base::TimeTicks::Now() - profile_image_load_start_time_);
850  }
851
852  if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) {
853    // Retry download after a delay if a network error occurred.
854    profile_download_one_shot_timer_.Start(
855        FROM_HERE,
856        base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec),
857        base::Bind(&UserImageManagerImpl::DownloadProfileData,
858                   base::Unretained(this),
859                   kProfileDownloadReasonRetry));
860  }
861
862  content::NotificationService::current()->Notify(
863      chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
864      content::Source<UserImageManager>(this),
865      content::NotificationService::NoDetails());
866}
867
868bool UserImageManagerImpl::IsUserImageManaged() const {
869  return has_managed_image_;
870}
871
872void UserImageManagerImpl::SetInitialUserImage() {
873  // Choose a random default image.
874  SaveUserDefaultImageIndex(
875      base::RandInt(user_manager::kFirstDefaultImageIndex,
876                    user_manager::kDefaultImagesCount - 1));
877}
878
879void UserImageManagerImpl::TryToInitDownloadedProfileImage() {
880  const user_manager::User* user = GetUser();
881  if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE &&
882      downloaded_profile_image_.isNull() && !user->image_is_stub()) {
883    // Initialize the |downloaded_profile_image_| for the currently logged-in
884    // user if it has not been initialized already, the user image is the
885    // profile image and the user image has been loaded successfully.
886    VLOG(1) << "Profile image initialized from disk.";
887    downloaded_profile_image_ = user->GetImage();
888    profile_image_url_ = user->image_url();
889  }
890}
891
892bool UserImageManagerImpl::NeedProfileImage() const {
893  const user_manager::User* user = GetUser();
894  return IsUserLoggedInAndRegular() &&
895         (user->image_index() == user_manager::User::USER_IMAGE_PROFILE ||
896          profile_image_requested_);
897}
898
899void UserImageManagerImpl::DownloadProfileData(const std::string& reason) {
900  // GAIA profiles exist for regular users only.
901  if (!IsUserLoggedInAndRegular())
902    return;
903
904  // If a download is already in progress, allow it to continue, with one
905  // exception: If the current download does not include the profile image but
906  // the image has since become necessary, start a new download that includes
907  // the profile image.
908  if (profile_downloader_ &&
909      (downloading_profile_image_ || !NeedProfileImage())) {
910    return;
911  }
912
913  downloading_profile_image_ = NeedProfileImage();
914  profile_image_download_reason_ = reason;
915  profile_image_load_start_time_ = base::TimeTicks::Now();
916  profile_downloader_.reset(new ProfileDownloader(this));
917  profile_downloader_->Start();
918}
919
920void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry(
921    const char* prefs_dict_root) {
922  DictionaryPrefUpdate update(g_browser_process->local_state(),
923                              prefs_dict_root);
924  const base::DictionaryValue* image_properties;
925  if (!update->GetDictionaryWithoutPathExpansion(user_id(), &image_properties))
926    return;
927
928  std::string image_path;
929  image_properties->GetString(kImagePathNodeName, &image_path);
930  if (!image_path.empty()) {
931    background_task_runner_->PostTask(
932        FROM_HERE,
933        base::Bind(base::IgnoreResult(&base::DeleteFile),
934                   base::FilePath(image_path),
935                   false));
936  }
937  update->RemoveWithoutPathExpansion(user_id(), NULL);
938}
939
940void UserImageManagerImpl::OnJobChangedUserImage() {
941  if (GetUser()->is_logged_in())
942    TryToInitDownloadedProfileImage();
943
944  content::NotificationService::current()->Notify(
945      chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
946      content::Source<UserImageManagerImpl>(this),
947      content::Details<const user_manager::User>(GetUser()));
948}
949
950void UserImageManagerImpl::OnJobDone() {
951  if (job_.get())
952    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, job_.release());
953  else
954    NOTREACHED();
955
956  if (!user_needs_migration_)
957    return;
958  // Migration completed for |user_id|.
959  user_needs_migration_ = false;
960
961  const base::DictionaryValue* prefs_images_unsafe =
962      g_browser_process->local_state()->GetDictionary(kUserImages);
963  const base::DictionaryValue* image_properties = NULL;
964  if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
965          user_id(), &image_properties)) {
966    NOTREACHED();
967    return;
968  }
969
970  int image_index = user_manager::User::USER_IMAGE_INVALID;
971  image_properties->GetInteger(kImageIndexNodeName, &image_index);
972  UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
973                            ImageIndexToHistogramIndex(image_index),
974                            user_manager::kHistogramImagesCount);
975
976  std::string image_path;
977  image_properties->GetString(kImagePathNodeName, &image_path);
978  if (!image_path.empty()) {
979    // If an old image exists, delete it and remove |user_id| from the old prefs
980    // dictionary only after the deletion has completed. This ensures that no
981    // orphaned image is left behind if the browser crashes before the deletion
982    // has been performed: In that case, local state will be unchanged and the
983    // migration will be run again on the user's next login.
984    background_task_runner_->PostTaskAndReply(
985        FROM_HERE,
986        base::Bind(base::IgnoreResult(&base::DeleteFile),
987                   base::FilePath(image_path),
988                   false),
989        base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration,
990                   weak_factory_.GetWeakPtr()));
991  } else {
992    // If no old image exists, remove |user_id| from the old prefs dictionary.
993    UpdateLocalStateAfterMigration();
994  }
995}
996
997void UserImageManagerImpl::UpdateLocalStateAfterMigration() {
998  DictionaryPrefUpdate update(g_browser_process->local_state(),
999                              kUserImages);
1000  update->RemoveWithoutPathExpansion(user_id(), NULL);
1001}
1002
1003void UserImageManagerImpl::TryToCreateImageSyncObserver() {
1004  const user_manager::User* user = GetUser();
1005  // If the currently logged-in user's user image is managed, the sync observer
1006  // must not be started so that the policy-set image does not get synced out.
1007  if (!user_image_sync_observer_ &&
1008      user && user->CanSyncImage() &&
1009      !IsUserImageManaged()) {
1010    user_image_sync_observer_.reset(new UserImageSyncObserver(user));
1011  }
1012}
1013
1014const user_manager::User* UserImageManagerImpl::GetUser() const {
1015  return user_manager_->FindUser(user_id());
1016}
1017
1018user_manager::User* UserImageManagerImpl::GetUserAndModify() const {
1019  return user_manager_->FindUserAndModify(user_id());
1020}
1021
1022bool UserImageManagerImpl::IsUserLoggedInAndRegular() const {
1023  const user_manager::User* user = GetUser();
1024  if (!user)
1025    return false;
1026  return user->is_logged_in() &&
1027         user->GetType() == user_manager::USER_TYPE_REGULAR;
1028}
1029
1030}  // namespace chromeos
1031