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.
5 #include "chrome/browser/chromeos/login/screen_locker.h"
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 "base/bind.h"
15 #include "base/command_line.h"
16 #include "base/lazy_instance.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/metrics/histogram.h"
20 #include "base/strings/string_util.h"
21 #include "base/timer/timer.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
24 #include "chrome/browser/chromeos/login/authenticator.h"
25 #include "chrome/browser/chromeos/login/login_performer.h"
26 #include "chrome/browser/chromeos/login/login_utils.h"
27 #include "chrome/browser/chromeos/login/user_adding_screen.h"
28 #include "chrome/browser/chromeos/login/user_manager.h"
29 #include "chrome/browser/chromeos/login/webui_screen_locker.h"
30 #include "chrome/browser/lifetime/application_lifetime.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/profiles/profile_manager.h"
33 #include "chrome/browser/signin/signin_manager.h"
34 #include "chrome/browser/signin/signin_manager_factory.h"
35 #include "chrome/browser/sync/profile_sync_service.h"
36 #include "chrome/browser/sync/profile_sync_service_factory.h"
37 #include "chrome/browser/ui/browser.h"
38 #include "chrome/browser/ui/browser_commands.h"
39 #include "chrome/browser/ui/browser_finder.h"
40 #include "chrome/browser/ui/browser_window.h"
41 #include "chrome/common/chrome_switches.h"
42 #include "chromeos/dbus/dbus_thread_manager.h"
43 #include "chromeos/dbus/session_manager_client.h"
44 #include "content/public/browser/browser_thread.h"
45 #include "content/public/browser/notification_service.h"
46 #include "content/public/browser/user_metrics.h"
47 #include "grit/generated_resources.h"
48 #include "media/audio/sounds/sounds_manager.h"
49 #include "ui/base/l10n/l10n_util.h"
52 using content::BrowserThread;
53 using content::UserMetricsAction;
57 // Timeout for unlock animation guard - some animations may be required to run
58 // on successful authentication before unlocking, but we want to be sure that
59 // unlock happens even if animations are broken.
60 const int kUnlockGuardTimeoutMs = 400;
62 // Observer to start ScreenLocker when the screen lock
63 class ScreenLockObserver : public chromeos::SessionManagerClient::Observer,
64 public content::NotificationObserver,
65 public chromeos::UserAddingScreen::Observer {
67 ScreenLockObserver() : session_started_(false) {
69 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
70 content::NotificationService::AllSources());
72 chrome::NOTIFICATION_SESSION_STARTED,
73 content::NotificationService::AllSources());
76 // NotificationObserver overrides:
77 virtual void Observe(int type,
78 const content::NotificationSource& source,
79 const content::NotificationDetails& details) OVERRIDE {
81 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
82 // Register Screen Lock only after a user has logged in.
83 chromeos::SessionManagerClient* session_manager =
84 chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
85 if (!session_manager->HasObserver(this))
86 session_manager->AddObserver(this);
90 case chrome::NOTIFICATION_SESSION_STARTED: {
91 session_started_ = true;
100 virtual void LockScreen() OVERRIDE {
101 VLOG(1) << "Received LockScreen D-Bus signal from session manager";
102 if (chromeos::UserAddingScreen::Get()->IsRunning()) {
103 VLOG(1) << "Waiting for user adding screen to stop";
104 chromeos::UserAddingScreen::Get()->AddObserver(this);
105 chromeos::UserAddingScreen::Get()->Cancel();
108 if (session_started_ &&
109 chromeos::UserManager::Get()->CanCurrentUserLock()) {
110 chromeos::ScreenLocker::Show();
112 // If the current user's session cannot be locked or the user has not
113 // completed all sign-in steps yet, log out instead. The latter is done to
114 // avoid complications with displaying the lock screen over the login
115 // screen while remaining secure in the case the user walks away during
116 // the sign-in steps. See crbug.com/112225 and crbug.com/110933.
117 VLOG(1) << "Calling session manager's StopSession D-Bus method";
118 chromeos::DBusThreadManager::Get()->
119 GetSessionManagerClient()->StopSession();
123 virtual void OnUserAddingFinished() OVERRIDE {
124 chromeos::UserAddingScreen::Get()->RemoveObserver(this);
129 bool session_started_;
130 content::NotificationRegistrar registrar_;
131 std::string saved_previous_input_method_id_;
132 std::string saved_current_input_method_id_;
133 std::vector<std::string> saved_active_input_method_list_;
135 DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver);
138 void PlaySound(media::SoundsManager::Sound sound) {
139 if (chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled())
140 media::SoundsManager::Get()->Play(sound);
143 static base::LazyInstance<ScreenLockObserver> g_screen_lock_observer =
144 LAZY_INSTANCE_INITIALIZER;
151 ScreenLocker* ScreenLocker::screen_locker_ = NULL;
153 //////////////////////////////////////////////////////////////////////////////
154 // ScreenLocker, public:
156 ScreenLocker::ScreenLocker(const UserList& users)
159 start_time_(base::Time::Now()),
160 login_status_consumer_(NULL),
161 incorrect_passwords_count_(0),
162 weak_factory_(this) {
163 DCHECK(!screen_locker_);
164 screen_locker_ = this;
166 ash::Shell::GetInstance()->lock_state_controller()->
167 SetLockScreenDisplayedCallback(
168 base::Bind(&PlaySound, media::SoundsManager::SOUND_LOCK));
171 void ScreenLocker::Init() {
172 authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
173 delegate_.reset(new WebUIScreenLocker(this));
174 delegate_->LockScreen();
177 void ScreenLocker::OnLoginFailure(const LoginFailure& error) {
178 content::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure"));
179 if (authentication_start_time_.is_null()) {
180 LOG(ERROR) << "Start time is not set at authentication failure";
182 base::TimeDelta delta = base::Time::Now() - authentication_start_time_;
183 VLOG(1) << "Authentication failure: " << delta.InSecondsF() << " second(s)";
184 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta);
188 // Don't enable signout button here as we're showing
191 delegate_->ShowErrorMessage(incorrect_passwords_count_++ ?
192 IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME :
193 IDS_LOGIN_ERROR_AUTHENTICATING,
194 HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
196 if (login_status_consumer_)
197 login_status_consumer_->OnLoginFailure(error);
200 void ScreenLocker::OnLoginSuccess(const UserContext& user_context) {
201 incorrect_passwords_count_ = 0;
202 if (authentication_start_time_.is_null()) {
203 if (!user_context.username.empty())
204 LOG(ERROR) << "Start time is not set at authentication success";
206 base::TimeDelta delta = base::Time::Now() - authentication_start_time_;
207 VLOG(1) << "Authentication success: " << delta.InSecondsF() << " second(s)";
208 UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta);
211 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kMultiProfiles)) {
212 // TODO(dzhioev): It seems like this branch never executed and should be
213 // removed before multi-profile enabling.
214 Profile* profile = ProfileManager::GetDefaultProfile();
215 if (profile && !user_context.password.empty()) {
216 // We have a non-empty password, so notify listeners (such as the sync
218 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
220 GoogleServiceSigninSuccessDetails details(
221 signin->GetAuthenticatedUsername(),
222 user_context.password);
223 content::NotificationService::current()->Notify(
224 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
225 content::Source<Profile>(profile),
226 content::Details<const GoogleServiceSigninSuccessDetails>(&details));
230 if (const User* user = UserManager::Get()->FindUser(user_context.username)) {
231 if (!user->is_active())
232 UserManager::Get()->SwitchActiveUser(user_context.username);
234 NOTREACHED() << "Logged in user not found.";
237 authentication_capture_.reset(new AuthenticationParametersCapture());
238 authentication_capture_->user_context = user_context;
240 // Add guard for case when something get broken in call chain to unlock
242 base::MessageLoop::current()->PostDelayedTask(
244 base::Bind(&ScreenLocker::UnlockOnLoginSuccess,
245 weak_factory_.GetWeakPtr()),
246 base::TimeDelta::FromMilliseconds(kUnlockGuardTimeoutMs));
247 delegate_->AnimateAuthenticationSuccess();
250 void ScreenLocker::UnlockOnLoginSuccess() {
251 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
252 if (!authentication_capture_.get()) {
253 LOG(WARNING) << "Call to UnlockOnLoginSuccess without previous " <<
254 "authentication success.";
258 if (login_status_consumer_) {
259 login_status_consumer_->OnLoginSuccess(
260 UserContext(authentication_capture_->user_context.username,
261 authentication_capture_->user_context.password,
262 authentication_capture_->user_context.auth_code,
263 authentication_capture_->user_context.username_hash,
264 authentication_capture_->user_context.using_oauth));
266 authentication_capture_.reset();
267 weak_factory_.InvalidateWeakPtrs();
269 VLOG(1) << "Hiding the lock screen.";
270 chromeos::ScreenLocker::Hide();
273 void ScreenLocker::Authenticate(const UserContext& user_context) {
274 LOG_ASSERT(IsUserLoggedIn(user_context.username))
275 << "Invalid user trying to unlock.";
276 authentication_start_time_ = base::Time::Now();
277 delegate_->SetInputEnabled(false);
278 delegate_->OnAuthenticate();
280 BrowserThread::PostTask(
281 BrowserThread::UI, FROM_HERE,
282 base::Bind(&Authenticator::AuthenticateToUnlock,
283 authenticator_.get(),
287 void ScreenLocker::AuthenticateByPassword(const std::string& password) {
288 LOG_ASSERT(users_.size() == 1);
289 Authenticate(UserContext(users_[0]->email(), password, ""));
292 void ScreenLocker::ClearErrors() {
293 delegate_->ClearErrors();
296 void ScreenLocker::EnableInput() {
297 delegate_->SetInputEnabled(true);
300 void ScreenLocker::Signout() {
301 delegate_->ClearErrors();
302 content::RecordAction(UserMetricsAction("ScreenLocker_Signout"));
303 // We expect that this call will not wait for any user input.
304 // If it changes at some point, we will need to force exit.
305 chrome::AttemptUserExit();
307 // Don't hide yet the locker because the chrome screen may become visible
311 void ScreenLocker::ShowErrorMessage(int error_msg_id,
312 HelpAppLauncher::HelpTopic help_topic_id,
313 bool sign_out_only) {
314 delegate_->SetInputEnabled(!sign_out_only);
315 delegate_->ShowErrorMessage(error_msg_id, help_topic_id);
318 void ScreenLocker::SetLoginStatusConsumer(
319 chromeos::LoginStatusConsumer* consumer) {
320 login_status_consumer_ = consumer;
324 void ScreenLocker::Show() {
325 content::RecordAction(UserMetricsAction("ScreenLocker_Show"));
326 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
328 // Check whether the currently logged in user is a guest account and if so,
329 // refuse to lock the screen (crosbug.com/23764).
330 // For a demo user, we should never show the lock screen (crosbug.com/27647).
331 if (UserManager::Get()->IsLoggedInAsGuest() ||
332 UserManager::Get()->IsLoggedInAsDemoUser()) {
333 VLOG(1) << "Refusing to lock screen for guest/demo account";
338 Browser* browser = chrome::FindLastActiveWithHostDesktopType(
339 chrome::HOST_DESKTOP_TYPE_ASH);
340 // browser can be NULL if we receive a lock request before the first browser
342 if (browser && browser->window()->IsFullscreen()) {
343 chrome::ToggleFullscreenMode(browser);
346 if (!screen_locker_) {
347 ScreenLocker* locker =
348 new ScreenLocker(UserManager::Get()->GetUnlockUsers());
349 VLOG(1) << "Created ScreenLocker " << locker;
352 VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; "
353 << " calling session manager's HandleLockScreenShown D-Bus method";
354 DBusThreadManager::Get()->GetSessionManagerClient()->
355 NotifyLockScreenShown();
360 void ScreenLocker::Hide() {
361 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
362 // For a guest/demo user, screen_locker_ would have never been initialized.
363 if (UserManager::Get()->IsLoggedInAsGuest() ||
364 UserManager::Get()->IsLoggedInAsDemoUser()) {
365 VLOG(1) << "Refusing to hide lock screen for guest/demo account";
369 DCHECK(screen_locker_);
370 base::Callback<void(void)> callback =
371 base::Bind(&ScreenLocker::ScheduleDeletion);
372 ash::Shell::GetInstance()->lock_state_controller()->
373 OnLockScreenHide(callback);
376 void ScreenLocker::ScheduleDeletion() {
377 // Avoid possible multiple calls.
378 if (screen_locker_ == NULL)
380 VLOG(1) << "Deleting ScreenLocker " << screen_locker_;
382 PlaySound(media::SoundsManager::SOUND_UNLOCK);
384 delete screen_locker_;
385 screen_locker_ = NULL;
389 void ScreenLocker::InitClass() {
390 g_screen_lock_observer.Get();
393 ////////////////////////////////////////////////////////////////////////////////
394 // ScreenLocker, private:
396 ScreenLocker::~ScreenLocker() {
397 VLOG(1) << "Destroying ScreenLocker " << this;
398 DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
400 if (authenticator_.get())
401 authenticator_->SetConsumer(NULL);
404 VLOG(1) << "Moving desktop background to unlocked container";
405 ash::Shell::GetInstance()->
406 desktop_background_controller()->MoveDesktopToUnlockedContainer();
408 screen_locker_ = NULL;
410 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state;
411 content::NotificationService::current()->Notify(
412 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
413 content::Source<ScreenLocker>(this),
414 content::Details<bool>(&state));
415 VLOG(1) << "Calling session manager's HandleLockScreenDismissed D-Bus method";
416 DBusThreadManager::Get()->GetSessionManagerClient()->
417 NotifyLockScreenDismissed();
420 void ScreenLocker::SetAuthenticator(Authenticator* authenticator) {
421 authenticator_ = authenticator;
424 void ScreenLocker::ScreenLockReady() {
426 base::TimeDelta delta = base::Time::Now() - start_time_;
427 VLOG(1) << "ScreenLocker " << this << " is ready after "
428 << delta.InSecondsF() << " second(s)";
429 UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta);
431 VLOG(1) << "Moving desktop background to locked container";
432 ash::Shell::GetInstance()->
433 desktop_background_controller()->MoveDesktopToLockedContainer();
436 VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state;
437 content::NotificationService::current()->Notify(
438 chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
439 content::Source<ScreenLocker>(this),
440 content::Details<bool>(&state));
441 VLOG(1) << "Calling session manager's HandleLockScreenShown D-Bus method";
442 DBusThreadManager::Get()->GetSessionManagerClient()->NotifyLockScreenShown();
445 content::WebUI* ScreenLocker::GetAssociatedWebUI() {
446 return delegate_->GetAssociatedWebUI();
449 bool ScreenLocker::IsUserLoggedIn(const std::string& username) {
450 for (UserList::const_iterator it = users_.begin(); it != users_.end(); ++it) {
451 if ((*it)->email() == username)
457 } // namespace chromeos