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/files/file_path.h" 10 #include "base/files/file_util.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/profiles/profile_helper.h" 29 #include "chrome/browser/profiles/profile_downloader.h" 30 #include "chrome/browser/profiles/profile_manager.h" 31 #include "chrome/common/chrome_paths.h" 32 #include "chrome/grit/theme_resources.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_manager.h" 36 #include "components/user_manager/user_type.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" 42 43 namespace chromeos { 44 45 namespace { 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. 50 const char kUserImages[] = "UserImages"; 51 52 // A dictionary that maps user_ids to user image data with images stored in 53 // JPEG format. 54 const char kUserImageProperties[] = "user_image_info"; 55 56 // Names of user image properties. 57 const char kImagePathNodeName[] = "path"; 58 const char kImageIndexNodeName[] = "index"; 59 const char kImageURLNodeName[] = "url"; 60 61 // Delay betweeen user login and attempt to update user's profile data. 62 const int kProfileDataDownloadDelaySec = 10; 63 64 // Interval betweeen retries to update user's profile data. 65 const int kProfileDataDownloadRetryIntervalSec = 300; 66 67 // Delay betweeen subsequent profile refresh attempts (24 hrs). 68 const int kProfileRefreshIntervalSec = 24 * 3600; 69 70 const char kSafeImagePathExtension[] = ".jpg"; 71 72 // Enum for reporting histograms about profile picture download. 73 enum 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. 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"; 105 106 static 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|. 110 void 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. 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; 155 default: 156 return image_index; 157 } 158 } 159 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(); 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 189 void 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. 202 class 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 285 UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent) 286 : parent_(parent), 287 run_(false), 288 weak_factory_(this) { 289 } 290 291 UserImageManagerImpl::Job::~Job() { 292 } 293 294 void 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 328 void 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 344 void 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 360 void 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 383 void 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 401 void 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 412 void 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 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); 437 438 base::PostTaskAndReplyWithResult( 439 parent_->background_task_runner_.get(), 440 FROM_HERE, 441 base::Bind(&SaveImage, user_image_, image_path_), 442 base::Bind(&Job::OnSaveImageDone, weak_factory_.GetWeakPtr())); 443 } 444 445 void UserImageManagerImpl::Job::OnSaveImageDone(bool success) { 446 if (success || image_index_ == user_manager::User::USER_IMAGE_PROFILE) 447 UpdateLocalState(); 448 NotifyJobDone(); 449 } 450 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())) 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 469 void UserImageManagerImpl::Job::NotifyJobDone() { 470 parent_->OnJobDone(); 471 } 472 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_); 493 } 494 495 UserImageManagerImpl::~UserImageManagerImpl() {} 496 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) 504 return; 505 user_manager::User* user = GetUserAndModify(); 506 bool needs_migration = false; 507 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); 515 if (needs_migration) 516 user_needs_migration_ = true; 517 } 518 if (prefs_images) { 519 prefs_images->GetDictionaryWithoutPathExpansion(user_id(), 520 &image_properties); 521 } 522 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()) 527 return; 528 529 if (!image_properties) { 530 SetInitialUserImage(); 531 return; 532 } 533 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) { 537 user->SetImage( 538 user_manager::UserImage(user_manager::GetDefaultImage(image_index)), 539 image_index); 540 return; 541 } 542 543 if (image_index != user_manager::User::USER_IMAGE_EXTERNAL && 544 image_index != user_manager::User::USER_IMAGE_PROFILE) { 545 NOTREACHED(); 546 return; 547 } 548 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); 554 555 user->SetImageURL(image_url); 556 user->SetStubImage(user_manager::UserImage( 557 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 558 IDR_PROFILE_PICTURE_LOADING)), 559 image_index, 560 true); 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. 568 return; 569 } 570 571 job_.reset(new Job(this)); 572 job_->LoadImage(base::FilePath(image_path), image_index, image_url); 573 } 574 575 void UserImageManagerImpl::UserLoggedIn(bool user_is_new, 576 bool user_is_local) { 577 const user_manager::User* user = GetUser(); 578 if (user_is_new) { 579 if (!user_is_local) 580 SetInitialUserImage(); 581 } else { 582 UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", 583 ImageIndexToHistogramIndex(user->image_index()), 584 user_manager::kHistogramImagesCount); 585 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), 598 user->image_index(), 599 user->image_url(), 600 false); 601 } else { 602 job_->SetToDefaultImage(user->image_index()); 603 } 604 } 605 } 606 } 607 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; 612 613 if (IsUserLoggedInAndRegular()) { 614 TryToInitDownloadedProfileImage(); 615 616 // Schedule an initial download of the profile data (full name and 617 // optionally image). 618 profile_download_one_shot_timer_.Start( 619 FROM_HERE, 620 g_ignore_profile_data_download_delay_ ? 621 base::TimeDelta() : 622 base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec), 623 base::Bind(&UserImageManagerImpl::DownloadProfileData, 624 base::Unretained(this), 625 kProfileDownloadReasonLoggedIn)); 626 // Schedule periodic refreshes of the profile data. 627 profile_download_periodic_timer_.Start( 628 FROM_HERE, 629 base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), 630 base::Bind(&UserImageManagerImpl::DownloadProfileData, 631 base::Unretained(this), 632 kProfileDownloadReasonScheduled)); 633 } else { 634 profile_download_one_shot_timer_.Stop(); 635 profile_download_periodic_timer_.Stop(); 636 } 637 638 user_image_sync_observer_.reset(); 639 TryToCreateImageSyncObserver(); 640 } 641 642 void UserImageManagerImpl::SaveUserDefaultImageIndex(int default_image_index) { 643 if (IsUserImageManaged()) 644 return; 645 job_.reset(new Job(this)); 646 job_->SetToDefaultImage(default_image_index); 647 } 648 649 void UserImageManagerImpl::SaveUserImage( 650 const user_manager::UserImage& user_image) { 651 if (IsUserImageManaged()) 652 return; 653 job_.reset(new Job(this)); 654 job_->SetToImage(user_manager::User::USER_IMAGE_EXTERNAL, user_image); 655 } 656 657 void UserImageManagerImpl::SaveUserImageFromFile(const base::FilePath& path) { 658 if (IsUserImageManaged()) 659 return; 660 job_.reset(new Job(this)); 661 job_->SetToPath(path, user_manager::User::USER_IMAGE_EXTERNAL, GURL(), true); 662 } 663 664 void UserImageManagerImpl::SaveUserImageFromProfileImage() { 665 if (IsUserImageManaged()) 666 return; 667 // Use the profile image if it has been downloaded already. Otherwise, use a 668 // stub image (gray avatar). 669 job_.reset(new Job(this)); 670 job_->SetToImage(user_manager::User::USER_IMAGE_PROFILE, 671 downloaded_profile_image_.isNull() 672 ? user_manager::UserImage() 673 : user_manager::UserImage::CreateAndEncode( 674 downloaded_profile_image_)); 675 // If no profile image has been downloaded yet, ensure that a download is 676 // started. 677 if (downloaded_profile_image_.isNull()) 678 DownloadProfileData(kProfileDownloadReasonProfileImageChosen); 679 } 680 681 void UserImageManagerImpl::DeleteUserImage() { 682 job_.reset(); 683 DeleteUserImageAndLocalStateEntry(kUserImages); 684 DeleteUserImageAndLocalStateEntry(kUserImageProperties); 685 } 686 687 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { 688 profile_image_requested_ = true; 689 DownloadProfileData(reason); 690 } 691 692 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { 693 return downloaded_profile_image_; 694 } 695 696 UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { 697 return user_image_sync_observer_.get(); 698 } 699 700 void UserImageManagerImpl::Shutdown() { 701 profile_downloader_.reset(); 702 user_image_sync_observer_.reset(); 703 } 704 705 void UserImageManagerImpl::OnExternalDataSet(const std::string& policy) { 706 DCHECK_EQ(policy::key::kUserAvatarImage, policy); 707 if (IsUserImageManaged()) 708 return; 709 710 has_managed_image_ = true; 711 job_.reset(); 712 713 const user_manager::User* user = GetUser(); 714 // If the user image for the currently logged-in user became managed, stop the 715 // sync observer so that the policy-set image does not get synced out. 716 if (user && user->is_logged_in()) 717 user_image_sync_observer_.reset(); 718 } 719 720 void UserImageManagerImpl::OnExternalDataCleared(const std::string& policy) { 721 DCHECK_EQ(policy::key::kUserAvatarImage, policy); 722 has_managed_image_ = false; 723 SetInitialUserImage(); 724 TryToCreateImageSyncObserver(); 725 } 726 727 void UserImageManagerImpl::OnExternalDataFetched(const std::string& policy, 728 scoped_ptr<std::string> data) { 729 DCHECK_EQ(policy::key::kUserAvatarImage, policy); 730 DCHECK(IsUserImageManaged()); 731 if (data) { 732 job_.reset(new Job(this)); 733 job_->SetToImageData(data.Pass()); 734 } 735 } 736 737 // static 738 void UserImageManagerImpl::IgnoreProfileDataDownloadDelayForTesting() { 739 g_ignore_profile_data_download_delay_ = true; 740 } 741 742 bool UserImageManagerImpl::NeedsProfilePicture() const { 743 return downloading_profile_image_; 744 } 745 746 int UserImageManagerImpl::GetDesiredImageSideLength() const { 747 return GetCurrentUserImageSize(); 748 } 749 750 Profile* UserImageManagerImpl::GetBrowserProfile() { 751 return ProfileHelper::Get()->GetProfileByUserUnsafe(GetUser()); 752 } 753 754 std::string UserImageManagerImpl::GetCachedPictureURL() const { 755 return profile_image_url_.spec(); 756 } 757 758 void UserImageManagerImpl::OnProfileDownloadSuccess( 759 ProfileDownloader* downloader) { 760 // Ensure that the |profile_downloader_| is deleted when this method returns. 761 scoped_ptr<ProfileDownloader> profile_downloader( 762 profile_downloader_.release()); 763 DCHECK_EQ(downloader, profile_downloader.get()); 764 765 user_manager_->UpdateUserAccountData( 766 user_id(), 767 user_manager::UserManager::UserAccountData( 768 downloader->GetProfileFullName(), 769 downloader->GetProfileGivenName(), 770 downloader->GetProfileLocale())); 771 if (!downloading_profile_image_) 772 return; 773 774 ProfileDownloadResult result = kDownloadFailure; 775 switch (downloader->GetProfilePictureStatus()) { 776 case ProfileDownloader::PICTURE_SUCCESS: 777 result = kDownloadSuccess; 778 break; 779 case ProfileDownloader::PICTURE_CACHED: 780 result = kDownloadCached; 781 break; 782 case ProfileDownloader::PICTURE_DEFAULT: 783 result = kDownloadDefault; 784 break; 785 default: 786 NOTREACHED(); 787 } 788 789 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 790 result, 791 kDownloadResultsCount); 792 DCHECK(!profile_image_load_start_time_.is_null()); 793 AddProfileImageTimeHistogram( 794 result, 795 profile_image_download_reason_, 796 base::TimeTicks::Now() - profile_image_load_start_time_); 797 798 // Ignore the image if it is no longer needed. 799 if (!NeedProfileImage()) 800 return; 801 802 if (result == kDownloadDefault) { 803 content::NotificationService::current()->Notify( 804 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 805 content::Source<UserImageManager>(this), 806 content::NotificationService::NoDetails()); 807 } else { 808 profile_image_requested_ = false; 809 } 810 811 // Nothing to do if the picture is cached or is the default avatar. 812 if (result != kDownloadSuccess) 813 return; 814 815 downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap( 816 downloader->GetProfilePicture()); 817 profile_image_url_ = GURL(downloader->GetProfilePictureURL()); 818 819 const user_manager::User* user = GetUser(); 820 if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE) { 821 VLOG(1) << "Updating profile image for logged-in user."; 822 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 823 kDownloadSuccessChanged, 824 kDownloadResultsCount); 825 // This will persist |downloaded_profile_image_| to disk. 826 SaveUserImageFromProfileImage(); 827 } 828 829 content::NotificationService::current()->Notify( 830 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 831 content::Source<UserImageManager>(this), 832 content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); 833 } 834 835 void UserImageManagerImpl::OnProfileDownloadFailure( 836 ProfileDownloader* downloader, 837 ProfileDownloaderDelegate::FailureReason reason) { 838 DCHECK_EQ(downloader, profile_downloader_.get()); 839 profile_downloader_.reset(); 840 841 if (downloading_profile_image_) { 842 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 843 kDownloadFailure, 844 kDownloadResultsCount); 845 DCHECK(!profile_image_load_start_time_.is_null()); 846 AddProfileImageTimeHistogram( 847 kDownloadFailure, 848 profile_image_download_reason_, 849 base::TimeTicks::Now() - profile_image_load_start_time_); 850 } 851 852 if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) { 853 // Retry download after a delay if a network error occurred. 854 profile_download_one_shot_timer_.Start( 855 FROM_HERE, 856 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec), 857 base::Bind(&UserImageManagerImpl::DownloadProfileData, 858 base::Unretained(this), 859 kProfileDownloadReasonRetry)); 860 } 861 862 content::NotificationService::current()->Notify( 863 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 864 content::Source<UserImageManager>(this), 865 content::NotificationService::NoDetails()); 866 } 867 868 bool UserImageManagerImpl::IsUserImageManaged() const { 869 return has_managed_image_; 870 } 871 872 void UserImageManagerImpl::SetInitialUserImage() { 873 // Choose a random default image. 874 SaveUserDefaultImageIndex( 875 base::RandInt(user_manager::kFirstDefaultImageIndex, 876 user_manager::kDefaultImagesCount - 1)); 877 } 878 879 void UserImageManagerImpl::TryToInitDownloadedProfileImage() { 880 const user_manager::User* user = GetUser(); 881 if (user->image_index() == user_manager::User::USER_IMAGE_PROFILE && 882 downloaded_profile_image_.isNull() && !user->image_is_stub()) { 883 // Initialize the |downloaded_profile_image_| for the currently logged-in 884 // user if it has not been initialized already, the user image is the 885 // profile image and the user image has been loaded successfully. 886 VLOG(1) << "Profile image initialized from disk."; 887 downloaded_profile_image_ = user->GetImage(); 888 profile_image_url_ = user->image_url(); 889 } 890 } 891 892 bool UserImageManagerImpl::NeedProfileImage() const { 893 const user_manager::User* user = GetUser(); 894 return IsUserLoggedInAndRegular() && 895 (user->image_index() == user_manager::User::USER_IMAGE_PROFILE || 896 profile_image_requested_); 897 } 898 899 void UserImageManagerImpl::DownloadProfileData(const std::string& reason) { 900 // GAIA profiles exist for regular users only. 901 if (!IsUserLoggedInAndRegular()) 902 return; 903 904 // If a download is already in progress, allow it to continue, with one 905 // exception: If the current download does not include the profile image but 906 // the image has since become necessary, start a new download that includes 907 // the profile image. 908 if (profile_downloader_ && 909 (downloading_profile_image_ || !NeedProfileImage())) { 910 return; 911 } 912 913 downloading_profile_image_ = NeedProfileImage(); 914 profile_image_download_reason_ = reason; 915 profile_image_load_start_time_ = base::TimeTicks::Now(); 916 profile_downloader_.reset(new ProfileDownloader(this)); 917 profile_downloader_->Start(); 918 } 919 920 void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry( 921 const char* prefs_dict_root) { 922 DictionaryPrefUpdate update(g_browser_process->local_state(), 923 prefs_dict_root); 924 const base::DictionaryValue* image_properties; 925 if (!update->GetDictionaryWithoutPathExpansion(user_id(), &image_properties)) 926 return; 927 928 std::string image_path; 929 image_properties->GetString(kImagePathNodeName, &image_path); 930 if (!image_path.empty()) { 931 background_task_runner_->PostTask( 932 FROM_HERE, 933 base::Bind(base::IgnoreResult(&base::DeleteFile), 934 base::FilePath(image_path), 935 false)); 936 } 937 update->RemoveWithoutPathExpansion(user_id(), NULL); 938 } 939 940 void UserImageManagerImpl::OnJobChangedUserImage() { 941 if (GetUser()->is_logged_in()) 942 TryToInitDownloadedProfileImage(); 943 944 content::NotificationService::current()->Notify( 945 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 946 content::Source<UserImageManagerImpl>(this), 947 content::Details<const user_manager::User>(GetUser())); 948 } 949 950 void UserImageManagerImpl::OnJobDone() { 951 if (job_.get()) 952 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, job_.release()); 953 else 954 NOTREACHED(); 955 956 if (!user_needs_migration_) 957 return; 958 // Migration completed for |user_id|. 959 user_needs_migration_ = false; 960 961 const base::DictionaryValue* prefs_images_unsafe = 962 g_browser_process->local_state()->GetDictionary(kUserImages); 963 const base::DictionaryValue* image_properties = NULL; 964 if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion( 965 user_id(), &image_properties)) { 966 NOTREACHED(); 967 return; 968 } 969 970 int image_index = user_manager::User::USER_IMAGE_INVALID; 971 image_properties->GetInteger(kImageIndexNodeName, &image_index); 972 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", 973 ImageIndexToHistogramIndex(image_index), 974 user_manager::kHistogramImagesCount); 975 976 std::string image_path; 977 image_properties->GetString(kImagePathNodeName, &image_path); 978 if (!image_path.empty()) { 979 // If an old image exists, delete it and remove |user_id| from the old prefs 980 // dictionary only after the deletion has completed. This ensures that no 981 // orphaned image is left behind if the browser crashes before the deletion 982 // has been performed: In that case, local state will be unchanged and the 983 // migration will be run again on the user's next login. 984 background_task_runner_->PostTaskAndReply( 985 FROM_HERE, 986 base::Bind(base::IgnoreResult(&base::DeleteFile), 987 base::FilePath(image_path), 988 false), 989 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration, 990 weak_factory_.GetWeakPtr())); 991 } else { 992 // If no old image exists, remove |user_id| from the old prefs dictionary. 993 UpdateLocalStateAfterMigration(); 994 } 995 } 996 997 void UserImageManagerImpl::UpdateLocalStateAfterMigration() { 998 DictionaryPrefUpdate update(g_browser_process->local_state(), 999 kUserImages); 1000 update->RemoveWithoutPathExpansion(user_id(), NULL); 1001 } 1002 1003 void UserImageManagerImpl::TryToCreateImageSyncObserver() { 1004 const user_manager::User* user = GetUser(); 1005 // If the currently logged-in user's user image is managed, the sync observer 1006 // must not be started so that the policy-set image does not get synced out. 1007 if (!user_image_sync_observer_ && 1008 user && user->CanSyncImage() && 1009 !IsUserImageManaged()) { 1010 user_image_sync_observer_.reset(new UserImageSyncObserver(user)); 1011 } 1012 } 1013 1014 const user_manager::User* UserImageManagerImpl::GetUser() const { 1015 return user_manager_->FindUser(user_id()); 1016 } 1017 1018 user_manager::User* UserImageManagerImpl::GetUserAndModify() const { 1019 return user_manager_->FindUserAndModify(user_id()); 1020 } 1021 1022 bool UserImageManagerImpl::IsUserLoggedInAndRegular() const { 1023 const user_manager::User* user = GetUser(); 1024 if (!user) 1025 return false; 1026 return user->is_logged_in() && 1027 user->GetType() == user_manager::USER_TYPE_REGULAR; 1028 } 1029 1030 } // namespace chromeos 1031