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"
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"
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
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
,
77 // Must be the last, convenient count.
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
;
112 case kDownloadFailure
:
113 histogram_name
= kProfileDownloadFailureTime
;
115 case kDownloadDefault
:
116 histogram_name
= kProfileDownloadDefaultTime
;
118 case kDownloadSuccess
:
119 histogram_name
= kProfileDownloadSuccessTime
;
121 case kDownloadCached
:
122 histogram_name
= kProfileDownloadCachedTime
;
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
;
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();
167 NOTREACHED() << "Raw image missing.";
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.";
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
{
200 // The |Job| will update the user object corresponding to |parent|.
201 explicit Job(UserImageManagerImpl
* parent
);
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
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
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
230 void SetToPath(const base::FilePath
& path
,
232 const GURL
& image_url
,
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_|.
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
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.
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
)
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
) {
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_
));
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(),
323 void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index
) {
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_
));
338 void UserImageManagerImpl::Job::SetToImage(int image_index
,
339 const UserImage
& user_image
) {
343 DCHECK(image_index
== User::kExternalImageIndex
||
344 image_index
== User::kProfileImageIndex
);
346 image_index_
= image_index
;
347 user_image_
= user_image
;
350 SaveImageAndUpdateLocalState();
353 void UserImageManagerImpl::Job::SetToImageData(scoped_ptr
<std::string
> data
) {
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(),
376 void UserImageManagerImpl::Job::SetToPath(const base::FilePath
& path
,
378 const GURL
& image_url
,
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(),
394 void UserImageManagerImpl::Job::OnLoadImageDone(bool save
,
395 const UserImage
& user_image
) {
396 user_image_
= user_image
;
399 SaveImageAndUpdateLocalState();
404 void UserImageManagerImpl::Job::UpdateUser() {
405 User
* user
= parent_
->user_manager_
->FindUserAndModify(user_id());
409 if (!user_image_
.image().isNull())
410 user
->SetImage(user_image_
, image_index_
);
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_
,
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
)
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()))
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
)
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
);
500 user_needs_migration_
= true;
503 prefs_images
->GetDictionaryWithoutPathExpansion(user_id(),
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())
513 if (!image_properties
) {
514 SetInitialUserImage();
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
)),
526 if (image_index
!= User::kExternalImageIndex
&&
527 image_index
!= User::kProfileImageIndex
) {
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.
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();
558 SetInitialUserImage();
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
),
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(
598 g_ignore_profile_data_download_delay_
?
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(
607 base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec
),
608 base::Bind(&UserImageManagerImpl::DownloadProfileData
,
609 base::Unretained(this),
610 kProfileDownloadReasonScheduled
));
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())
623 job_
.reset(new Job(this));
624 job_
->SetToDefaultImage(default_image_index
);
627 void UserImageManagerImpl::SaveUserImage(const UserImage
& user_image
) {
628 if (IsUserImageManaged())
630 job_
.reset(new Job(this));
631 job_
->SetToImage(User::kExternalImageIndex
, user_image
);
634 void UserImageManagerImpl::SaveUserImageFromFile(const base::FilePath
& path
) {
635 if (IsUserImageManaged())
637 job_
.reset(new Job(this));
638 job_
->SetToPath(path
, User::kExternalImageIndex
, GURL(), true);
641 void UserImageManagerImpl::SaveUserImageFromProfileImage() {
642 if (IsUserImageManaged())
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() ?
650 UserImage::CreateAndEncode(downloaded_profile_image_
));
651 // If no profile image has been downloaded yet, ensure that a download is
653 if (downloaded_profile_image_
.isNull())
654 DownloadProfileData(kProfileDownloadReasonProfileImageChosen
);
657 void UserImageManagerImpl::DeleteUserImage() {
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())
686 has_managed_image_
= true;
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());
707 job_
.reset(new Job(this));
708 job_
->SetToImageData(data
.Pass());
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(
742 UserManager::UserAccountData(downloader
->GetProfileFullName(),
743 downloader
->GetProfileGivenName(),
744 downloader
->GetProfileLocale()));
745 if (!downloading_profile_image_
)
748 ProfileDownloadResult result
= kDownloadFailure
;
749 switch (downloader
->GetProfilePictureStatus()) {
750 case ProfileDownloader::PICTURE_SUCCESS
:
751 result
= kDownloadSuccess
;
753 case ProfileDownloader::PICTURE_CACHED
:
754 result
= kDownloadCached
;
756 case ProfileDownloader::PICTURE_DEFAULT
:
757 result
= kDownloadDefault
;
763 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
765 kDownloadResultsCount
);
766 DCHECK(!profile_image_load_start_time_
.is_null());
767 AddProfileImageTimeHistogram(
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())
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());
782 profile_image_requested_
= false;
785 // Nothing to do if the picture is cached or is the default avatar.
786 if (result
!= kDownloadSuccess
)
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",
818 kDownloadResultsCount
);
819 DCHECK(!profile_image_load_start_time_
.is_null());
820 AddProfileImageTimeHistogram(
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(
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())
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())) {
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(),
898 const base::DictionaryValue
* image_properties
;
899 if (!update
->GetDictionaryWithoutPathExpansion(user_id(), &image_properties
))
902 std::string image_path
;
903 image_properties
->GetString(kImagePathNodeName
, &image_path
);
904 if (!image_path
.empty()) {
905 background_task_runner_
->PostTask(
907 base::Bind(base::IgnoreResult(&base::DeleteFile
),
908 base::FilePath(image_path
),
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() {
926 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE
, job_
.release());
930 if (!user_needs_migration_
)
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
)) {
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(
960 base::Bind(base::IgnoreResult(&base::DeleteFile
),
961 base::FilePath(image_path
),
963 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration
,
964 weak_factory_
.GetWeakPtr()));
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(),
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();
1000 return user
->is_logged_in() && user
->GetType() == User::USER_TYPE_REGULAR
;
1003 } // namespace chromeos