Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / user_image_manager_impl.cc
blob5ead90da988dd2ba95170665ed5b67d77b642c47
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.
5 #include "chrome/browser/chromeos/login/user_image_manager_impl.h"
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/default_user_images.h"
27 #include "chrome/browser/chromeos/login/helper.h"
28 #include "chrome/browser/chromeos/login/user_image.h"
29 #include "chrome/browser/chromeos/login/user_image_sync_observer.h"
30 #include "chrome/browser/chromeos/login/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"
40 namespace chromeos {
42 namespace {
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";
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";
53 // Names of user image properties.
54 const char kImagePathNodeName[] = "path";
55 const char kImageIndexNodeName[] = "index";
56 const char kImageURLNodeName[] = "url";
58 // Delay betweeen user login and attempt to update user's profile data.
59 const int kProfileDataDownloadDelaySec = 10;
61 // Interval betweeen retries to update user's profile data.
62 const int kProfileDataDownloadRetryIntervalSec = 300;
64 // Delay betweeen subsequent profile refresh attempts (24 hrs).
65 const int kProfileRefreshIntervalSec = 24 * 3600;
67 const char kSafeImagePathExtension[] = ".jpg";
69 // Enum for reporting histograms about profile picture download.
70 enum ProfileDownloadResult {
71 kDownloadSuccessChanged,
72 kDownloadSuccess,
73 kDownloadFailure,
74 kDownloadDefault,
75 kDownloadCached,
77 // Must be the last, convenient count.
78 kDownloadResultsCount
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";
103 static bool g_ignore_profile_data_download_delay_ = false;
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();
127 if (!download_reason.empty()) {
128 histogram_name += ".";
129 histogram_name += download_reason;
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);
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);
141 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF();
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;
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;
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;
179 return true;
182 } // namespace
184 // static
185 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) {
186 registry->RegisterDictionaryPref(kUserImages);
187 registry->RegisterDictionaryPref(kUserImageProperties);
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.
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();
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);
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);
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);
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);
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);
235 private:
236 // Called back after an image has been loaded from disk.
237 void OnLoadImageDone(bool save, const UserImage& user_image);
239 // Updates the user object with |user_image_|.
240 void UpdateUser();
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();
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);
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();
260 // Notifies the |parent_| that the Job is done.
261 void NotifyJobDone();
263 const std::string& user_id() const { return parent_->user_id(); }
265 UserImageManagerImpl* parent_;
267 // Whether one of the Load*() or Set*() methods has been run already.
268 bool run_;
270 int image_index_;
271 GURL image_url_;
272 base::FilePath image_path_;
274 UserImage user_image_;
276 base::WeakPtrFactory<Job> weak_factory_;
278 DISALLOW_COPY_AND_ASSIGN(Job);
281 UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent)
282 : parent_(parent),
283 run_(false),
284 weak_factory_(this) {
287 UserImageManagerImpl::Job::~Job() {
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;
296 image_index_ = image_index;
297 image_url_ = image_url;
298 image_path_ = image_path;
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(),
314 base::Bind(&Job::OnLoadImageDone,
315 weak_factory_.GetWeakPtr(),
316 false));
317 } else {
318 NOTREACHED();
319 NotifyJobDone();
323 void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index) {
324 DCHECK(!run_);
325 run_ = true;
327 DCHECK_LE(0, default_image_index);
328 DCHECK_GT(kDefaultImagesCount, default_image_index);
330 image_index_ = default_image_index;
331 user_image_ = UserImage(GetDefaultImage(image_index_));
333 UpdateUser();
334 UpdateLocalState();
335 NotifyJobDone();
338 void UserImageManagerImpl::Job::SetToImage(int image_index,
339 const UserImage& user_image) {
340 DCHECK(!run_);
341 run_ = true;
343 DCHECK(image_index == User::kExternalImageIndex ||
344 image_index == User::kProfileImageIndex);
346 image_index_ = image_index;
347 user_image_ = user_image;
349 UpdateUser();
350 SaveImageAndUpdateLocalState();
353 void UserImageManagerImpl::Job::SetToImageData(scoped_ptr<std::string> data) {
354 DCHECK(!run_);
355 run_ = true;
357 image_index_ = User::kExternalImageIndex;
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));
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;
383 image_index_ = image_index;
384 image_url_ = image_url;
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));
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();
404 void UserImageManagerImpl::Job::UpdateUser() {
405 User* user = parent_->user_manager_->FindUserAndModify(user_id());
406 if (!user)
407 return;
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_);
415 parent_->OnJobChangedUserImage();
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);
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()));
430 void UserImageManagerImpl::Job::OnSaveImageDone(bool success) {
431 if (success || image_index_ == User::kProfileImageIndex)
432 UpdateLocalState();
433 NotifyJobDone();
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;
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());
451 parent_->user_manager_->NotifyLocalStateChanged();
454 void UserImageManagerImpl::Job::NotifyJobDone() {
455 parent_->OnJobDone();
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_);
479 UserImageManagerImpl::~UserImageManagerImpl() {}
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;
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;
502 if (prefs_images) {
503 prefs_images->GetDictionaryWithoutPathExpansion(user_id(),
504 &image_properties);
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;
513 if (!image_properties) {
514 SetInitialUserImage();
515 return;
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;
526 if (image_index != User::kExternalImageIndex &&
527 image_index != User::kProfileImageIndex) {
528 NOTREACHED();
529 return;
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);
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;
549 job_.reset(new Job(this));
550 job_->LoadImage(base::FilePath(image_path), image_index, image_url);
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);
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());
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;
591 if (IsUserLoggedInAndRegular()) {
592 TryToInitDownloadedProfileImage();
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();
616 user_image_sync_observer_.reset();
617 TryToCreateImageSyncObserver();
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);
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);
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);
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);
657 void UserImageManagerImpl::DeleteUserImage() {
658 job_.reset();
659 DeleteUserImageAndLocalStateEntry(kUserImages);
660 DeleteUserImageAndLocalStateEntry(kUserImageProperties);
663 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) {
664 profile_image_requested_ = true;
665 DownloadProfileData(reason);
668 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const {
669 return downloaded_profile_image_;
672 UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const {
673 return user_image_sync_observer_.get();
676 void UserImageManagerImpl::Shutdown() {
677 profile_downloader_.reset();
678 user_image_sync_observer_.reset();
681 void UserImageManagerImpl::OnExternalDataSet(const std::string& policy) {
682 DCHECK_EQ(policy::key::kUserAvatarImage, policy);
683 if (IsUserImageManaged())
684 return;
686 has_managed_image_ = true;
687 job_.reset();
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();
696 void UserImageManagerImpl::OnExternalDataCleared(const std::string& policy) {
697 DCHECK_EQ(policy::key::kUserAvatarImage, policy);
698 has_managed_image_ = false;
699 TryToCreateImageSyncObserver();
702 void UserImageManagerImpl::OnExternalDataFetched(const std::string& policy,
703 scoped_ptr<std::string> data) {
704 DCHECK_EQ(policy::key::kUserAvatarImage, policy);
705 DCHECK(IsUserImageManaged());
706 if (data) {
707 job_.reset(new Job(this));
708 job_->SetToImageData(data.Pass());
712 // static
713 void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() {
714 g_ignore_profile_data_download_delay_ = true;
717 bool UserImageManagerImpl::NeedsProfilePicture() const {
718 return downloading_profile_image_;
721 int UserImageManagerImpl::GetDesiredImageSideLength() const {
722 return GetCurrentUserImageSize();
725 Profile* UserImageManagerImpl::GetBrowserProfile() {
726 return user_manager_->GetProfileByUser(GetUser());
729 std::string UserImageManagerImpl::GetCachedPictureURL() const {
730 return profile_image_url_.spec();
733 void UserImageManagerImpl::OnProfileDownloadSuccess(
734 ProfileDownloader* downloader) {
735 // Ensure that the |profile_downloader_| is deleted when this method returns.
736 scoped_ptr<ProfileDownloader> profile_downloader(
737 profile_downloader_.release());
738 DCHECK_EQ(downloader, profile_downloader.get());
740 user_manager_->UpdateUserAccountData(
741 user_id(),
742 UserManager::UserAccountData(downloader->GetProfileFullName(),
743 downloader->GetProfileGivenName(),
744 downloader->GetProfileLocale()));
745 if (!downloading_profile_image_)
746 return;
748 ProfileDownloadResult result = kDownloadFailure;
749 switch (downloader->GetProfilePictureStatus()) {
750 case ProfileDownloader::PICTURE_SUCCESS:
751 result = kDownloadSuccess;
752 break;
753 case ProfileDownloader::PICTURE_CACHED:
754 result = kDownloadCached;
755 break;
756 case ProfileDownloader::PICTURE_DEFAULT:
757 result = kDownloadDefault;
758 break;
759 default:
760 NOTREACHED();
763 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
764 result,
765 kDownloadResultsCount);
766 DCHECK(!profile_image_load_start_time_.is_null());
767 AddProfileImageTimeHistogram(
768 result,
769 profile_image_download_reason_,
770 base::TimeTicks::Now() - profile_image_load_start_time_);
772 // Ignore the image if it is no longer needed.
773 if (!NeedProfileImage())
774 return;
776 if (result == kDownloadDefault) {
777 content::NotificationService::current()->Notify(
778 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
779 content::Source<UserImageManager>(this),
780 content::NotificationService::NoDetails());
781 } else {
782 profile_image_requested_ = false;
785 // Nothing to do if the picture is cached or is the default avatar.
786 if (result != kDownloadSuccess)
787 return;
789 downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap(
790 downloader->GetProfilePicture());
791 profile_image_url_ = GURL(downloader->GetProfilePictureURL());
793 const User* user = GetUser();
794 if (user->image_index() == User::kProfileImageIndex) {
795 VLOG(1) << "Updating profile image for logged-in user.";
796 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
797 kDownloadSuccessChanged,
798 kDownloadResultsCount);
799 // This will persist |downloaded_profile_image_| to disk.
800 SaveUserImageFromProfileImage();
803 content::NotificationService::current()->Notify(
804 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
805 content::Source<UserImageManager>(this),
806 content::Details<const gfx::ImageSkia>(&downloaded_profile_image_));
809 void UserImageManagerImpl::OnProfileDownloadFailure(
810 ProfileDownloader* downloader,
811 ProfileDownloaderDelegate::FailureReason reason) {
812 DCHECK_EQ(downloader, profile_downloader_.get());
813 profile_downloader_.reset();
815 if (downloading_profile_image_) {
816 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
817 kDownloadFailure,
818 kDownloadResultsCount);
819 DCHECK(!profile_image_load_start_time_.is_null());
820 AddProfileImageTimeHistogram(
821 kDownloadFailure,
822 profile_image_download_reason_,
823 base::TimeTicks::Now() - profile_image_load_start_time_);
826 if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) {
827 // Retry download after a delay if a network error occurred.
828 profile_download_one_shot_timer_.Start(
829 FROM_HERE,
830 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec),
831 base::Bind(&UserImageManagerImpl::DownloadProfileData,
832 base::Unretained(this),
833 kProfileDownloadReasonRetry));
836 content::NotificationService::current()->Notify(
837 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
838 content::Source<UserImageManager>(this),
839 content::NotificationService::NoDetails());
842 bool UserImageManagerImpl::IsUserImageManaged() const {
843 return has_managed_image_;
846 void UserImageManagerImpl::SetInitialUserImage() {
847 // Choose a random default image.
848 SaveUserDefaultImageIndex(base::RandInt(kFirstDefaultImageIndex,
849 kDefaultImagesCount - 1));
852 void UserImageManagerImpl::TryToInitDownloadedProfileImage() {
853 const User* user = GetUser();
854 if (user->image_index() == User::kProfileImageIndex &&
855 downloaded_profile_image_.isNull() &&
856 !user->image_is_stub()) {
857 // Initialize the |downloaded_profile_image_| for the currently logged-in
858 // user if it has not been initialized already, the user image is the
859 // profile image and the user image has been loaded successfully.
860 VLOG(1) << "Profile image initialized from disk.";
861 downloaded_profile_image_ = user->GetImage();
862 profile_image_url_ = user->image_url();
866 bool UserImageManagerImpl::NeedProfileImage() const {
867 const User* user = GetUser();
868 return IsUserLoggedInAndRegular() &&
869 (user->image_index() == User::kProfileImageIndex ||
870 profile_image_requested_);
873 void UserImageManagerImpl::DownloadProfileData(const std::string& reason) {
874 // GAIA profiles exist for regular users only.
875 if (!IsUserLoggedInAndRegular())
876 return;
878 // If a download is already in progress, allow it to continue, with one
879 // exception: If the current download does not include the profile image but
880 // the image has since become necessary, start a new download that includes
881 // the profile image.
882 if (profile_downloader_ &&
883 (downloading_profile_image_ || !NeedProfileImage())) {
884 return;
887 downloading_profile_image_ = NeedProfileImage();
888 profile_image_download_reason_ = reason;
889 profile_image_load_start_time_ = base::TimeTicks::Now();
890 profile_downloader_.reset(new ProfileDownloader(this));
891 profile_downloader_->Start();
894 void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry(
895 const char* prefs_dict_root) {
896 DictionaryPrefUpdate update(g_browser_process->local_state(),
897 prefs_dict_root);
898 const base::DictionaryValue* image_properties;
899 if (!update->GetDictionaryWithoutPathExpansion(user_id(), &image_properties))
900 return;
902 std::string image_path;
903 image_properties->GetString(kImagePathNodeName, &image_path);
904 if (!image_path.empty()) {
905 background_task_runner_->PostTask(
906 FROM_HERE,
907 base::Bind(base::IgnoreResult(&base::DeleteFile),
908 base::FilePath(image_path),
909 false));
911 update->RemoveWithoutPathExpansion(user_id(), NULL);
914 void UserImageManagerImpl::OnJobChangedUserImage() {
915 if (GetUser()->is_logged_in())
916 TryToInitDownloadedProfileImage();
918 content::NotificationService::current()->Notify(
919 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
920 content::Source<UserImageManagerImpl>(this),
921 content::Details<const User>(GetUser()));
924 void UserImageManagerImpl::OnJobDone() {
925 if (job_.get())
926 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, job_.release());
927 else
928 NOTREACHED();
930 if (!user_needs_migration_)
931 return;
932 // Migration completed for |user_id|.
933 user_needs_migration_ = false;
935 const base::DictionaryValue* prefs_images_unsafe =
936 g_browser_process->local_state()->GetDictionary(kUserImages);
937 const base::DictionaryValue* image_properties = NULL;
938 if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
939 user_id(), &image_properties)) {
940 NOTREACHED();
941 return;
944 int image_index = User::kInvalidImageIndex;
945 image_properties->GetInteger(kImageIndexNodeName, &image_index);
946 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
947 ImageIndexToHistogramIndex(image_index),
948 kHistogramImagesCount);
950 std::string image_path;
951 image_properties->GetString(kImagePathNodeName, &image_path);
952 if (!image_path.empty()) {
953 // If an old image exists, delete it and remove |user_id| from the old prefs
954 // dictionary only after the deletion has completed. This ensures that no
955 // orphaned image is left behind if the browser crashes before the deletion
956 // has been performed: In that case, local state will be unchanged and the
957 // migration will be run again on the user's next login.
958 background_task_runner_->PostTaskAndReply(
959 FROM_HERE,
960 base::Bind(base::IgnoreResult(&base::DeleteFile),
961 base::FilePath(image_path),
962 false),
963 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration,
964 weak_factory_.GetWeakPtr()));
965 } else {
966 // If no old image exists, remove |user_id| from the old prefs dictionary.
967 UpdateLocalStateAfterMigration();
971 void UserImageManagerImpl::UpdateLocalStateAfterMigration() {
972 DictionaryPrefUpdate update(g_browser_process->local_state(),
973 kUserImages);
974 update->RemoveWithoutPathExpansion(user_id(), NULL);
977 void UserImageManagerImpl::TryToCreateImageSyncObserver() {
978 const User* user = GetUser();
979 // If the currently logged-in user's user image is managed, the sync observer
980 // must not be started so that the policy-set image does not get synced out.
981 if (!user_image_sync_observer_ &&
982 user && user->CanSyncImage() &&
983 !IsUserImageManaged()) {
984 user_image_sync_observer_.reset(new UserImageSyncObserver(user));
988 const User* UserImageManagerImpl::GetUser() const {
989 return user_manager_->FindUser(user_id());
992 User* UserImageManagerImpl::GetUserAndModify() const {
993 return user_manager_->FindUserAndModify(user_id());
996 bool UserImageManagerImpl::IsUserLoggedInAndRegular() const {
997 const User* user = GetUser();
998 if (!user)
999 return false;
1000 return user->is_logged_in() && user->GetType() == User::USER_TYPE_REGULAR;
1003 } // namespace chromeos