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::kEasyUnlockEnabled
,
213 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
214 registry
->RegisterDictionaryPref(
215 prefs::kEasyUnlockPairing
,
216 new base::DictionaryValue(),
217 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
218 registry
->RegisterBooleanPref(
219 prefs::kEasyUnlockAllowed
,
221 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
225 void EasyUnlockService::RegisterPrefs(PrefRegistrySimple
* registry
) {
226 registry
->RegisterDictionaryPref(prefs::kEasyUnlockHardlockState
);
230 void EasyUnlockService::ResetLocalStateForUser(const std::string
& user_id
) {
231 DCHECK(!user_id
.empty());
233 PrefService
* local_state
= GetLocalState();
237 DictionaryPrefUpdate
update(local_state
, prefs::kEasyUnlockHardlockState
);
238 update
->RemoveWithoutPathExpansion(user_id
, NULL
);
241 bool EasyUnlockService::IsAllowed() {
245 if (!IsAllowedInternal())
248 #if defined(OS_CHROMEOS)
249 if (!bluetooth_detector_
->IsPresent())
254 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
259 void EasyUnlockService::SetHardlockState(
260 EasyUnlockScreenlockStateHandler::HardlockState state
) {
261 const std::string user_id
= GetUserEmail();
265 SetHardlockStateForUser(user_id
, state
);
268 EasyUnlockScreenlockStateHandler::HardlockState
269 EasyUnlockService::GetHardlockState() const {
270 std::string user_id
= GetUserEmail();
272 return EasyUnlockScreenlockStateHandler::NO_HARDLOCK
;
274 PrefService
* local_state
= GetLocalState();
276 return EasyUnlockScreenlockStateHandler::NO_HARDLOCK
;
278 const base::DictionaryValue
* dict
=
279 local_state
->GetDictionary(prefs::kEasyUnlockHardlockState
);
281 if (!dict
|| !dict
->GetIntegerWithoutPathExpansion(user_id
, &state
))
282 return EasyUnlockScreenlockStateHandler::NO_HARDLOCK
;
284 return static_cast<EasyUnlockScreenlockStateHandler::HardlockState
>(state
);
287 void EasyUnlockService::MaybeShowHardlockUI() {
288 if (GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_HARDLOCK
)
290 if (GetScreenlockStateHandler())
291 screenlock_state_handler_
->MaybeShowHardlockUI();
294 EasyUnlockScreenlockStateHandler
*
295 EasyUnlockService::GetScreenlockStateHandler() {
298 if (!screenlock_state_handler_
) {
299 screenlock_state_handler_
.reset(new EasyUnlockScreenlockStateHandler(
302 ScreenlockBridge::Get()));
304 return screenlock_state_handler_
.get();
307 bool EasyUnlockService::UpdateScreenlockState(
308 EasyUnlockScreenlockStateHandler::State state
) {
309 EasyUnlockScreenlockStateHandler
* handler
= GetScreenlockStateHandler();
313 handler
->ChangeState(state
);
315 if (state
!= EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED
)
316 auth_attempt_
.reset();
320 void EasyUnlockService::AttemptAuth(const std::string
& user_id
) {
321 auth_attempt_
.reset(new EasyUnlockAuthAttempt(
324 GetType() == TYPE_REGULAR
? EasyUnlockAuthAttempt::TYPE_UNLOCK
325 : EasyUnlockAuthAttempt::TYPE_SIGNIN
));
326 if (!auth_attempt_
->Start(user_id
))
327 auth_attempt_
.reset();
330 void EasyUnlockService::FinalizeUnlock(bool success
) {
332 auth_attempt_
->FinalizeUnlock(GetUserEmail(), success
);
333 auth_attempt_
.reset();
336 void EasyUnlockService::FinalizeSignin(const std::string
& key
) {
339 std::string wrapped_secret
= GetWrappedSecret();
340 if (!wrapped_secret
.empty())
341 auth_attempt_
->FinalizeSignin(GetUserEmail(), wrapped_secret
, key
);
342 auth_attempt_
.reset();
345 void EasyUnlockService::CheckCryptohomeKeysAndMaybeHardlock() {
346 #if defined(OS_CHROMEOS)
347 std::string user_id
= GetUserEmail();
351 const base::ListValue
* device_list
= GetRemoteDevices();
352 std::set
<std::string
> paired_devices
;
354 chromeos::EasyUnlockDeviceKeyDataList parsed_paired
;
355 chromeos::EasyUnlockKeyManager::RemoteDeviceListToDeviceDataList(
356 *device_list
, &parsed_paired
);
357 for (const auto& device_key_data
: parsed_paired
)
358 paired_devices
.insert(device_key_data
.public_key
);
360 if (paired_devices
.empty()) {
361 SetHardlockState(EasyUnlockScreenlockStateHandler::NO_PAIRING
);
365 // No need to compare if a change is already recorded.
366 if (GetHardlockState() == EasyUnlockScreenlockStateHandler::PAIRING_CHANGED
)
369 chromeos::EasyUnlockKeyManager
* key_manager
=
370 chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
373 key_manager
->GetDeviceDataList(
374 chromeos::UserContext(user_id
),
375 base::Bind(&EasyUnlockService::OnCryptohomeKeysFetchedForChecking
,
376 weak_ptr_factory_
.GetWeakPtr(),
382 void EasyUnlockService::SetTrialRun() {
383 DCHECK(GetType() == TYPE_REGULAR
);
385 EasyUnlockScreenlockStateHandler
* handler
= GetScreenlockStateHandler();
387 handler
->SetTrialRun();
390 void EasyUnlockService::AddObserver(EasyUnlockServiceObserver
* observer
) {
391 observers_
.AddObserver(observer
);
394 void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver
* observer
) {
395 observers_
.RemoveObserver(observer
);
398 void EasyUnlockService::Shutdown() {
405 weak_ptr_factory_
.InvalidateWeakPtrs();
407 ResetScreenlockState();
408 bluetooth_detector_
.reset();
409 #if defined(OS_CHROMEOS)
410 power_monitor_
.reset();
414 void EasyUnlockService::LoadApp() {
417 #if defined(GOOGLE_CHROME_BUILD)
418 base::FilePath easy_unlock_path
;
419 #if defined(OS_CHROMEOS)
420 easy_unlock_path
= base::FilePath("/usr/share/chromeos-assets/easy_unlock");
421 #endif // defined(OS_CHROMEOS)
424 // Only allow app path override switch for debug build.
425 const CommandLine
* command_line
= CommandLine::ForCurrentProcess();
426 if (command_line
->HasSwitch(switches::kEasyUnlockAppPath
)) {
428 command_line
->GetSwitchValuePath(switches::kEasyUnlockAppPath
);
430 #endif // !defined(NDEBUG)
432 if (!easy_unlock_path
.empty()) {
433 extensions::ComponentLoader
* loader
= GetComponentLoader(profile_
);
434 if (!loader
->Exists(extension_misc::kEasyUnlockAppId
))
435 loader
->Add(IDR_EASY_UNLOCK_MANIFEST
, easy_unlock_path
);
437 ExtensionService
* extension_service
=
438 extensions::ExtensionSystem::Get(profile_
)->extension_service();
439 extension_service
->EnableExtension(extension_misc::kEasyUnlockAppId
);
443 #endif // defined(GOOGLE_CHROME_BUILD)
446 void EasyUnlockService::DisableAppIfLoaded() {
447 extensions::ComponentLoader
* loader
= GetComponentLoader(profile_
);
448 if (!loader
->Exists(extension_misc::kEasyUnlockAppId
))
451 ExtensionService
* extension_service
=
452 extensions::ExtensionSystem::Get(profile_
)->extension_service();
453 extension_service
->DisableExtension(extension_misc::kEasyUnlockAppId
,
454 extensions::Extension::DISABLE_RELOAD
);
457 void EasyUnlockService::UnloadApp() {
458 GetComponentLoader(profile_
)->Remove(extension_misc::kEasyUnlockAppId
);
461 void EasyUnlockService::ReloadApp() {
462 // Make sure lock screen state set by the extension gets reset.
463 ResetScreenlockState();
465 if (!GetComponentLoader(profile_
)->Exists(extension_misc::kEasyUnlockAppId
))
467 extensions::ExtensionSystem
* extension_system
=
468 extensions::ExtensionSystem::Get(profile_
);
469 extension_system
->extension_service()->ReloadExtension(
470 extension_misc::kEasyUnlockAppId
);
474 void EasyUnlockService::UpdateAppState() {
478 #if defined(OS_CHROMEOS)
480 power_monitor_
.reset(new PowerMonitor(this));
483 bool bluetooth_waking_up
= false;
484 #if defined(OS_CHROMEOS)
485 // If the service is not allowed due to bluetooth not being detected just
486 // after system suspend is done, give bluetooth more time to be detected
487 // before disabling the app (and resetting screenlock state).
488 bluetooth_waking_up
=
489 power_monitor_
.get() && power_monitor_
->waking_up() &&
490 !bluetooth_detector_
->IsPresent();
493 if (!bluetooth_waking_up
) {
494 DisableAppIfLoaded();
495 ResetScreenlockState();
496 #if defined(OS_CHROMEOS)
497 power_monitor_
.reset();
503 void EasyUnlockService::NotifyUserUpdated() {
504 std::string user_id
= GetUserEmail();
508 // Notify the easy unlock app that the user info changed.
509 extensions::api::easy_unlock_private::UserInfo info
;
510 info
.user_id
= user_id
;
511 info
.logged_in
= GetType() == TYPE_REGULAR
;
512 info
.data_ready
= info
.logged_in
|| GetRemoteDevices() != NULL
;
514 scoped_ptr
<base::ListValue
> args(new base::ListValue());
515 args
->Append(info
.ToValue().release());
517 scoped_ptr
<extensions::Event
> event(new extensions::Event(
518 extensions::api::easy_unlock_private::OnUserInfoUpdated::kEventName
,
521 extensions::EventRouter::Get(profile_
)->DispatchEventToExtension(
522 extension_misc::kEasyUnlockAppId
, event
.Pass());
525 void EasyUnlockService::NotifyTurnOffOperationStatusChanged() {
527 EasyUnlockServiceObserver
, observers_
, OnTurnOffOperationStatusChanged());
530 void EasyUnlockService::ResetScreenlockState() {
531 screenlock_state_handler_
.reset();
532 auth_attempt_
.reset();
535 void EasyUnlockService::SetScreenlockHardlockedState(
536 EasyUnlockScreenlockStateHandler::HardlockState state
) {
537 if (screenlock_state_handler_
)
538 screenlock_state_handler_
->SetHardlockState(state
);
539 if (state
!= EasyUnlockScreenlockStateHandler::NO_HARDLOCK
)
540 auth_attempt_
.reset();
543 void EasyUnlockService::Initialize() {
544 InitializeInternal();
546 #if defined(OS_CHROMEOS)
547 // Only start Bluetooth detection for ChromeOS since the feature is
548 // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
549 // previously introduced a performance regression: http://crbug.com/404482
550 // Make sure not to reintroduce a performance regression if re-enabling on
551 // additional platforms.
552 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
553 bluetooth_detector_
->Initialize();
554 #endif // defined(OS_CHROMEOS)
557 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
561 void EasyUnlockService::SetHardlockStateForUser(
562 const std::string
& user_id
,
563 EasyUnlockScreenlockStateHandler::HardlockState state
) {
564 DCHECK(!user_id
.empty());
566 PrefService
* local_state
= GetLocalState();
570 DictionaryPrefUpdate
update(local_state
, prefs::kEasyUnlockHardlockState
);
571 update
->SetIntegerWithoutPathExpansion(user_id
, static_cast<int>(state
));
573 if (GetUserEmail() == user_id
)
574 SetScreenlockHardlockedState(state
);
577 #if defined(OS_CHROMEOS)
578 void EasyUnlockService::OnCryptohomeKeysFetchedForChecking(
579 const std::string
& user_id
,
580 const std::set
<std::string
> paired_devices
,
582 const chromeos::EasyUnlockDeviceKeyDataList
& key_data_list
) {
583 DCHECK(!user_id
.empty() && !paired_devices
.empty());
586 SetHardlockStateForUser(user_id
,
587 EasyUnlockScreenlockStateHandler::NO_PAIRING
);
591 std::set
<std::string
> devices_in_cryptohome
;
592 for (const auto& device_key_data
: key_data_list
)
593 devices_in_cryptohome
.insert(device_key_data
.public_key
);
595 if (paired_devices
!= devices_in_cryptohome
||
596 GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING
) {
597 SetHardlockStateForUser(user_id
,
598 EasyUnlockScreenlockStateHandler::PAIRING_CHANGED
);
603 void EasyUnlockService::PrepareForSuspend() {
604 DisableAppIfLoaded();
605 if (screenlock_state_handler_
&& screenlock_state_handler_
->IsActive()) {
606 UpdateScreenlockState(
607 EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING
);