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_presence_notifier.h" 21 #include "chrome/browser/chromeos/login/login_utils.h" 22 #include "chrome/browser/chromeos/login/screen_manager.h" 23 #include "chrome/browser/chromeos/login/screens/screen_observer.h" 24 #include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h" 25 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h" 26 #include "chrome/browser/chromeos/login/wizard_controller.h" 27 #include "chrome/browser/chromeos/profiles/profile_helper.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 "components/user_manager/user.h" 36 #include "components/user_manager/user_image/default_user_images.h" 37 #include "components/user_manager/user_image/user_image.h" 38 #include "components/user_manager/user_manager.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 "third_party/skia/include/core/SkBitmap.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 amount 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 // static 62 UserImageScreen* UserImageScreen::Get(ScreenManager* manager) { 63 return static_cast<UserImageScreen*>( 64 manager->GetScreen(WizardController::kUserImageScreenName)); 65 } 66 67 UserImageScreen::UserImageScreen(ScreenObserver* screen_observer, 68 UserImageScreenActor* actor) 69 : WizardScreen(screen_observer), 70 actor_(actor), 71 accept_photo_after_decoding_(false), 72 selected_image_(user_manager::User::USER_IMAGE_INVALID), 73 profile_picture_data_url_(url::kAboutBlankURL), 74 profile_picture_absent_(false), 75 is_screen_ready_(false), 76 user_has_selected_image_(false) { 77 actor_->SetDelegate(this); 78 notification_registrar_.Add(this, 79 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 80 content::NotificationService::AllSources()); 81 notification_registrar_.Add(this, 82 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 83 content::NotificationService::AllSources()); 84 notification_registrar_.Add(this, 85 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 86 content::NotificationService::AllSources()); 87 } 88 89 UserImageScreen::~UserImageScreen() { 90 CameraPresenceNotifier::GetInstance()->RemoveObserver(this); 91 if (actor_) 92 actor_->SetDelegate(NULL); 93 if (image_decoder_.get()) 94 image_decoder_->set_delegate(NULL); 95 } 96 97 void UserImageScreen::OnScreenReady() { 98 is_screen_ready_ = true; 99 if (!IsWaitingForSync()) 100 HideCurtain(); 101 } 102 103 void UserImageScreen::OnPhotoTaken(const std::string& raw_data) { 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 105 user_photo_ = gfx::ImageSkia(); 106 if (image_decoder_.get()) 107 image_decoder_->set_delegate(NULL); 108 image_decoder_ = new ImageDecoder(this, raw_data, 109 ImageDecoder::DEFAULT_CODEC); 110 scoped_refptr<base::MessageLoopProxy> task_runner = 111 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 112 image_decoder_->Start(task_runner); 113 } 114 115 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) { 116 if (actor_) 117 actor_->SetCameraPresent(is_camera_present); 118 } 119 120 void UserImageScreen::HideCurtain() { 121 if (actor_) 122 actor_->HideCurtain(); 123 } 124 125 void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder, 126 const SkBitmap& decoded_image) { 127 DCHECK_EQ(image_decoder_.get(), decoder); 128 user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image); 129 if (accept_photo_after_decoding_) 130 OnImageAccepted(); 131 } 132 133 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) { 134 NOTREACHED() << "Failed to decode PNG image from WebUI"; 135 } 136 137 void UserImageScreen::OnInitialSync(bool local_image_updated) { 138 DCHECK(sync_timer_); 139 if (!local_image_updated) { 140 sync_timer_.reset(); 141 GetSyncObserver()->RemoveObserver(this); 142 if (is_screen_ready_) 143 HideCurtain(); 144 return; 145 } 146 ExitScreen(); 147 } 148 149 void UserImageScreen::OnSyncTimeout() { 150 sync_timer_.reset(); 151 GetSyncObserver()->RemoveObserver(this); 152 if (is_screen_ready_) 153 HideCurtain(); 154 } 155 156 bool UserImageScreen::IsWaitingForSync() const { 157 return sync_timer_.get() && sync_timer_->IsRunning(); 158 } 159 160 void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous, 161 const base::Value* current) { 162 if (current) { 163 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, 164 policy_registrar_.release()); 165 ExitScreen(); 166 } 167 } 168 169 void UserImageScreen::OnImageSelected(const std::string& image_type, 170 const std::string& image_url, 171 bool is_user_selection) { 172 if (is_user_selection) { 173 user_has_selected_image_ = true; 174 } 175 if (image_url.empty()) 176 return; 177 int user_image_index = user_manager::User::USER_IMAGE_INVALID; 178 if (image_type == "default" && 179 user_manager::IsDefaultImageUrl(image_url, &user_image_index)) { 180 selected_image_ = user_image_index; 181 } else if (image_type == "camera") { 182 selected_image_ = user_manager::User::USER_IMAGE_EXTERNAL; 183 } else if (image_type == "profile") { 184 selected_image_ = user_manager::User::USER_IMAGE_PROFILE; 185 } else { 186 NOTREACHED() << "Unexpected image type: " << image_type; 187 } 188 } 189 190 void UserImageScreen::OnImageAccepted() { 191 UserImageManager* image_manager = GetUserImageManager(); 192 int uma_index = 0; 193 switch (selected_image_) { 194 case user_manager::User::USER_IMAGE_EXTERNAL: 195 // Photo decoding may not have been finished yet. 196 if (user_photo_.isNull()) { 197 accept_photo_after_decoding_ = true; 198 return; 199 } 200 image_manager->SaveUserImage( 201 user_manager::UserImage::CreateAndEncode(user_photo_)); 202 uma_index = user_manager::kHistogramImageFromCamera; 203 break; 204 case user_manager::User::USER_IMAGE_PROFILE: 205 image_manager->SaveUserImageFromProfileImage(); 206 uma_index = user_manager::kHistogramImageFromProfile; 207 break; 208 default: 209 DCHECK(selected_image_ >= 0 && 210 selected_image_ < user_manager::kDefaultImagesCount); 211 image_manager->SaveUserDefaultImageIndex(selected_image_); 212 uma_index = user_manager::GetDefaultImageHistogramValue(selected_image_); 213 break; 214 } 215 if (user_has_selected_image_) { 216 UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice", 217 uma_index, 218 user_manager::kHistogramImagesCount); 219 } 220 ExitScreen(); 221 } 222 223 224 void UserImageScreen::PrepareToShow() { 225 if (actor_) 226 actor_->PrepareToShow(); 227 } 228 229 const user_manager::User* UserImageScreen::GetUser() { 230 return user_manager::UserManager::Get()->GetLoggedInUser(); 231 } 232 233 UserImageManager* UserImageScreen::GetUserImageManager() { 234 return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email()); 235 } 236 237 UserImageSyncObserver* UserImageScreen::GetSyncObserver() { 238 return GetUserImageManager()->GetSyncObserver(); 239 } 240 241 void UserImageScreen::Show() { 242 if (!actor_) 243 return; 244 245 DCHECK(!policy_registrar_); 246 if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(GetUser())) { 247 policy::PolicyService* policy_service = 248 policy::ProfilePolicyConnectorFactory::GetForProfile(profile)-> 249 policy_service(); 250 if (policy_service->GetPolicies( 251 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, 252 std::string())) 253 .Get(policy::key::kUserAvatarImage)) { 254 // If the user image is managed by policy, skip the screen because the 255 // user is not allowed to override a policy-set image. 256 ExitScreen(); 257 return; 258 } 259 260 // Listen for policy changes. If at any point, the user image becomes 261 // managed by policy, the screen will close. 262 policy_registrar_.reset(new policy::PolicyChangeRegistrar( 263 policy_service, 264 policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()))); 265 policy_registrar_->Observe( 266 policy::key::kUserAvatarImage, 267 base::Bind(&UserImageScreen::OnUserImagePolicyChanged, 268 base::Unretained(this))); 269 } else { 270 NOTREACHED(); 271 } 272 273 if (GetUser()->CanSyncImage()) { 274 if (UserImageSyncObserver* sync_observer = GetSyncObserver()) { 275 // We have synced image already. 276 if (sync_observer->is_synced()) { 277 ExitScreen(); 278 return; 279 } 280 sync_observer->AddObserver(this); 281 sync_timer_.reset(new base::Timer( 282 FROM_HERE, 283 base::TimeDelta::FromSeconds(kSyncTimeoutSeconds), 284 base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)), 285 false)); 286 sync_timer_->Reset(); 287 } 288 } 289 CameraPresenceNotifier::GetInstance()->AddObserver(this); 290 actor_->Show(); 291 292 selected_image_ = GetUser()->image_index(); 293 actor_->SelectImage(selected_image_); 294 295 // Start fetching the profile image. 296 GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason); 297 } 298 299 void UserImageScreen::Hide() { 300 CameraPresenceNotifier::GetInstance()->RemoveObserver(this); 301 notification_registrar_.RemoveAll(); 302 if (actor_) 303 actor_->Hide(); 304 } 305 306 std::string UserImageScreen::GetName() const { 307 return WizardController::kUserImageScreenName; 308 } 309 310 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) { 311 if (actor_ == actor) 312 actor_ = NULL; 313 } 314 315 void UserImageScreen::Observe(int type, 316 const content::NotificationSource& source, 317 const content::NotificationDetails& details) { 318 switch (type) { 319 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: { 320 // We've got a new profile image. 321 profile_picture_data_url_ = webui::GetBitmapDataUrl( 322 *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap()); 323 if (actor_) 324 actor_->SendProfileImage(profile_picture_data_url_); 325 break; 326 } 327 case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: { 328 // User has a default profile image or fetching profile image has failed. 329 profile_picture_absent_ = true; 330 if (actor_) 331 actor_->OnProfileImageAbsent(); 332 break; 333 } 334 case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: { 335 if (actor_) 336 actor_->SelectImage(GetUser()->image_index()); 337 break; 338 } 339 default: 340 NOTREACHED(); 341 } 342 } 343 344 bool UserImageScreen::profile_picture_absent() { 345 return profile_picture_absent_; 346 } 347 348 int UserImageScreen::selected_image() { 349 return selected_image_; 350 } 351 352 std::string UserImageScreen::profile_picture_data_url() { 353 return profile_picture_data_url_; 354 } 355 356 void UserImageScreen::ExitScreen() { 357 policy_registrar_.reset(); 358 sync_timer_.reset(); 359 if (UserImageSyncObserver* sync_observer = GetSyncObserver()) 360 sync_observer->RemoveObserver(this); 361 get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED); 362 } 363 364 } // namespace chromeos 365