1 // Copyright 2014 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/signin/easy_unlock_service.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/extensions/component_loader.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/signin/easy_unlock_auth_attempt.h"
22 #include "chrome/browser/signin/easy_unlock_service_factory.h"
23 #include "chrome/browser/signin/easy_unlock_service_observer.h"
24 #include "chrome/browser/signin/screenlock_bridge.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/extensions/api/easy_unlock_private.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/pref_registry/pref_registry_syncable.h"
30 #include "components/user_manager/user.h"
31 #include "device/bluetooth/bluetooth_adapter.h"
32 #include "device/bluetooth/bluetooth_adapter_factory.h"
33 #include "extensions/browser/event_router.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/common/one_shot_event.h"
37 #include "grit/browser_resources.h"
39 #if defined(OS_CHROMEOS)
40 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
41 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
42 #include "chrome/browser/chromeos/profiles/profile_helper.h"
43 #include "chromeos/chromeos_switches.h"
44 #include "chromeos/dbus/dbus_thread_manager.h"
45 #include "chromeos/dbus/power_manager_client.h"
50 extensions::ComponentLoader* GetComponentLoader(
51 content::BrowserContext* context) {
52 extensions::ExtensionSystem* extension_system =
53 extensions::ExtensionSystem::Get(context);
54 ExtensionService* extension_service = extension_system->extension_service();
55 return extension_service->component_loader();
58 PrefService* GetLocalState() {
59 return g_browser_process ? g_browser_process->local_state() : NULL;
65 EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
66 return EasyUnlockServiceFactory::GetForProfile(profile);
70 EasyUnlockService* EasyUnlockService::GetForUser(
71 const user_manager::User& user) {
72 #if defined(OS_CHROMEOS)
73 Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(&user);
76 return EasyUnlockService::Get(profile);
83 bool EasyUnlockService::IsSignInEnabled() {
84 #if defined(OS_CHROMEOS)
85 const std::string group_name =
86 base::FieldTrialList::FindFullName("EasySignIn");
88 if (CommandLine::ForCurrentProcess()->HasSwitch(
89 chromeos::switches::kDisableEasySignin)) {
93 return group_name == "Enable";
99 class EasyUnlockService::BluetoothDetector
100 : public device::BluetoothAdapter::Observer {
102 explicit BluetoothDetector(EasyUnlockService* service)
104 weak_ptr_factory_(this) {
107 ~BluetoothDetector() override {
109 adapter_->RemoveObserver(this);
113 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
116 device::BluetoothAdapterFactory::GetAdapter(
117 base::Bind(&BluetoothDetector::OnAdapterInitialized,
118 weak_ptr_factory_.GetWeakPtr()));
121 bool IsPresent() const { return adapter_.get() && adapter_->IsPresent(); }
123 // device::BluetoothAdapter::Observer:
124 void AdapterPresentChanged(device::BluetoothAdapter* adapter,
125 bool present) override {
126 service_->OnBluetoothAdapterPresentChanged();
130 void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
132 adapter_->AddObserver(this);
133 service_->OnBluetoothAdapterPresentChanged();
136 // Owner of this class and should out-live this class.
137 EasyUnlockService* service_;
138 scoped_refptr<device::BluetoothAdapter> adapter_;
139 base::WeakPtrFactory<BluetoothDetector> weak_ptr_factory_;
141 DISALLOW_COPY_AND_ASSIGN(BluetoothDetector);
144 #if defined(OS_CHROMEOS)
145 class EasyUnlockService::PowerMonitor
146 : public chromeos::PowerManagerClient::Observer {
148 explicit PowerMonitor(EasyUnlockService* service)
151 weak_ptr_factory_(this) {
152 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
156 virtual ~PowerMonitor() {
157 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
158 RemoveObserver(this);
161 bool waking_up() const { return waking_up_; }
164 // chromeos::PowerManagerClient::Observer:
165 virtual void SuspendImminent() override {
166 service_->PrepareForSuspend();
169 virtual void SuspendDone(const base::TimeDelta& sleep_duration) override {
171 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
173 base::Bind(&PowerMonitor::ResetWakingUp,
174 weak_ptr_factory_.GetWeakPtr()),
175 base::TimeDelta::FromSeconds(5));
176 service_->UpdateAppState();
177 // Note that |this| may get deleted after |UpdateAppState| is called.
180 void ResetWakingUp() {
182 service_->UpdateAppState();
185 EasyUnlockService* service_;
187 base::WeakPtrFactory<PowerMonitor> weak_ptr_factory_;
189 DISALLOW_COPY_AND_ASSIGN(PowerMonitor);
193 EasyUnlockService::EasyUnlockService(Profile* profile)
195 bluetooth_detector_(new BluetoothDetector(this)),
197 weak_ptr_factory_(this) {
198 extensions::ExtensionSystem::Get(profile_)->ready().Post(
200 base::Bind(&EasyUnlockService::Initialize,
201 weak_ptr_factory_.GetWeakPtr()));
204 EasyUnlockService::~EasyUnlockService() {
208 void EasyUnlockService::RegisterProfilePrefs(
209 user_prefs::PrefRegistrySyncable* registry) {
210 registry->RegisterBooleanPref(
211 prefs::kEasyUnlockAllowed,
213 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
214 registry->RegisterBooleanPref(
215 prefs::kEasyUnlockEnabled,
217 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
218 registry->RegisterDictionaryPref(
219 prefs::kEasyUnlockPairing,
220 new base::DictionaryValue(),
221 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
222 registry->RegisterBooleanPref(
223 prefs::kEasyUnlockProximityRequired,
225 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
229 void EasyUnlockService::RegisterPrefs(PrefRegistrySimple* registry) {
230 registry->RegisterDictionaryPref(prefs::kEasyUnlockHardlockState);
234 void EasyUnlockService::ResetLocalStateForUser(const std::string& user_id) {
235 DCHECK(!user_id.empty());
237 PrefService* local_state = GetLocalState();
241 DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
242 update->RemoveWithoutPathExpansion(user_id, NULL);
245 bool EasyUnlockService::IsAllowed() {
249 if (!IsAllowedInternal())
252 #if defined(OS_CHROMEOS)
253 if (!bluetooth_detector_->IsPresent())
258 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
263 void EasyUnlockService::SetHardlockState(
264 EasyUnlockScreenlockStateHandler::HardlockState state) {
265 const std::string user_id = GetUserEmail();
269 SetHardlockStateForUser(user_id, state);
272 EasyUnlockScreenlockStateHandler::HardlockState
273 EasyUnlockService::GetHardlockState() const {
274 EasyUnlockScreenlockStateHandler::HardlockState state;
275 if (GetPersistedHardlockState(&state))
278 return EasyUnlockScreenlockStateHandler::NO_HARDLOCK;
281 bool EasyUnlockService::GetPersistedHardlockState(
282 EasyUnlockScreenlockStateHandler::HardlockState* state) const {
283 std::string user_id = GetUserEmail();
287 PrefService* local_state = GetLocalState();
291 const base::DictionaryValue* dict =
292 local_state->GetDictionary(prefs::kEasyUnlockHardlockState);
294 if (dict && dict->GetIntegerWithoutPathExpansion(user_id, &state_int)) {
296 static_cast<EasyUnlockScreenlockStateHandler::HardlockState>(state_int);
303 void EasyUnlockService::ShowInitialUserState() {
304 if (!GetScreenlockStateHandler())
307 EasyUnlockScreenlockStateHandler::HardlockState state;
308 bool has_persisted_state = GetPersistedHardlockState(&state);
309 if (!has_persisted_state)
312 if (state == EasyUnlockScreenlockStateHandler::NO_HARDLOCK) {
313 // Show connecting icon early when there is a persisted non hardlock state.
314 UpdateScreenlockState(
315 EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING);
317 screenlock_state_handler_->MaybeShowHardlockUI();
321 EasyUnlockScreenlockStateHandler*
322 EasyUnlockService::GetScreenlockStateHandler() {
325 if (!screenlock_state_handler_) {
326 screenlock_state_handler_.reset(new EasyUnlockScreenlockStateHandler(
329 ScreenlockBridge::Get()));
331 return screenlock_state_handler_.get();
334 bool EasyUnlockService::UpdateScreenlockState(
335 EasyUnlockScreenlockStateHandler::State state) {
336 EasyUnlockScreenlockStateHandler* handler = GetScreenlockStateHandler();
340 handler->ChangeState(state);
342 if (state != EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED &&
343 auth_attempt_.get()) {
344 auth_attempt_.reset();
346 if (!handler->InStateValidOnRemoteAuthFailure())
347 HandleAuthFailure(GetUserEmail());
352 void EasyUnlockService::AttemptAuth(const std::string& user_id) {
353 auth_attempt_.reset(new EasyUnlockAuthAttempt(
356 GetType() == TYPE_REGULAR ? EasyUnlockAuthAttempt::TYPE_UNLOCK
357 : EasyUnlockAuthAttempt::TYPE_SIGNIN));
358 if (!auth_attempt_->Start(user_id))
359 auth_attempt_.reset();
362 void EasyUnlockService::FinalizeUnlock(bool success) {
363 if (!auth_attempt_.get())
366 auth_attempt_->FinalizeUnlock(GetUserEmail(), success);
367 auth_attempt_.reset();
369 // Make sure that the lock screen is updated on failure.
371 HandleAuthFailure(GetUserEmail());
374 void EasyUnlockService::FinalizeSignin(const std::string& key) {
375 if (!auth_attempt_.get())
377 std::string wrapped_secret = GetWrappedSecret();
378 if (!wrapped_secret.empty())
379 auth_attempt_->FinalizeSignin(GetUserEmail(), wrapped_secret, key);
380 auth_attempt_.reset();
382 // Processing empty key is equivalent to auth cancellation. In this case the
383 // signin request will not actually be processed by login stack, so the lock
384 // screen state should be set from here.
386 HandleAuthFailure(GetUserEmail());
389 void EasyUnlockService::HandleAuthFailure(const std::string& user_id) {
390 if (user_id != GetUserEmail())
393 if (!screenlock_state_handler_.get())
396 screenlock_state_handler_->SetHardlockState(
397 EasyUnlockScreenlockStateHandler::LOGIN_FAILED);
400 void EasyUnlockService::CheckCryptohomeKeysAndMaybeHardlock() {
401 #if defined(OS_CHROMEOS)
402 std::string user_id = GetUserEmail();
406 const base::ListValue* device_list = GetRemoteDevices();
407 std::set<std::string> paired_devices;
409 chromeos::EasyUnlockDeviceKeyDataList parsed_paired;
410 chromeos::EasyUnlockKeyManager::RemoteDeviceListToDeviceDataList(
411 *device_list, &parsed_paired);
412 for (const auto& device_key_data : parsed_paired)
413 paired_devices.insert(device_key_data.psk);
415 if (paired_devices.empty()) {
416 SetHardlockState(EasyUnlockScreenlockStateHandler::NO_PAIRING);
420 // No need to compare if a change is already recorded.
421 if (GetHardlockState() == EasyUnlockScreenlockStateHandler::PAIRING_CHANGED ||
422 GetHardlockState() == EasyUnlockScreenlockStateHandler::PAIRING_ADDED) {
426 chromeos::EasyUnlockKeyManager* key_manager =
427 chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
430 key_manager->GetDeviceDataList(
431 chromeos::UserContext(user_id),
432 base::Bind(&EasyUnlockService::OnCryptohomeKeysFetchedForChecking,
433 weak_ptr_factory_.GetWeakPtr(),
439 void EasyUnlockService::SetTrialRun() {
440 DCHECK(GetType() == TYPE_REGULAR);
442 EasyUnlockScreenlockStateHandler* handler = GetScreenlockStateHandler();
444 handler->SetTrialRun();
447 void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
448 observers_.AddObserver(observer);
451 void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) {
452 observers_.RemoveObserver(observer);
455 void EasyUnlockService::Shutdown() {
462 weak_ptr_factory_.InvalidateWeakPtrs();
464 ResetScreenlockState();
465 bluetooth_detector_.reset();
466 #if defined(OS_CHROMEOS)
467 power_monitor_.reset();
471 void EasyUnlockService::LoadApp() {
474 #if defined(GOOGLE_CHROME_BUILD)
475 base::FilePath easy_unlock_path;
476 #if defined(OS_CHROMEOS)
477 easy_unlock_path = base::FilePath("/usr/share/chromeos-assets/easy_unlock");
478 #endif // defined(OS_CHROMEOS)
481 // Only allow app path override switch for debug build.
482 const CommandLine* command_line = CommandLine::ForCurrentProcess();
483 if (command_line->HasSwitch(switches::kEasyUnlockAppPath)) {
485 command_line->GetSwitchValuePath(switches::kEasyUnlockAppPath);
487 #endif // !defined(NDEBUG)
489 if (!easy_unlock_path.empty()) {
490 extensions::ComponentLoader* loader = GetComponentLoader(profile_);
491 if (!loader->Exists(extension_misc::kEasyUnlockAppId))
492 loader->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path);
494 ExtensionService* extension_service =
495 extensions::ExtensionSystem::Get(profile_)->extension_service();
496 extension_service->EnableExtension(extension_misc::kEasyUnlockAppId);
500 #endif // defined(GOOGLE_CHROME_BUILD)
503 void EasyUnlockService::DisableAppIfLoaded() {
504 extensions::ComponentLoader* loader = GetComponentLoader(profile_);
505 if (!loader->Exists(extension_misc::kEasyUnlockAppId))
508 ExtensionService* extension_service =
509 extensions::ExtensionSystem::Get(profile_)->extension_service();
510 extension_service->DisableExtension(extension_misc::kEasyUnlockAppId,
511 extensions::Extension::DISABLE_RELOAD);
514 void EasyUnlockService::UnloadApp() {
515 GetComponentLoader(profile_)->Remove(extension_misc::kEasyUnlockAppId);
518 void EasyUnlockService::ReloadApp() {
519 // Make sure lock screen state set by the extension gets reset.
520 ResetScreenlockState();
522 if (!GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId))
524 extensions::ExtensionSystem* extension_system =
525 extensions::ExtensionSystem::Get(profile_);
526 extension_system->extension_service()->ReloadExtension(
527 extension_misc::kEasyUnlockAppId);
531 void EasyUnlockService::UpdateAppState() {
535 #if defined(OS_CHROMEOS)
537 power_monitor_.reset(new PowerMonitor(this));
540 bool bluetooth_waking_up = false;
541 #if defined(OS_CHROMEOS)
542 // If the service is not allowed due to bluetooth not being detected just
543 // after system suspend is done, give bluetooth more time to be detected
544 // before disabling the app (and resetting screenlock state).
545 bluetooth_waking_up =
546 power_monitor_.get() && power_monitor_->waking_up() &&
547 !bluetooth_detector_->IsPresent();
550 if (!bluetooth_waking_up) {
551 DisableAppIfLoaded();
552 ResetScreenlockState();
553 #if defined(OS_CHROMEOS)
554 power_monitor_.reset();
560 void EasyUnlockService::NotifyUserUpdated() {
561 std::string user_id = GetUserEmail();
565 // Notify the easy unlock app that the user info changed.
566 extensions::api::easy_unlock_private::UserInfo info;
567 info.user_id = user_id;
568 info.logged_in = GetType() == TYPE_REGULAR;
569 info.data_ready = info.logged_in || GetRemoteDevices() != NULL;
571 scoped_ptr<base::ListValue> args(new base::ListValue());
572 args->Append(info.ToValue().release());
574 scoped_ptr<extensions::Event> event(new extensions::Event(
575 extensions::api::easy_unlock_private::OnUserInfoUpdated::kEventName,
578 extensions::EventRouter::Get(profile_)->DispatchEventToExtension(
579 extension_misc::kEasyUnlockAppId, event.Pass());
582 void EasyUnlockService::NotifyTurnOffOperationStatusChanged() {
584 EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
587 void EasyUnlockService::ResetScreenlockState() {
588 screenlock_state_handler_.reset();
589 auth_attempt_.reset();
592 void EasyUnlockService::SetScreenlockHardlockedState(
593 EasyUnlockScreenlockStateHandler::HardlockState state) {
594 if (screenlock_state_handler_)
595 screenlock_state_handler_->SetHardlockState(state);
596 if (state != EasyUnlockScreenlockStateHandler::NO_HARDLOCK)
597 auth_attempt_.reset();
600 void EasyUnlockService::Initialize() {
601 InitializeInternal();
603 #if defined(OS_CHROMEOS)
604 // Only start Bluetooth detection for ChromeOS since the feature is
605 // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
606 // previously introduced a performance regression: http://crbug.com/404482
607 // Make sure not to reintroduce a performance regression if re-enabling on
608 // additional platforms.
609 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
610 bluetooth_detector_->Initialize();
611 #endif // defined(OS_CHROMEOS)
614 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
618 void EasyUnlockService::SetHardlockStateForUser(
619 const std::string& user_id,
620 EasyUnlockScreenlockStateHandler::HardlockState state) {
621 DCHECK(!user_id.empty());
623 PrefService* local_state = GetLocalState();
627 DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
628 update->SetIntegerWithoutPathExpansion(user_id, static_cast<int>(state));
630 if (GetUserEmail() == user_id)
631 SetScreenlockHardlockedState(state);
634 #if defined(OS_CHROMEOS)
635 void EasyUnlockService::OnCryptohomeKeysFetchedForChecking(
636 const std::string& user_id,
637 const std::set<std::string> paired_devices,
639 const chromeos::EasyUnlockDeviceKeyDataList& key_data_list) {
640 DCHECK(!user_id.empty() && !paired_devices.empty());
643 SetHardlockStateForUser(user_id,
644 EasyUnlockScreenlockStateHandler::NO_PAIRING);
648 std::set<std::string> devices_in_cryptohome;
649 for (const auto& device_key_data : key_data_list)
650 devices_in_cryptohome.insert(device_key_data.psk);
652 if (paired_devices != devices_in_cryptohome ||
653 GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING) {
654 SetHardlockStateForUser(
656 devices_in_cryptohome.empty()
657 ? EasyUnlockScreenlockStateHandler::PAIRING_ADDED
658 : EasyUnlockScreenlockStateHandler::PAIRING_CHANGED);
663 void EasyUnlockService::PrepareForSuspend() {
664 DisableAppIfLoaded();
665 if (screenlock_state_handler_ && screenlock_state_handler_->IsActive()) {
666 UpdateScreenlockState(
667 EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING);