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