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