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