• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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|.
AddProfileImageTimeHistogram(ProfileDownloadResult result,const std::string & download_reason,const base::TimeDelta & time_delta)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.
ImageIndexToHistogramIndex(int image_index)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 
SaveImage(const UserImage & user_image,const base::FilePath & image_path)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
RegisterPrefs(PrefRegistrySimple * registry)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 
user_id() const263   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 
Job(UserImageManagerImpl * parent)281 UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent)
282     : parent_(parent),
283       run_(false),
284       weak_factory_(this) {
285 }
286 
~Job()287 UserImageManagerImpl::Job::~Job() {
288 }
289 
LoadImage(base::FilePath image_path,const int image_index,const GURL & image_url)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 
SetToDefaultImage(int default_image_index)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 
SetToImage(int image_index,const UserImage & user_image)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 
SetToImageData(scoped_ptr<std::string> data)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 
SetToPath(const base::FilePath & path,int image_index,const GURL & image_url,bool resize)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 
OnLoadImageDone(bool save,const UserImage & user_image)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 
UpdateUser()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 
SaveImageAndUpdateLocalState()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 
OnSaveImageDone(bool success)430 void UserImageManagerImpl::Job::OnSaveImageDone(bool success) {
431   if (success || image_index_ == User::kProfileImageIndex)
432     UpdateLocalState();
433   NotifyJobDone();
434 }
435 
UpdateLocalState()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 
NotifyJobDone()454 void UserImageManagerImpl::Job::NotifyJobDone() {
455   parent_->OnJobDone();
456 }
457 
UserImageManagerImpl(const std::string & user_id,UserManager * user_manager)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 
~UserImageManagerImpl()479 UserImageManagerImpl::~UserImageManagerImpl() {}
480 
LoadUserImage()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 
UserLoggedIn(bool user_is_new,bool user_is_local)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 
SaveUserDefaultImageIndex(int default_image_index)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 
SaveUserImage(const UserImage & user_image)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 
SaveUserImageFromFile(const base::FilePath & path)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 
SaveUserImageFromProfileImage()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 
DeleteUserImage()657 void UserImageManagerImpl::DeleteUserImage() {
658   job_.reset();
659   DeleteUserImageAndLocalStateEntry(kUserImages);
660   DeleteUserImageAndLocalStateEntry(kUserImageProperties);
661 }
662 
DownloadProfileImage(const std::string & reason)663 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
664   profile_image_requested_ = true;
665   DownloadProfileData(reason);
666 }
667 
DownloadedProfileImage() const668 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
669   return downloaded_profile_image_;
670 }
671 
GetSyncObserver() const672 UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
673   return user_image_sync_observer_.get();
674 }
675 
Shutdown()676 void UserImageManagerImpl::Shutdown() {
677   profile_downloader_.reset();
678   user_image_sync_observer_.reset();
679 }
680 
OnExternalDataSet(const std::string & policy)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 
OnExternalDataCleared(const std::string & policy)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 
OnExternalDataFetched(const std::string & policy,scoped_ptr<std::string> data)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
IgnoreProfileDataDownloadDelayForTesting()714 void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() {
715   g_ignore_profile_data_download_delay_ = true;
716 }
717 
NeedsProfilePicture() const718 bool UserImageManagerImpl::NeedsProfilePicture() const {
719   return downloading_profile_image_;
720 }
721 
GetDesiredImageSideLength() const722 int UserImageManagerImpl::GetDesiredImageSideLength() const {
723   return GetCurrentUserImageSize();
724 }
725 
GetBrowserProfile()726 Profile* UserImageManagerImpl::GetBrowserProfile() {
727   return user_manager_->GetProfileByUser(GetUser());
728 }
729 
GetCachedPictureURL() const730 std::string UserImageManagerImpl::GetCachedPictureURL() const {
731   return profile_image_url_.spec();
732 }
733 
OnProfileDownloadSuccess(ProfileDownloader * downloader)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 
OnProfileDownloadFailure(ProfileDownloader * downloader,ProfileDownloaderDelegate::FailureReason reason)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 
IsUserImageManaged() const843 bool UserImageManagerImpl::IsUserImageManaged() const {
844   return has_managed_image_;
845 }
846 
SetInitialUserImage()847 void UserImageManagerImpl::SetInitialUserImage() {
848   // Choose a random default image.
849   SaveUserDefaultImageIndex(base::RandInt(kFirstDefaultImageIndex,
850                                           kDefaultImagesCount - 1));
851 }
852 
TryToInitDownloadedProfileImage()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 
NeedProfileImage() const867 bool UserImageManagerImpl::NeedProfileImage() const {
868   const User* user = GetUser();
869   return IsUserLoggedInAndRegular() &&
870       (user->image_index() == User::kProfileImageIndex ||
871        profile_image_requested_);
872 }
873 
DownloadProfileData(const std::string & reason)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 
DeleteUserImageAndLocalStateEntry(const char * prefs_dict_root)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 
OnJobChangedUserImage()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 
OnJobDone()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 
UpdateLocalStateAfterMigration()972 void UserImageManagerImpl::UpdateLocalStateAfterMigration() {
973   DictionaryPrefUpdate update(g_browser_process->local_state(),
974                               kUserImages);
975   update->RemoveWithoutPathExpansion(user_id(), NULL);
976 }
977 
TryToCreateImageSyncObserver()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 
GetUser() const989 const User* UserImageManagerImpl::GetUser() const {
990   return user_manager_->FindUser(user_id());
991 }
992 
GetUserAndModify() const993 User* UserImageManagerImpl::GetUserAndModify() const {
994   return user_manager_->FindUserAndModify(user_id());
995 }
996 
IsUserLoggedInAndRegular() const997 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