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.
5 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager_impl.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/path_service.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/prefs/scoped_user_pref_update.h"
16 #include "base/rand_util.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/task_runner_util.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "base/values.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/chromeos/login/helper.h"
28 #include "chrome/browser/chromeos/login/users/avatar/user_image_sync_observer.h"
29 #include "chrome/browser/chromeos/profiles/profile_helper.h"
30 #include "chrome/browser/profiles/profile_downloader.h"
31 #include "chrome/browser/profiles/profile_manager.h"
32 #include "chrome/common/chrome_paths.h"
33 #include "chrome/grit/theme_resources.h"
34 #include "components/user_manager/user_image/default_user_images.h"
35 #include "components/user_manager/user_image/user_image.h"
36 #include "components/user_manager/user_manager.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_service.h"
39 #include "policy/policy_constants.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/gfx/image/image_skia.h"
47 // A dictionary that maps user_ids to old user image data with images stored in
48 // PNG format. Deprecated.
49 // TODO(ivankr): remove this const char after migration is gone.
50 const char kUserImages
[] = "UserImages";
52 // A dictionary that maps user_ids to user image data with images stored in
54 const char kUserImageProperties
[] = "user_image_info";
56 // Names of user image properties.
57 const char kImagePathNodeName
[] = "path";
58 const char kImageIndexNodeName
[] = "index";
59 const char kImageURLNodeName
[] = "url";
61 // Delay betweeen user login and attempt to update user's profile data.
62 const int kProfileDataDownloadDelaySec
= 10;
64 // Interval betweeen retries to update user's profile data.
65 const int kProfileDataDownloadRetryIntervalSec
= 300;
67 // Delay betweeen subsequent profile refresh attempts (24 hrs).
68 const int kProfileRefreshIntervalSec
= 24 * 3600;
70 const char kSafeImagePathExtension
[] = ".jpg";
72 // Enum for reporting histograms about profile picture download.
73 enum ProfileDownloadResult
{
74 kDownloadSuccessChanged
,
80 // Must be the last, convenient count.
84 // Time histogram prefix for a cached profile image download.
85 const char kProfileDownloadCachedTime
[] =
86 "UserImage.ProfileDownloadTime.Cached";
87 // Time histogram prefix for the default profile image download.
88 const char kProfileDownloadDefaultTime
[] =
89 "UserImage.ProfileDownloadTime.Default";
90 // Time histogram prefix for a failed profile image download.
91 const char kProfileDownloadFailureTime
[] =
92 "UserImage.ProfileDownloadTime.Failure";
93 // Time histogram prefix for a successful profile image download.
94 const char kProfileDownloadSuccessTime
[] =
95 "UserImage.ProfileDownloadTime.Success";
96 // Time histogram suffix for a profile image download after login.
97 const char kProfileDownloadReasonLoggedIn
[] = "LoggedIn";
98 // Time histogram suffix for a profile image download when the user chooses the
99 // profile image but it has not been downloaded yet.
100 const char kProfileDownloadReasonProfileImageChosen
[] = "ProfileImageChosen";
101 // Time histogram suffix for a scheduled profile image download.
102 const char kProfileDownloadReasonScheduled
[] = "Scheduled";
103 // Time histogram suffix for a profile image download retry.
104 const char kProfileDownloadReasonRetry
[] = "Retry";
106 static bool g_ignore_profile_data_download_delay_
= false;
108 // Add a histogram showing the time it takes to download profile image.
109 // Separate histograms are reported for each download |reason| and |result|.
110 void AddProfileImageTimeHistogram(ProfileDownloadResult result
,
111 const std::string
& download_reason
,
112 const base::TimeDelta
& time_delta
) {
113 std::string histogram_name
;
115 case kDownloadFailure
:
116 histogram_name
= kProfileDownloadFailureTime
;
118 case kDownloadDefault
:
119 histogram_name
= kProfileDownloadDefaultTime
;
121 case kDownloadSuccess
:
122 histogram_name
= kProfileDownloadSuccessTime
;
124 case kDownloadCached
:
125 histogram_name
= kProfileDownloadCachedTime
;
130 if (!download_reason
.empty()) {
131 histogram_name
+= ".";
132 histogram_name
+= download_reason
;
135 static const base::TimeDelta min_time
= base::TimeDelta::FromMilliseconds(1);
136 static const base::TimeDelta max_time
= base::TimeDelta::FromSeconds(50);
137 const size_t bucket_count(50);
139 base::HistogramBase
* counter
= base::Histogram::FactoryTimeGet(
140 histogram_name
, min_time
, max_time
, bucket_count
,
141 base::HistogramBase::kUmaTargetedHistogramFlag
);
142 counter
->AddTime(time_delta
);
144 DVLOG(1) << "Profile image download time: " << time_delta
.InSecondsF();
147 // Converts |image_index| to UMA histogram value.
148 int ImageIndexToHistogramIndex(int image_index
) {
149 switch (image_index
) {
150 case user_manager::User::USER_IMAGE_EXTERNAL
:
151 // TODO(ivankr): Distinguish this from selected from file.
152 return user_manager::kHistogramImageFromCamera
;
153 case user_manager::User::USER_IMAGE_PROFILE
:
154 return user_manager::kHistogramImageFromProfile
;
160 bool SaveImage(const user_manager::UserImage
& user_image
,
161 const base::FilePath
& image_path
) {
162 user_manager::UserImage safe_image
;
163 const user_manager::UserImage::RawImage
* encoded_image
= NULL
;
164 if (!user_image
.is_safe_format()) {
165 safe_image
= user_manager::UserImage::CreateAndEncode(user_image
.image());
166 encoded_image
= &safe_image
.raw_image();
167 UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image
->size());
168 } else if (user_image
.has_raw_image()) {
169 encoded_image
= &user_image
.raw_image();
171 NOTREACHED() << "Raw image missing.";
175 if (!encoded_image
->size() ||
176 base::WriteFile(image_path
,
177 reinterpret_cast<const char*>(&(*encoded_image
)[0]),
178 encoded_image
->size()) == -1) {
179 LOG(ERROR
) << "Failed to save image to file.";
189 void UserImageManager::RegisterPrefs(PrefRegistrySimple
* registry
) {
190 registry
->RegisterDictionaryPref(kUserImages
);
191 registry
->RegisterDictionaryPref(kUserImageProperties
);
194 // Every image load or update is encapsulated by a Job. The Job is allowed to
195 // perform tasks on background threads or in helper processes but:
196 // * Changes to User objects and local state as well as any calls to the
197 // |parent_| must be performed on the thread that the Job is created on only.
198 // * File writes and deletions must be performed via the |parent_|'s
199 // |background_task_runner_| only.
201 // Only one of the Load*() and Set*() methods may be called per Job.
202 class UserImageManagerImpl::Job
{
204 // The |Job| will update the user object corresponding to |parent|.
205 explicit Job(UserImageManagerImpl
* parent
);
208 // Loads the image at |image_path| or one of the default images,
209 // depending on |image_index|, and updates the user object with the
211 void LoadImage(base::FilePath image_path
,
212 const int image_index
,
213 const GURL
& image_url
);
215 // Sets the user image in local state to the default image indicated
216 // by |default_image_index|. Also updates the user object with the
218 void SetToDefaultImage(int default_image_index
);
220 // Saves the |user_image| to disk and sets the user image in local
221 // state to that image. Also updates the user with the new image.
222 void SetToImage(int image_index
, const user_manager::UserImage
& user_image
);
224 // Decodes the JPEG image |data|, crops and resizes the image, saves
225 // it to disk and sets the user image in local state to that image.
226 // Also updates the user object with the new image.
227 void SetToImageData(scoped_ptr
<std::string
> data
);
229 // Loads the image at |path|, transcodes it to JPEG format, saves
230 // the image to disk and sets the user image in local state to that
231 // image. If |resize| is true, the image is cropped and resized
232 // before transcoding. Also updates the user object with the new
234 void SetToPath(const base::FilePath
& path
,
236 const GURL
& image_url
,
240 // Called back after an image has been loaded from disk.
241 void OnLoadImageDone(bool save
, const user_manager::UserImage
& user_image
);
243 // Updates the user object with |user_image_|.
246 // Saves |user_image_| to disk in JPEG format. Local state will be updated
247 // when a callback indicates that the image has been saved.
248 void SaveImageAndUpdateLocalState();
250 // Called back after the |user_image_| has been saved to
251 // disk. Updates the user image information in local state. The
252 // information is only updated if |success| is true (indicating that
253 // the image was saved successfully) or the user image is the
254 // profile image (indicating that even if the image could not be
255 // saved because it is not available right now, it will be
256 // downloaded eventually).
257 void OnSaveImageDone(bool success
);
259 // Updates the user image in local state, setting it to one of the
260 // default images or the saved |user_image_|, depending on
262 void UpdateLocalState();
264 // Notifies the |parent_| that the Job is done.
265 void NotifyJobDone();
267 const std::string
& user_id() const { return parent_
->user_id(); }
269 UserImageManagerImpl
* parent_
;
271 // Whether one of the Load*() or Set*() methods has been run already.
276 base::FilePath image_path_
;
278 user_manager::UserImage user_image_
;
280 base::WeakPtrFactory
<Job
> weak_factory_
;
282 DISALLOW_COPY_AND_ASSIGN(Job
);
285 UserImageManagerImpl::Job::Job(UserImageManagerImpl
* parent
)
288 weak_factory_(this) {
291 UserImageManagerImpl::Job::~Job() {
294 void UserImageManagerImpl::Job::LoadImage(base::FilePath image_path
,
295 const int image_index
,
296 const GURL
& image_url
) {
300 image_index_
= image_index
;
301 image_url_
= image_url
;
302 image_path_
= image_path
;
304 if (image_index_
>= 0 && image_index_
< user_manager::kDefaultImagesCount
) {
305 // Load one of the default images. This happens synchronously.
307 user_manager::UserImage(user_manager::GetDefaultImage(image_index_
));
310 } else if (image_index_
== user_manager::User::USER_IMAGE_EXTERNAL
||
311 image_index_
== user_manager::User::USER_IMAGE_PROFILE
) {
312 // Load the user image from a file referenced by |image_path|. This happens
313 // asynchronously. The JPEG image loader can be used here because
314 // LoadImage() is called only for users whose user image has previously
315 // been set by one of the Set*() methods, which transcode to JPEG format.
316 DCHECK(!image_path_
.empty());
317 parent_
->image_loader_
->Start(image_path_
.value(),
319 base::Bind(&Job::OnLoadImageDone
,
320 weak_factory_
.GetWeakPtr(),
328 void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index
) {
332 DCHECK_LE(0, default_image_index
);
333 DCHECK_GT(user_manager::kDefaultImagesCount
, default_image_index
);
335 image_index_
= default_image_index
;
337 user_manager::UserImage(user_manager::GetDefaultImage(image_index_
));
344 void UserImageManagerImpl::Job::SetToImage(
346 const user_manager::UserImage
& user_image
) {
350 DCHECK(image_index
== user_manager::User::USER_IMAGE_EXTERNAL
||
351 image_index
== user_manager::User::USER_IMAGE_PROFILE
);
353 image_index_
= image_index
;
354 user_image_
= user_image
;
357 SaveImageAndUpdateLocalState();
360 void UserImageManagerImpl::Job::SetToImageData(scoped_ptr
<std::string
> data
) {
364 image_index_
= user_manager::User::USER_IMAGE_EXTERNAL
;
366 // This method uses the image_loader_, not the unsafe_image_loader_:
367 // * This is necessary because the method is used to update the user image
368 // whenever the policy for a user is set. In the case of device-local
369 // accounts, policy may change at any time, even if the user is not
370 // currently logged in (and thus, the unsafe_image_loader_ may not be used).
371 // * This is possible because only JPEG |data| is accepted. No support for
372 // other image file formats is needed.
373 // * This is safe because the image_loader_ employs a hardened JPEG decoder
374 // that protects against malicious invalid image data being used to attack
375 // the login screen or another user session currently in progress.
376 parent_
->image_loader_
->Start(data
.Pass(),
377 login::kMaxUserImageSize
,
378 base::Bind(&Job::OnLoadImageDone
,
379 weak_factory_
.GetWeakPtr(),
383 void UserImageManagerImpl::Job::SetToPath(const base::FilePath
& path
,
385 const GURL
& image_url
,
390 image_index_
= image_index
;
391 image_url_
= image_url
;
393 DCHECK(!path
.empty());
394 parent_
->unsafe_image_loader_
->Start(path
.value(),
395 resize
? login::kMaxUserImageSize
: 0,
396 base::Bind(&Job::OnLoadImageDone
,
397 weak_factory_
.GetWeakPtr(),
401 void UserImageManagerImpl::Job::OnLoadImageDone(
403 const user_manager::UserImage
& user_image
) {
404 user_image_
= user_image
;
407 SaveImageAndUpdateLocalState();
412 void UserImageManagerImpl::Job::UpdateUser() {
413 user_manager::User
* user
=
414 parent_
->user_manager_
->FindUserAndModify(user_id());
418 if (!user_image_
.image().isNull()) {
419 user
->SetImage(user_image_
, image_index_
);
422 user_manager::UserImage(
423 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
424 IDR_PROFILE_PICTURE_LOADING
)),
428 user
->SetImageURL(image_url_
);
430 parent_
->OnJobChangedUserImage();
433 void UserImageManagerImpl::Job::SaveImageAndUpdateLocalState() {
434 base::FilePath user_data_dir
;
435 PathService::Get(chrome::DIR_USER_DATA
, &user_data_dir
);
436 image_path_
= user_data_dir
.Append(user_id() + kSafeImagePathExtension
);
438 base::PostTaskAndReplyWithResult(
439 parent_
->background_task_runner_
.get(),
441 base::Bind(&SaveImage
, user_image_
, image_path_
),
442 base::Bind(&Job::OnSaveImageDone
, weak_factory_
.GetWeakPtr()));
445 void UserImageManagerImpl::Job::OnSaveImageDone(bool success
) {
446 if (success
|| image_index_
== user_manager::User::USER_IMAGE_PROFILE
)
451 void UserImageManagerImpl::Job::UpdateLocalState() {
452 // Ignore if data stored or cached outside the user's cryptohome is to be
453 // treated as ephemeral.
454 if (parent_
->user_manager_
->IsUserNonCryptohomeDataEphemeral(user_id()))
457 scoped_ptr
<base::DictionaryValue
> entry(new base::DictionaryValue
);
458 entry
->Set(kImagePathNodeName
, new base::StringValue(image_path_
.value()));
459 entry
->Set(kImageIndexNodeName
, new base::FundamentalValue(image_index_
));
460 if (!image_url_
.is_empty())
461 entry
->Set(kImageURLNodeName
, new base::StringValue(image_url_
.spec()));
462 DictionaryPrefUpdate
update(g_browser_process
->local_state(),
463 kUserImageProperties
);
464 update
->SetWithoutPathExpansion(user_id(), entry
.release());
466 parent_
->user_manager_
->NotifyLocalStateChanged();
469 void UserImageManagerImpl::Job::NotifyJobDone() {
470 parent_
->OnJobDone();
473 UserImageManagerImpl::UserImageManagerImpl(
474 const std::string
& user_id
,
475 user_manager::UserManager
* user_manager
)
476 : UserImageManager(user_id
),
477 user_manager_(user_manager
),
478 downloading_profile_image_(false),
479 profile_image_requested_(false),
480 has_managed_image_(false),
481 user_needs_migration_(false),
482 weak_factory_(this) {
483 base::SequencedWorkerPool
* blocking_pool
=
484 content::BrowserThread::GetBlockingPool();
485 background_task_runner_
=
486 blocking_pool
->GetSequencedTaskRunnerWithShutdownBehavior(
487 blocking_pool
->GetSequenceToken(),
488 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
489 image_loader_
= new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC
,
490 background_task_runner_
);
491 unsafe_image_loader_
= new UserImageLoader(ImageDecoder::DEFAULT_CODEC
,
492 background_task_runner_
);
495 UserImageManagerImpl::~UserImageManagerImpl() {}
497 void UserImageManagerImpl::LoadUserImage() {
498 PrefService
* local_state
= g_browser_process
->local_state();
499 const base::DictionaryValue
* prefs_images_unsafe
=
500 local_state
->GetDictionary(kUserImages
);
501 const base::DictionaryValue
* prefs_images
=
502 local_state
->GetDictionary(kUserImageProperties
);
503 if (!prefs_images
&& !prefs_images_unsafe
)
505 user_manager::User
* user
= GetUserAndModify();
506 bool needs_migration
= false;
508 // If entries are found in both |prefs_images_unsafe| and |prefs_images|,
509 // |prefs_images| is honored for now but |prefs_images_unsafe| will be
510 // migrated, overwriting the |prefs_images| entry, when the user logs in.
511 const base::DictionaryValue
* image_properties
= NULL
;
512 if (prefs_images_unsafe
) {
513 needs_migration
= prefs_images_unsafe
->GetDictionaryWithoutPathExpansion(
514 user_id(), &image_properties
);
516 user_needs_migration_
= true;
519 prefs_images
->GetDictionaryWithoutPathExpansion(user_id(),
523 // If the user image for |user_id| is managed by policy and the policy-set
524 // image is being loaded and persisted right now, let that job continue. It
525 // will update the user image when done.
526 if (IsUserImageManaged() && job_
.get())
529 if (!image_properties
) {
530 SetInitialUserImage();
534 int image_index
= user_manager::User::USER_IMAGE_INVALID
;
535 image_properties
->GetInteger(kImageIndexNodeName
, &image_index
);
536 if (image_index
>= 0 && image_index
< user_manager::kDefaultImagesCount
) {
538 user_manager::UserImage(user_manager::GetDefaultImage(image_index
)),
543 if (image_index
!= user_manager::User::USER_IMAGE_EXTERNAL
&&
544 image_index
!= user_manager::User::USER_IMAGE_PROFILE
) {
549 std::string image_url_string
;
550 image_properties
->GetString(kImageURLNodeName
, &image_url_string
);
551 GURL
image_url(image_url_string
);
552 std::string image_path
;
553 image_properties
->GetString(kImagePathNodeName
, &image_path
);
555 user
->SetImageURL(image_url
);
556 user
->SetStubImage(user_manager::UserImage(
557 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
558 IDR_PROFILE_PICTURE_LOADING
)),
561 DCHECK(!image_path
.empty() ||
562 image_index
== user_manager::User::USER_IMAGE_PROFILE
);
563 if (image_path
.empty() || needs_migration
) {
564 // Return if either of the following is true:
565 // * The profile image is to be used but has not been downloaded yet. The
566 // profile image will be downloaded after login.
567 // * The image needs migration. Migration will be performed after login.
571 job_
.reset(new Job(this));
572 job_
->LoadImage(base::FilePath(image_path
), image_index
, image_url
);
575 void UserImageManagerImpl::UserLoggedIn(bool user_is_new
,
576 bool user_is_local
) {
577 const user_manager::User
* user
= GetUser();
580 SetInitialUserImage();
582 UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn",
583 ImageIndexToHistogramIndex(user
->image_index()),
584 user_manager::kHistogramImagesCount
);
586 if (!IsUserImageManaged() && user_needs_migration_
) {
587 const base::DictionaryValue
* prefs_images_unsafe
=
588 g_browser_process
->local_state()->GetDictionary(kUserImages
);
589 const base::DictionaryValue
* image_properties
= NULL
;
590 if (prefs_images_unsafe
->GetDictionaryWithoutPathExpansion(
591 user_id(), &image_properties
)) {
592 std::string image_path
;
593 image_properties
->GetString(kImagePathNodeName
, &image_path
);
594 job_
.reset(new Job(this));
595 if (!image_path
.empty()) {
596 VLOG(0) << "Loading old user image, then migrating it.";
597 job_
->SetToPath(base::FilePath(image_path
),
602 job_
->SetToDefaultImage(user
->image_index());
608 // Reset the downloaded profile image as a new user logged in.
609 downloaded_profile_image_
= gfx::ImageSkia();
610 profile_image_url_
= GURL();
611 profile_image_requested_
= false;
613 user_image_sync_observer_
.reset();
614 TryToCreateImageSyncObserver();
617 void UserImageManagerImpl::UserProfileCreated() {
618 if (IsUserLoggedInAndHasGaiaAccount()) {
619 TryToInitDownloadedProfileImage();
621 // Schedule an initial download of the profile data (full name and
622 // optionally image).
623 profile_download_one_shot_timer_
.Start(
625 g_ignore_profile_data_download_delay_
?
627 base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec
),
628 base::Bind(&UserImageManagerImpl::DownloadProfileData
,
629 base::Unretained(this),
630 kProfileDownloadReasonLoggedIn
));
631 // Schedule periodic refreshes of the profile data.
632 profile_download_periodic_timer_
.Start(
634 base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec
),
635 base::Bind(&UserImageManagerImpl::DownloadProfileData
,
636 base::Unretained(this),
637 kProfileDownloadReasonScheduled
));
639 profile_download_one_shot_timer_
.Stop();
640 profile_download_periodic_timer_
.Stop();
644 void UserImageManagerImpl::SaveUserDefaultImageIndex(int default_image_index
) {
645 if (IsUserImageManaged())
647 job_
.reset(new Job(this));
648 job_
->SetToDefaultImage(default_image_index
);
651 void UserImageManagerImpl::SaveUserImage(
652 const user_manager::UserImage
& user_image
) {
653 if (IsUserImageManaged())
655 job_
.reset(new Job(this));
656 job_
->SetToImage(user_manager::User::USER_IMAGE_EXTERNAL
, user_image
);
659 void UserImageManagerImpl::SaveUserImageFromFile(const base::FilePath
& path
) {
660 if (IsUserImageManaged())
662 job_
.reset(new Job(this));
663 job_
->SetToPath(path
, user_manager::User::USER_IMAGE_EXTERNAL
, GURL(), true);
666 void UserImageManagerImpl::SaveUserImageFromProfileImage() {
667 if (IsUserImageManaged())
669 // Use the profile image if it has been downloaded already. Otherwise, use a
670 // stub image (gray avatar).
671 job_
.reset(new Job(this));
672 job_
->SetToImage(user_manager::User::USER_IMAGE_PROFILE
,
673 downloaded_profile_image_
.isNull()
674 ? user_manager::UserImage()
675 : user_manager::UserImage::CreateAndEncode(
676 downloaded_profile_image_
));
677 // If no profile image has been downloaded yet, ensure that a download is
679 if (downloaded_profile_image_
.isNull())
680 DownloadProfileData(kProfileDownloadReasonProfileImageChosen
);
683 void UserImageManagerImpl::DeleteUserImage() {
685 DeleteUserImageAndLocalStateEntry(kUserImages
);
686 DeleteUserImageAndLocalStateEntry(kUserImageProperties
);
689 void UserImageManagerImpl::DownloadProfileImage(const std::string
& reason
) {
690 profile_image_requested_
= true;
691 DownloadProfileData(reason
);
694 const gfx::ImageSkia
& UserImageManagerImpl::DownloadedProfileImage() const {
695 return downloaded_profile_image_
;
698 UserImageSyncObserver
* UserImageManagerImpl::GetSyncObserver() const {
699 return user_image_sync_observer_
.get();
702 void UserImageManagerImpl::Shutdown() {
703 profile_downloader_
.reset();
704 user_image_sync_observer_
.reset();
707 void UserImageManagerImpl::OnExternalDataSet(const std::string
& policy
) {
708 DCHECK_EQ(policy::key::kUserAvatarImage
, policy
);
709 if (IsUserImageManaged())
712 has_managed_image_
= true;
715 const user_manager::User
* user
= GetUser();
716 // If the user image for the currently logged-in user became managed, stop the
717 // sync observer so that the policy-set image does not get synced out.
718 if (user
&& user
->is_logged_in())
719 user_image_sync_observer_
.reset();
722 void UserImageManagerImpl::OnExternalDataCleared(const std::string
& policy
) {
723 DCHECK_EQ(policy::key::kUserAvatarImage
, policy
);
724 has_managed_image_
= false;
725 SetInitialUserImage();
726 TryToCreateImageSyncObserver();
729 void UserImageManagerImpl::OnExternalDataFetched(const std::string
& policy
,
730 scoped_ptr
<std::string
> data
) {
731 DCHECK_EQ(policy::key::kUserAvatarImage
, policy
);
732 DCHECK(IsUserImageManaged());
734 job_
.reset(new Job(this));
735 job_
->SetToImageData(data
.Pass());
740 void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() {
741 g_ignore_profile_data_download_delay_
= true;
744 bool UserImageManagerImpl::NeedsProfilePicture() const {
745 return downloading_profile_image_
;
748 int UserImageManagerImpl::GetDesiredImageSideLength() const {
749 return GetCurrentUserImageSize();
752 Profile
* UserImageManagerImpl::GetBrowserProfile() {
753 return ProfileHelper::Get()->GetProfileByUserUnsafe(GetUser());
756 std::string
UserImageManagerImpl::GetCachedPictureURL() const {
757 return profile_image_url_
.spec();
760 bool UserImageManagerImpl::IsPreSignin() const {
764 void UserImageManagerImpl::OnProfileDownloadSuccess(
765 ProfileDownloader
* downloader
) {
766 // Ensure that the |profile_downloader_| is deleted when this method returns.
767 scoped_ptr
<ProfileDownloader
> profile_downloader(
768 profile_downloader_
.release());
769 DCHECK_EQ(downloader
, profile_downloader
.get());
771 user_manager_
->UpdateUserAccountData(
773 user_manager::UserManager::UserAccountData(
774 downloader
->GetProfileFullName(),
775 downloader
->GetProfileGivenName(),
776 downloader
->GetProfileLocale()));
777 if (!downloading_profile_image_
)
780 ProfileDownloadResult result
= kDownloadFailure
;
781 switch (downloader
->GetProfilePictureStatus()) {
782 case ProfileDownloader::PICTURE_SUCCESS
:
783 result
= kDownloadSuccess
;
785 case ProfileDownloader::PICTURE_CACHED
:
786 result
= kDownloadCached
;
788 case ProfileDownloader::PICTURE_DEFAULT
:
789 result
= kDownloadDefault
;
795 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
797 kDownloadResultsCount
);
798 DCHECK(!profile_image_load_start_time_
.is_null());
799 AddProfileImageTimeHistogram(
801 profile_image_download_reason_
,
802 base::TimeTicks::Now() - profile_image_load_start_time_
);
804 // Ignore the image if it is no longer needed.
805 if (!NeedProfileImage())
808 if (result
== kDownloadDefault
) {
809 content::NotificationService::current()->Notify(
810 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
,
811 content::Source
<UserImageManager
>(this),
812 content::NotificationService::NoDetails());
814 profile_image_requested_
= false;
817 // Nothing to do if the picture is cached or is the default avatar.
818 if (result
!= kDownloadSuccess
)
821 downloaded_profile_image_
= gfx::ImageSkia::CreateFrom1xBitmap(
822 downloader
->GetProfilePicture());
823 profile_image_url_
= GURL(downloader
->GetProfilePictureURL());
825 const user_manager::User
* user
= GetUser();
826 if (user
->image_index() == user_manager::User::USER_IMAGE_PROFILE
) {
827 VLOG(1) << "Updating profile image for logged-in user.";
828 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
829 kDownloadSuccessChanged
,
830 kDownloadResultsCount
);
831 // This will persist |downloaded_profile_image_| to disk.
832 SaveUserImageFromProfileImage();
835 content::NotificationService::current()->Notify(
836 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED
,
837 content::Source
<UserImageManager
>(this),
838 content::Details
<const gfx::ImageSkia
>(&downloaded_profile_image_
));
841 void UserImageManagerImpl::OnProfileDownloadFailure(
842 ProfileDownloader
* downloader
,
843 ProfileDownloaderDelegate::FailureReason reason
) {
844 DCHECK_EQ(downloader
, profile_downloader_
.get());
845 profile_downloader_
.reset();
847 if (downloading_profile_image_
) {
848 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult",
850 kDownloadResultsCount
);
851 DCHECK(!profile_image_load_start_time_
.is_null());
852 AddProfileImageTimeHistogram(
854 profile_image_download_reason_
,
855 base::TimeTicks::Now() - profile_image_load_start_time_
);
858 if (reason
== ProfileDownloaderDelegate::NETWORK_ERROR
) {
859 // Retry download after a delay if a network error occurred.
860 profile_download_one_shot_timer_
.Start(
862 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec
),
863 base::Bind(&UserImageManagerImpl::DownloadProfileData
,
864 base::Unretained(this),
865 kProfileDownloadReasonRetry
));
868 content::NotificationService::current()->Notify(
869 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED
,
870 content::Source
<UserImageManager
>(this),
871 content::NotificationService::NoDetails());
874 bool UserImageManagerImpl::IsUserImageManaged() const {
875 return has_managed_image_
;
878 void UserImageManagerImpl::SetInitialUserImage() {
879 // Choose a random default image.
880 SaveUserDefaultImageIndex(
881 base::RandInt(user_manager::kFirstDefaultImageIndex
,
882 user_manager::kDefaultImagesCount
- 1));
885 void UserImageManagerImpl::TryToInitDownloadedProfileImage() {
886 const user_manager::User
* user
= GetUser();
887 if (user
->image_index() == user_manager::User::USER_IMAGE_PROFILE
&&
888 downloaded_profile_image_
.isNull() && !user
->image_is_stub()) {
889 // Initialize the |downloaded_profile_image_| for the currently logged-in
890 // user if it has not been initialized already, the user image is the
891 // profile image and the user image has been loaded successfully.
892 VLOG(1) << "Profile image initialized from disk.";
893 downloaded_profile_image_
= user
->GetImage();
894 profile_image_url_
= user
->image_url();
898 bool UserImageManagerImpl::NeedProfileImage() const {
899 const user_manager::User
* user
= GetUser();
900 return IsUserLoggedInAndHasGaiaAccount() &&
901 (user
->image_index() == user_manager::User::USER_IMAGE_PROFILE
||
902 profile_image_requested_
);
905 void UserImageManagerImpl::DownloadProfileData(const std::string
& reason
) {
906 if (!IsUserLoggedInAndHasGaiaAccount())
909 // If a download is already in progress, allow it to continue, with one
910 // exception: If the current download does not include the profile image but
911 // the image has since become necessary, start a new download that includes
912 // the profile image.
913 if (profile_downloader_
&&
914 (downloading_profile_image_
|| !NeedProfileImage())) {
918 downloading_profile_image_
= NeedProfileImage();
919 profile_image_download_reason_
= reason
;
920 profile_image_load_start_time_
= base::TimeTicks::Now();
921 profile_downloader_
.reset(new ProfileDownloader(this));
922 profile_downloader_
->Start();
925 void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry(
926 const char* prefs_dict_root
) {
927 DictionaryPrefUpdate
update(g_browser_process
->local_state(),
929 const base::DictionaryValue
* image_properties
;
930 if (!update
->GetDictionaryWithoutPathExpansion(user_id(), &image_properties
))
933 std::string image_path
;
934 image_properties
->GetString(kImagePathNodeName
, &image_path
);
935 if (!image_path
.empty()) {
936 background_task_runner_
->PostTask(
938 base::Bind(base::IgnoreResult(&base::DeleteFile
),
939 base::FilePath(image_path
),
942 update
->RemoveWithoutPathExpansion(user_id(), NULL
);
945 void UserImageManagerImpl::OnJobChangedUserImage() {
946 if (GetUser()->is_logged_in())
947 TryToInitDownloadedProfileImage();
949 content::NotificationService::current()->Notify(
950 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED
,
951 content::Source
<UserImageManagerImpl
>(this),
952 content::Details
<const user_manager::User
>(GetUser()));
955 void UserImageManagerImpl::OnJobDone() {
957 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE
, job_
.release());
961 if (!user_needs_migration_
)
963 // Migration completed for |user_id|.
964 user_needs_migration_
= false;
966 const base::DictionaryValue
* prefs_images_unsafe
=
967 g_browser_process
->local_state()->GetDictionary(kUserImages
);
968 const base::DictionaryValue
* image_properties
= NULL
;
969 if (!prefs_images_unsafe
->GetDictionaryWithoutPathExpansion(
970 user_id(), &image_properties
)) {
975 int image_index
= user_manager::User::USER_IMAGE_INVALID
;
976 image_properties
->GetInteger(kImageIndexNodeName
, &image_index
);
977 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
978 ImageIndexToHistogramIndex(image_index
),
979 user_manager::kHistogramImagesCount
);
981 std::string image_path
;
982 image_properties
->GetString(kImagePathNodeName
, &image_path
);
983 if (!image_path
.empty()) {
984 // If an old image exists, delete it and remove |user_id| from the old prefs
985 // dictionary only after the deletion has completed. This ensures that no
986 // orphaned image is left behind if the browser crashes before the deletion
987 // has been performed: In that case, local state will be unchanged and the
988 // migration will be run again on the user's next login.
989 background_task_runner_
->PostTaskAndReply(
991 base::Bind(base::IgnoreResult(&base::DeleteFile
),
992 base::FilePath(image_path
),
994 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration
,
995 weak_factory_
.GetWeakPtr()));
997 // If no old image exists, remove |user_id| from the old prefs dictionary.
998 UpdateLocalStateAfterMigration();
1002 void UserImageManagerImpl::UpdateLocalStateAfterMigration() {
1003 DictionaryPrefUpdate
update(g_browser_process
->local_state(),
1005 update
->RemoveWithoutPathExpansion(user_id(), NULL
);
1008 void UserImageManagerImpl::TryToCreateImageSyncObserver() {
1009 const user_manager::User
* user
= GetUser();
1010 // If the currently logged-in user's user image is managed, the sync observer
1011 // must not be started so that the policy-set image does not get synced out.
1012 if (!user_image_sync_observer_
&&
1013 user
&& user
->CanSyncImage() &&
1014 !IsUserImageManaged()) {
1015 user_image_sync_observer_
.reset(new UserImageSyncObserver(user
));
1019 const user_manager::User
* UserImageManagerImpl::GetUser() const {
1020 return user_manager_
->FindUser(user_id());
1023 user_manager::User
* UserImageManagerImpl::GetUserAndModify() const {
1024 return user_manager_
->FindUserAndModify(user_id());
1027 bool UserImageManagerImpl::IsUserLoggedInAndHasGaiaAccount() const {
1028 const user_manager::User
* user
= GetUser();
1031 return user
->is_logged_in() && user
->HasGaiaAccount();
1034 } // namespace chromeos