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