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/screen_locker.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "ash/ash_switches.h" 11 #include "ash/desktop_background/desktop_background_controller.h" 12 #include "ash/shell.h" 13 #include "ash/wm/lock_state_controller.h" 14 #include "ash/wm/window_state.h" 15 #include "ash/wm/window_util.h" 16 #include "base/bind.h" 17 #include "base/command_line.h" 18 #include "base/lazy_instance.h" 19 #include "base/memory/weak_ptr.h" 20 #include "base/message_loop/message_loop.h" 21 #include "base/metrics/histogram.h" 22 #include "base/strings/string_util.h" 23 #include "base/timer/timer.h" 24 #include "chrome/browser/chrome_notification_types.h" 25 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 26 #include "chrome/browser/chromeos/login/authenticator.h" 27 #include "chrome/browser/chromeos/login/login_performer.h" 28 #include "chrome/browser/chromeos/login/login_utils.h" 29 #include "chrome/browser/chromeos/login/user_adding_screen.h" 30 #include "chrome/browser/chromeos/login/user_manager.h" 31 #include "chrome/browser/chromeos/login/webui_screen_locker.h" 32 #include "chrome/browser/lifetime/application_lifetime.h" 33 #include "chrome/browser/profiles/profile.h" 34 #include "chrome/browser/profiles/profile_manager.h" 35 #include "chrome/browser/signin/signin_manager.h" 36 #include "chrome/browser/signin/signin_manager_factory.h" 37 #include "chrome/browser/sync/profile_sync_service.h" 38 #include "chrome/browser/sync/profile_sync_service_factory.h" 39 #include "chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h" 40 #include "chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h" 41 #include "chrome/common/chrome_switches.h" 42 #include "chromeos/audio/chromeos_sounds.h" 43 #include "chromeos/dbus/dbus_thread_manager.h" 44 #include "chromeos/dbus/session_manager_client.h" 45 #include "content/public/browser/browser_thread.h" 46 #include "content/public/browser/notification_service.h" 47 #include "content/public/browser/url_data_source.h" 48 #include "content/public/browser/user_metrics.h" 49 #include "grit/browser_resources.h" 50 #include "grit/generated_resources.h" 51 #include "media/audio/sounds/sounds_manager.h" 52 #include "ui/base/l10n/l10n_util.h" 53 #include "ui/base/resource/resource_bundle.h" 54 #include "ui/gfx/image/image.h" 55 #include "url/gurl.h" 56 57 using content::BrowserThread; 58 using content::UserMetricsAction; 59 60 namespace { 61 62 // Timeout for unlock animation guard - some animations may be required to run 63 // on successful authentication before unlocking, but we want to be sure that 64 // unlock happens even if animations are broken. 65 const int kUnlockGuardTimeoutMs = 400; 66 67 // Observer to start ScreenLocker when the screen lock 68 class ScreenLockObserver : public chromeos::SessionManagerClient::Observer, 69 public content::NotificationObserver, 70 public chromeos::UserAddingScreen::Observer { 71 public: 72 ScreenLockObserver() : session_started_(false) { 73 registrar_.Add(this, 74 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 75 content::NotificationService::AllSources()); 76 registrar_.Add(this, 77 chrome::NOTIFICATION_SESSION_STARTED, 78 content::NotificationService::AllSources()); 79 } 80 81 // NotificationObserver overrides: 82 virtual void Observe(int type, 83 const content::NotificationSource& source, 84 const content::NotificationDetails& details) OVERRIDE { 85 switch (type) { 86 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: { 87 // Register Screen Lock only after a user has logged in. 88 chromeos::SessionManagerClient* session_manager = 89 chromeos::DBusThreadManager::Get()->GetSessionManagerClient(); 90 if (!session_manager->HasObserver(this)) 91 session_manager->AddObserver(this); 92 break; 93 } 94 95 case chrome::NOTIFICATION_SESSION_STARTED: { 96 session_started_ = true; 97 break; 98 } 99 100 default: 101 NOTREACHED(); 102 } 103 } 104 105 virtual void LockScreen() OVERRIDE { 106 VLOG(1) << "Received LockScreen D-Bus signal from session manager"; 107 if (chromeos::UserAddingScreen::Get()->IsRunning()) { 108 VLOG(1) << "Waiting for user adding screen to stop"; 109 chromeos::UserAddingScreen::Get()->AddObserver(this); 110 chromeos::UserAddingScreen::Get()->Cancel(); 111 return; 112 } 113 if (session_started_ && 114 chromeos::UserManager::Get()->CanCurrentUserLock()) { 115 chromeos::ScreenLocker::Show(); 116 } else { 117 // If the current user's session cannot be locked or the user has not 118 // completed all sign-in steps yet, log out instead. The latter is done to 119 // avoid complications with displaying the lock screen over the login 120 // screen while remaining secure in the case the user walks away during 121 // the sign-in steps. See crbug.com/112225 and crbug.com/110933. 122 VLOG(1) << "Calling session manager's StopSession D-Bus method"; 123 chromeos::DBusThreadManager::Get()-> 124 GetSessionManagerClient()->StopSession(); 125 } 126 } 127 128 virtual void OnUserAddingFinished() OVERRIDE { 129 chromeos::UserAddingScreen::Get()->RemoveObserver(this); 130 LockScreen(); 131 } 132 133 private: 134 bool session_started_; 135 content::NotificationRegistrar registrar_; 136 std::string saved_previous_input_method_id_; 137 std::string saved_current_input_method_id_; 138 std::vector<std::string> saved_active_input_method_list_; 139 140 DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver); 141 }; 142 143 void PlaySound(int sound_key) { 144 if (chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) 145 media::SoundsManager::Get()->Play(sound_key); 146 } 147 148 static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer = 149 LAZY_INSTANCE_INITIALIZER; 150 151 } // namespace 152 153 namespace chromeos { 154 155 // static 156 ScreenLocker* ScreenLocker::screen_locker_ = NULL; 157 158 ////////////////////////////////////////////////////////////////////////////// 159 // ScreenLocker, public: 160 161 ScreenLocker::ScreenLocker(const UserList& users) 162 : users_(users), 163 locked_(false), 164 start_time_(base::Time::Now()), 165 login_status_consumer_(NULL), 166 incorrect_passwords_count_(0), 167 weak_factory_(this) { 168 DCHECK(!screen_locker_); 169 screen_locker_ = this; 170 171 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 172 media::SoundsManager* manager = media::SoundsManager::Get(); 173 manager->Initialize(SOUND_LOCK, 174 bundle.GetRawDataResource(IDR_SOUND_LOCK_WAV)); 175 manager->Initialize(SOUND_UNLOCK, 176 bundle.GetRawDataResource(IDR_SOUND_UNLOCK_WAV)); 177 178 ash::Shell::GetInstance()-> 179 lock_state_controller()-> 180 SetLockScreenDisplayedCallback( 181 base::Bind(&PlaySound, static_cast<int>(chromeos::SOUND_LOCK))); 182 } 183 184 void ScreenLocker::Init() { 185 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this); 186 delegate_.reset(new WebUIScreenLocker(this)); 187 delegate_->LockScreen(); 188 189 // Ownership of |icon_image_source| is passed. 190 screenlock_icon_provider_.reset(new ScreenlockIconProvider); 191 ScreenlockIconSource* screenlock_icon_source = 192 new ScreenlockIconSource(screenlock_icon_provider_->AsWeakPtr()); 193 content::URLDataSource::Add( 194 Profile::FromWebUI(GetAssociatedWebUI()), 195 screenlock_icon_source); 196 } 197 198 void ScreenLocker::OnLoginFailure(const LoginFailure& error) { 199 content::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure")); 200 if (authentication_start_time_.is_null()) { 201 LOG(ERROR) << "Start time is not set at authentication failure"; 202 } else { 203 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 204 VLOG(1) << "Authentication failure: " << delta.InSecondsF() << " second(s)"; 205 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta); 206 } 207 208 EnableInput(); 209 // Don't enable signout button here as we're showing 210 // MessageBubble. 211 212 delegate_->ShowErrorMessage(incorrect_passwords_count_++ ? 213 IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME : 214 IDS_LOGIN_ERROR_AUTHENTICATING, 215 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); 216 217 if (login_status_consumer_) 218 login_status_consumer_->OnLoginFailure(error); 219 } 220 221 void ScreenLocker::OnLoginSuccess(const UserContext& user_context) { 222 incorrect_passwords_count_ = 0; 223 if (authentication_start_time_.is_null()) { 224 if (!user_context.username.empty()) 225 LOG(ERROR) << "Start time is not set at authentication success"; 226 } else { 227 base::TimeDelta delta = base::Time::Now() - authentication_start_time_; 228 VLOG(1) << "Authentication success: " << delta.InSecondsF() << " second(s)"; 229 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta); 230 } 231 232 if (const User* user = UserManager::Get()->FindUser(user_context.username)) { 233 if (!user->is_active()) 234 UserManager::Get()->SwitchActiveUser(user_context.username); 235 } else { 236 NOTREACHED() << "Logged in user not found."; 237 } 238 239 authentication_capture_.reset(new AuthenticationParametersCapture()); 240 authentication_capture_->user_context = user_context; 241 242 // Add guard for case when something get broken in call chain to unlock 243 // for sure. 244 base::MessageLoop::current()->PostDelayedTask( 245 FROM_HERE, 246 base::Bind(&ScreenLocker::UnlockOnLoginSuccess, 247 weak_factory_.GetWeakPtr()), 248 base::TimeDelta::FromMilliseconds(kUnlockGuardTimeoutMs)); 249 delegate_->AnimateAuthenticationSuccess(); 250 } 251 252 void ScreenLocker::UnlockOnLoginSuccess() { 253 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 254 if (!authentication_capture_.get()) { 255 LOG(WARNING) << "Call to UnlockOnLoginSuccess without previous " << 256 "authentication success."; 257 return; 258 } 259 260 if (login_status_consumer_) { 261 login_status_consumer_->OnLoginSuccess( 262 UserContext(authentication_capture_->user_context.username, 263 authentication_capture_->user_context.password, 264 authentication_capture_->user_context.auth_code, 265 authentication_capture_->user_context.username_hash, 266 authentication_capture_->user_context.using_oauth)); 267 } 268 authentication_capture_.reset(); 269 weak_factory_.InvalidateWeakPtrs(); 270 271 VLOG(1) << "Hiding the lock screen."; 272 chromeos::ScreenLocker::Hide(); 273 } 274 275 void ScreenLocker::Authenticate(const UserContext& user_context) { 276 LOG_ASSERT(IsUserLoggedIn(user_context.username)) 277 << "Invalid user trying to unlock."; 278 authentication_start_time_ = base::Time::Now(); 279 delegate_->SetInputEnabled(false); 280 delegate_->OnAuthenticate(); 281 282 BrowserThread::PostTask( 283 BrowserThread::UI, FROM_HERE, 284 base::Bind(&Authenticator::AuthenticateToUnlock, 285 authenticator_.get(), 286 user_context)); 287 } 288 289 void ScreenLocker::AuthenticateByPassword(const std::string& password) { 290 LOG_ASSERT(users_.size() == 1); 291 Authenticate(UserContext(users_[0]->email(), password, "")); 292 } 293 294 void ScreenLocker::ClearErrors() { 295 delegate_->ClearErrors(); 296 } 297 298 void ScreenLocker::EnableInput() { 299 delegate_->SetInputEnabled(true); 300 } 301 302 void ScreenLocker::Signout() { 303 delegate_->ClearErrors(); 304 content::RecordAction(UserMetricsAction("ScreenLocker_Signout")); 305 // We expect that this call will not wait for any user input. 306 // If it changes at some point, we will need to force exit. 307 chrome::AttemptUserExit(); 308 309 // Don't hide yet the locker because the chrome screen may become visible 310 // briefly. 311 } 312 313 void ScreenLocker::ShowBannerMessage(const std::string& message) { 314 delegate_->ShowBannerMessage(message); 315 } 316 317 void ScreenLocker::ShowUserPodButton(const std::string& username, 318 const gfx::Image& icon, 319 const base::Closure& click_callback) { 320 if (!locked_) 321 return; 322 323 screenlock_icon_provider_->AddIcon(username, icon); 324 delegate_->ShowUserPodButton( 325 username, 326 ScreenlockIconSource::GetIconURLForUser(username), 327 click_callback); 328 } 329 330 void ScreenLocker::ShowErrorMessage(int error_msg_id, 331 HelpAppLauncher::HelpTopic help_topic_id, 332 bool sign_out_only) { 333 delegate_->SetInputEnabled(!sign_out_only); 334 delegate_->ShowErrorMessage(error_msg_id, help_topic_id); 335 } 336 337 void ScreenLocker::SetLoginStatusConsumer( 338 chromeos::LoginStatusConsumer* consumer) { 339 login_status_consumer_ = consumer; 340 } 341 342 // static 343 void ScreenLocker::Show() { 344 content::RecordAction(UserMetricsAction("ScreenLocker_Show")); 345 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 346 347 // Check whether the currently logged in user is a guest account and if so, 348 // refuse to lock the screen (crosbug.com/23764). 349 // For a demo user, we should never show the lock screen (crosbug.com/27647). 350 if (UserManager::Get()->IsLoggedInAsGuest() || 351 UserManager::Get()->IsLoggedInAsDemoUser()) { 352 VLOG(1) << "Refusing to lock screen for guest/demo account"; 353 return; 354 } 355 356 // If the active window is fullscreen, exit fullscreen to avoid the web page 357 // or app mimicking the lock screen. Do not exit fullscreen if the shelf is 358 // visible while in fullscreen because the shelf makes it harder for a web 359 // page or app to mimick the lock screen. 360 ash::wm::WindowState* active_window_state = ash::wm::GetActiveWindowState(); 361 if (active_window_state && 362 active_window_state->IsFullscreen() && 363 active_window_state->hide_shelf_when_fullscreen()) { 364 active_window_state->ToggleFullscreen(); 365 } 366 367 if (!screen_locker_) { 368 ScreenLocker* locker = 369 new ScreenLocker(UserManager::Get()->GetUnlockUsers()); 370 VLOG(1) << "Created ScreenLocker " << locker; 371 locker->Init(); 372 } else { 373 VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; " 374 << " calling session manager's HandleLockScreenShown D-Bus method"; 375 DBusThreadManager::Get()->GetSessionManagerClient()-> 376 NotifyLockScreenShown(); 377 } 378 } 379 380 // static 381 void ScreenLocker::Hide() { 382 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 383 // For a guest/demo user, screen_locker_ would have never been initialized. 384 if (UserManager::Get()->IsLoggedInAsGuest() || 385 UserManager::Get()->IsLoggedInAsDemoUser()) { 386 VLOG(1) << "Refusing to hide lock screen for guest/demo account"; 387 return; 388 } 389 390 DCHECK(screen_locker_); 391 base::Callback<void(void)> callback = 392 base::Bind(&ScreenLocker::ScheduleDeletion); 393 ash::Shell::GetInstance()->lock_state_controller()-> 394 OnLockScreenHide(callback); 395 } 396 397 void ScreenLocker::ScheduleDeletion() { 398 // Avoid possible multiple calls. 399 if (screen_locker_ == NULL) 400 return; 401 VLOG(1) << "Deleting ScreenLocker " << screen_locker_; 402 403 PlaySound(SOUND_UNLOCK); 404 405 delete screen_locker_; 406 screen_locker_ = NULL; 407 } 408 409 // static 410 void ScreenLocker::InitClass() { 411 g_screen_lock_observer.Get(); 412 } 413 414 //////////////////////////////////////////////////////////////////////////////// 415 // ScreenLocker, private: 416 417 ScreenLocker::~ScreenLocker() { 418 VLOG(1) << "Destroying ScreenLocker " << this; 419 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI); 420 421 if (authenticator_.get()) 422 authenticator_->SetConsumer(NULL); 423 ClearErrors(); 424 425 VLOG(1) << "Moving desktop background to unlocked container"; 426 ash::Shell::GetInstance()-> 427 desktop_background_controller()->MoveDesktopToUnlockedContainer(); 428 429 screen_locker_ = NULL; 430 bool state = false; 431 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 432 content::NotificationService::current()->Notify( 433 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 434 content::Source<ScreenLocker>(this), 435 content::Details<bool>(&state)); 436 VLOG(1) << "Calling session manager's HandleLockScreenDismissed D-Bus method"; 437 DBusThreadManager::Get()->GetSessionManagerClient()-> 438 NotifyLockScreenDismissed(); 439 } 440 441 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) { 442 authenticator_ = authenticator; 443 } 444 445 void ScreenLocker::ScreenLockReady() { 446 locked_ = true; 447 base::TimeDelta delta = base::Time::Now() - start_time_; 448 VLOG(1) << "ScreenLocker " << this << " is ready after " 449 << delta.InSecondsF() << " second(s)"; 450 UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); 451 452 VLOG(1) << "Moving desktop background to locked container"; 453 ash::Shell::GetInstance()-> 454 desktop_background_controller()->MoveDesktopToLockedContainer(); 455 456 bool state = true; 457 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; 458 content::NotificationService::current()->Notify( 459 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, 460 content::Source<ScreenLocker>(this), 461 content::Details<bool>(&state)); 462 VLOG(1) << "Calling session manager's HandleLockScreenShown D-Bus method"; 463 DBusThreadManager::Get()->GetSessionManagerClient()->NotifyLockScreenShown(); 464 } 465 466 content::WebUI* ScreenLocker::GetAssociatedWebUI() { 467 return delegate_->GetAssociatedWebUI(); 468 } 469 470 bool ScreenLocker::IsUserLoggedIn(const std::string& username) { 471 for (UserList::const_iterator it = users_.begin(); it != users_.end(); ++it) { 472 if ((*it)->email() == username) 473 return true; 474 } 475 return false; 476 } 477 478 } // namespace chromeos 479