1 // Copyright (c) 2012 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/screens/user_image_screen.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/compiler_specific.h" 12 #include "base/location.h" 13 #include "base/logging.h" 14 #include "base/message_loop/message_loop_proxy.h" 15 #include "base/metrics/histogram.h" 16 #include "base/timer/timer.h" 17 #include "base/values.h" 18 #include "chrome/browser/chrome_notification_types.h" 19 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 20 #include "chrome/browser/chromeos/camera_detector.h" 21 #include "chrome/browser/chromeos/login/default_user_images.h" 22 #include "chrome/browser/chromeos/login/login_utils.h" 23 #include "chrome/browser/chromeos/login/screens/screen_observer.h" 24 #include "chrome/browser/chromeos/login/user_image.h" 25 #include "chrome/browser/chromeos/login/user_image_manager.h" 26 #include "chrome/browser/chromeos/login/user_manager.h" 27 #include "chrome/browser/chromeos/login/wizard_controller.h" 28 #include "chrome/browser/policy/profile_policy_connector.h" 29 #include "chrome/browser/policy/profile_policy_connector_factory.h" 30 #include "chrome/browser/profiles/profile.h" 31 #include "chrome/common/url_constants.h" 32 #include "components/policy/core/common/policy_map.h" 33 #include "components/policy/core/common/policy_namespace.h" 34 #include "components/policy/core/common/policy_service.h" 35 #include "content/public/browser/browser_thread.h" 36 #include "content/public/browser/notification_service.h" 37 #include "grit/generated_resources.h" 38 #include "grit/theme_resources.h" 39 #include "policy/policy_constants.h" 40 #include "third_party/skia/include/core/SkBitmap.h" 41 #include "ui/base/l10n/l10n_util.h" 42 #include "ui/base/resource/resource_bundle.h" 43 #include "ui/base/webui/web_ui_util.h" 44 #include "ui/gfx/image/image_skia.h" 45 46 using content::BrowserThread; 47 48 namespace chromeos { 49 50 namespace { 51 52 // Time histogram suffix for profile image download. 53 const char kProfileDownloadReason[] = "OOBE"; 54 55 // Maximum ammount of time to wait for the user image to sync. 56 // The screen is shown iff sync failed or time limit exceeded. 57 const int kSyncTimeoutSeconds = 10; 58 59 } // namespace 60 61 UserImageScreen::UserImageScreen(ScreenObserver* screen_observer, 62 UserImageScreenActor* actor) 63 : WizardScreen(screen_observer), 64 actor_(actor), 65 weak_factory_(this), 66 accept_photo_after_decoding_(false), 67 selected_image_(User::kInvalidImageIndex), 68 profile_picture_enabled_(false), 69 profile_picture_data_url_(content::kAboutBlankURL), 70 profile_picture_absent_(false), 71 is_screen_ready_(false), 72 user_has_selected_image_(false), 73 was_camera_present_(false) { 74 actor_->SetDelegate(this); 75 SetProfilePictureEnabled(true); 76 notification_registrar_.Add(this, 77 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 78 content::NotificationService::AllSources()); 79 } 80 81 UserImageScreen::~UserImageScreen() { 82 if (actor_) 83 actor_->SetDelegate(NULL); 84 if (image_decoder_.get()) 85 image_decoder_->set_delegate(NULL); 86 } 87 88 void UserImageScreen::OnScreenReady() { 89 is_screen_ready_ = true; 90 if (!IsWaitingForSync()) 91 HideCurtain(); 92 } 93 94 void UserImageScreen::OnPhotoTaken(const std::string& raw_data) { 95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 96 user_photo_ = gfx::ImageSkia(); 97 if (image_decoder_.get()) 98 image_decoder_->set_delegate(NULL); 99 image_decoder_ = new ImageDecoder(this, raw_data, 100 ImageDecoder::DEFAULT_CODEC); 101 scoped_refptr<base::MessageLoopProxy> task_runner = 102 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 103 image_decoder_->Start(task_runner); 104 } 105 106 void UserImageScreen::CheckCameraPresence() { 107 CameraDetector::StartPresenceCheck( 108 base::Bind(&UserImageScreen::OnCameraPresenceCheckDone, 109 weak_factory_.GetWeakPtr())); 110 } 111 112 void UserImageScreen::OnCameraPresenceCheckDone() { 113 bool is_camera_present = CameraDetector::camera_presence() == 114 CameraDetector::kCameraPresent; 115 if (actor_) { 116 if (is_camera_present != was_camera_present_) { 117 actor_->SetCameraPresent(is_camera_present); 118 was_camera_present_ = is_camera_present; 119 } 120 } 121 } 122 123 void UserImageScreen::HideCurtain() { 124 if (actor_) 125 actor_->HideCurtain(); 126 } 127 128 void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder, 129 const SkBitmap& decoded_image) { 130 DCHECK_EQ(image_decoder_.get(), decoder); 131 user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image); 132 if (accept_photo_after_decoding_) 133 OnImageAccepted(); 134 } 135 136 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) { 137 NOTREACHED() << "Failed to decode PNG image from WebUI"; 138 } 139 140 void UserImageScreen::OnInitialSync(bool local_image_updated) { 141 DCHECK(sync_timer_); 142 if (!local_image_updated) { 143 sync_timer_.reset(); 144 UserManager::Get()->GetUserImageManager()->GetSyncObserver()-> 145 RemoveObserver(this); 146 if (is_screen_ready_) 147 HideCurtain(); 148 return; 149 } 150 ExitScreen(); 151 } 152 153 void UserImageScreen::OnSyncTimeout() { 154 sync_timer_.reset(); 155 UserManager::Get()->GetUserImageManager()->GetSyncObserver()-> 156 RemoveObserver(this); 157 if (is_screen_ready_) 158 HideCurtain(); 159 } 160 161 bool UserImageScreen::IsWaitingForSync() const { 162 return sync_timer_.get() && sync_timer_->IsRunning(); 163 } 164 165 void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous, 166 const base::Value* current) { 167 if (current) { 168 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, 169 policy_registrar_.release()); 170 ExitScreen(); 171 } 172 } 173 174 void UserImageScreen::OnImageSelected(const std::string& image_type, 175 const std::string& image_url, 176 bool is_user_selection) { 177 if (is_user_selection) { 178 user_has_selected_image_ = true; 179 } 180 if (image_url.empty()) 181 return; 182 int user_image_index = User::kInvalidImageIndex; 183 if (image_type == "default" && 184 IsDefaultImageUrl(image_url, &user_image_index)) { 185 selected_image_ = user_image_index; 186 } else if (image_type == "camera") { 187 selected_image_ = User::kExternalImageIndex; 188 } else if (image_type == "profile") { 189 selected_image_ = User::kProfileImageIndex; 190 } else { 191 NOTREACHED() << "Unexpected image type: " << image_type; 192 } 193 } 194 195 void UserImageScreen::OnImageAccepted() { 196 UserManager* user_manager = UserManager::Get(); 197 UserImageManager* image_manager = user_manager->GetUserImageManager(); 198 std::string user_id = GetUser()->email(); 199 int uma_index = 0; 200 switch (selected_image_) { 201 case User::kExternalImageIndex: 202 // Photo decoding may not have been finished yet. 203 if (user_photo_.isNull()) { 204 accept_photo_after_decoding_ = true; 205 return; 206 } 207 image_manager-> 208 SaveUserImage(user_id, UserImage::CreateAndEncode(user_photo_)); 209 uma_index = kHistogramImageFromCamera; 210 break; 211 case User::kProfileImageIndex: 212 image_manager->SaveUserImageFromProfileImage(user_id); 213 uma_index = kHistogramImageFromProfile; 214 break; 215 default: 216 DCHECK(selected_image_ >= 0 && selected_image_ < kDefaultImagesCount); 217 image_manager->SaveUserDefaultImageIndex(user_id, selected_image_); 218 uma_index = GetDefaultImageHistogramValue(selected_image_); 219 break; 220 } 221 if (user_has_selected_image_) { 222 UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice", 223 uma_index, 224 kHistogramImagesCount); 225 } 226 ExitScreen(); 227 } 228 229 230 void UserImageScreen::SetProfilePictureEnabled(bool profile_picture_enabled) { 231 if (profile_picture_enabled_ == profile_picture_enabled) 232 return; 233 profile_picture_enabled_ = profile_picture_enabled; 234 if (profile_picture_enabled) { 235 notification_registrar_.Add(this, 236 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 237 content::NotificationService::AllSources()); 238 notification_registrar_.Add( 239 this, 240 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 241 content::NotificationService::AllSources()); 242 } else { 243 notification_registrar_.Remove(this, 244 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 245 content::NotificationService::AllSources()); 246 notification_registrar_.Remove( 247 this, 248 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 249 content::NotificationService::AllSources()); 250 } 251 if (actor_) 252 actor_->SetProfilePictureEnabled(profile_picture_enabled); 253 } 254 255 void UserImageScreen::SetUserID(const std::string& user_id) { 256 DCHECK(!user_id.empty()); 257 user_id_ = user_id; 258 } 259 260 void UserImageScreen::PrepareToShow() { 261 if (actor_) 262 actor_->PrepareToShow(); 263 } 264 265 const User* UserImageScreen::GetUser() { 266 if (user_id_.empty()) 267 return UserManager::Get()->GetLoggedInUser(); 268 const User* user = UserManager::Get()->FindUser(user_id_); 269 DCHECK(user); 270 return user; 271 } 272 273 void UserImageScreen::Show() { 274 if (!actor_) 275 return; 276 277 DCHECK(!policy_registrar_); 278 Profile* profile = UserManager::Get()->GetProfileByUser(GetUser()); 279 if (profile) { 280 policy::PolicyService* policy_service = 281 policy::ProfilePolicyConnectorFactory::GetForProfile(profile)-> 282 policy_service(); 283 if (policy_service->GetPolicies( 284 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, 285 std::string())) 286 .Get(policy::key::kUserAvatarImage)) { 287 // If the user image is managed by policy, skip the screen because the 288 // user is not allowed to override a policy-set image. 289 ExitScreen(); 290 return; 291 } 292 293 // Listen for policy changes. If at any point, the user image becomes 294 // managed by policy, the screen will close. 295 policy_registrar_.reset(new policy::PolicyChangeRegistrar( 296 policy_service, 297 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()))); 298 policy_registrar_->Observe( 299 policy::key::kUserAvatarImage, 300 base::Bind(&UserImageScreen::OnUserImagePolicyChanged, 301 base::Unretained(this))); 302 } else { 303 NOTREACHED(); 304 } 305 306 if (GetUser()->CanSyncImage()) { 307 if (UserImageSyncObserver* sync_observer = 308 UserManager::Get()->GetUserImageManager()->GetSyncObserver()) { 309 // We have synced image already. 310 if (sync_observer->is_synced()) { 311 ExitScreen(); 312 return; 313 } 314 sync_observer->AddObserver(this); 315 sync_timer_.reset(new base::Timer( 316 FROM_HERE, 317 base::TimeDelta::FromSeconds(kSyncTimeoutSeconds), 318 base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)), 319 false)); 320 sync_timer_->Reset(); 321 } 322 } 323 actor_->Show(); 324 actor_->SetProfilePictureEnabled(profile_picture_enabled_); 325 326 selected_image_ = GetUser()->image_index(); 327 actor_->SelectImage(selected_image_); 328 329 if (profile_picture_enabled_) { 330 // Start fetching the profile image. 331 UserManager::Get()->GetUserImageManager()-> 332 DownloadProfileImage(kProfileDownloadReason); 333 } 334 } 335 336 void UserImageScreen::Hide() { 337 if (actor_) 338 actor_->Hide(); 339 } 340 341 std::string UserImageScreen::GetName() const { 342 return WizardController::kUserImageScreenName; 343 } 344 345 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) { 346 if (actor_ == actor) 347 actor_ = NULL; 348 } 349 350 void UserImageScreen::Observe(int type, 351 const content::NotificationSource& source, 352 const content::NotificationDetails& details) { 353 DCHECK(profile_picture_enabled_); 354 switch (type) { 355 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: { 356 // We've got a new profile image. 357 profile_picture_data_url_ = webui::GetBitmapDataUrl( 358 *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap()); 359 if (actor_) 360 actor_->SendProfileImage(profile_picture_data_url_); 361 break; 362 } 363 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: { 364 // User has a default profile image or fetching profile image has failed. 365 profile_picture_absent_ = true; 366 if (actor_) 367 actor_->OnProfileImageAbsent(); 368 break; 369 } 370 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: { 371 if (actor_) 372 actor_->SelectImage(GetUser()->image_index()); 373 break; 374 } 375 default: 376 NOTREACHED(); 377 } 378 } 379 380 bool UserImageScreen::profile_picture_absent() { 381 return profile_picture_absent_; 382 } 383 384 int UserImageScreen::selected_image() { 385 return selected_image_; 386 } 387 388 std::string UserImageScreen::profile_picture_data_url() { 389 return profile_picture_data_url_; 390 } 391 392 void UserImageScreen::ExitScreen() { 393 policy_registrar_.reset(); 394 sync_timer_.reset(); 395 UserImageSyncObserver* sync_observer = 396 UserManager::Get()->GetUserImageManager()->GetSyncObserver(); 397 if (sync_observer) 398 sync_observer->RemoveObserver(this); 399 get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); 400 } 401 402 } // namespace chromeos 403