easy_unlock_service.cc revision 78901d17b47ef1f8d6d0a89eaf37f9523ba1de85
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.
4
5#include "chrome/browser/signin/easy_unlock_service.h"
6
7#include "base/bind.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"
38
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"
46#endif
47
48namespace {
49
50extensions::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();
56}
57
58PrefService* GetLocalState() {
59  return g_browser_process ? g_browser_process->local_state() : NULL;
60}
61
62}  // namespace
63
64// static
65EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
66  return EasyUnlockServiceFactory::GetForProfile(profile);
67}
68
69// static
70EasyUnlockService* EasyUnlockService::GetForUser(
71    const user_manager::User& user) {
72#if defined(OS_CHROMEOS)
73  Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(&user);
74  if (!profile)
75    return NULL;
76  return EasyUnlockService::Get(profile);
77#else
78  return NULL;
79#endif
80}
81
82// static
83bool EasyUnlockService::IsSignInEnabled() {
84#if defined(OS_CHROMEOS)
85  const std::string group_name =
86      base::FieldTrialList::FindFullName("EasySignIn");
87
88  if (CommandLine::ForCurrentProcess()->HasSwitch(
89          chromeos::switches::kDisableEasySignin)) {
90    return false;
91  }
92
93  return group_name == "Enable";
94#else
95  return false;
96#endif
97}
98
99class EasyUnlockService::BluetoothDetector
100    : public device::BluetoothAdapter::Observer {
101 public:
102  explicit BluetoothDetector(EasyUnlockService* service)
103      : service_(service),
104        weak_ptr_factory_(this) {
105  }
106
107  virtual ~BluetoothDetector() {
108    if (adapter_.get())
109      adapter_->RemoveObserver(this);
110  }
111
112  void Initialize() {
113    if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
114      return;
115
116    device::BluetoothAdapterFactory::GetAdapter(
117        base::Bind(&BluetoothDetector::OnAdapterInitialized,
118                   weak_ptr_factory_.GetWeakPtr()));
119  }
120
121  bool IsPresent() const { return adapter_.get() && adapter_->IsPresent(); }
122
123  // device::BluetoothAdapter::Observer:
124  virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter,
125                                     bool present) OVERRIDE {
126    service_->OnBluetoothAdapterPresentChanged();
127  }
128
129 private:
130  void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
131    adapter_ = adapter;
132    adapter_->AddObserver(this);
133    service_->OnBluetoothAdapterPresentChanged();
134  }
135
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_;
140
141  DISALLOW_COPY_AND_ASSIGN(BluetoothDetector);
142};
143
144#if defined(OS_CHROMEOS)
145class EasyUnlockService::PowerMonitor
146    : public chromeos::PowerManagerClient::Observer {
147 public:
148  explicit PowerMonitor(EasyUnlockService* service)
149      : service_(service),
150        waking_up_(false),
151        weak_ptr_factory_(this) {
152    chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
153        AddObserver(this);
154  }
155
156  virtual ~PowerMonitor() {
157    chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
158        RemoveObserver(this);
159  }
160
161  bool waking_up() const { return waking_up_; }
162
163 private:
164  // chromeos::PowerManagerClient::Observer:
165  virtual void SuspendImminent() OVERRIDE {
166    service_->PrepareForSuspend();
167  }
168
169  virtual void SuspendDone(const base::TimeDelta& sleep_duration) OVERRIDE {
170    waking_up_ = true;
171    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
172        FROM_HERE,
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.
178  }
179
180  void ResetWakingUp() {
181    waking_up_ = false;
182    service_->UpdateAppState();
183  }
184
185  EasyUnlockService* service_;
186  bool waking_up_;
187  base::WeakPtrFactory<PowerMonitor> weak_ptr_factory_;
188
189  DISALLOW_COPY_AND_ASSIGN(PowerMonitor);
190};
191#endif
192
193EasyUnlockService::EasyUnlockService(Profile* profile)
194    : profile_(profile),
195      bluetooth_detector_(new BluetoothDetector(this)),
196      shut_down_(false),
197      weak_ptr_factory_(this) {
198  extensions::ExtensionSystem::Get(profile_)->ready().Post(
199      FROM_HERE,
200      base::Bind(&EasyUnlockService::Initialize,
201                 weak_ptr_factory_.GetWeakPtr()));
202}
203
204EasyUnlockService::~EasyUnlockService() {
205}
206
207// static
208void EasyUnlockService::RegisterProfilePrefs(
209    user_prefs::PrefRegistrySyncable* registry) {
210  registry->RegisterBooleanPref(
211      prefs::kEasyUnlockEnabled,
212      false,
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,
220      true,
221      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
222}
223
224// static
225void EasyUnlockService::RegisterPrefs(PrefRegistrySimple* registry) {
226  registry->RegisterDictionaryPref(prefs::kEasyUnlockHardlockState);
227}
228
229// static
230void EasyUnlockService::ResetLocalStateForUser(const std::string& user_id) {
231  DCHECK(!user_id.empty());
232
233  PrefService* local_state = GetLocalState();
234  if (!local_state)
235    return;
236
237  DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
238  update->RemoveWithoutPathExpansion(user_id, NULL);
239}
240
241bool EasyUnlockService::IsAllowed() {
242  if (shut_down_)
243    return false;
244
245  if (!IsAllowedInternal())
246    return false;
247
248#if defined(OS_CHROMEOS)
249  if (!bluetooth_detector_->IsPresent())
250    return false;
251
252  return true;
253#else
254  // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
255  return false;
256#endif
257}
258
259void EasyUnlockService::SetHardlockState(
260    EasyUnlockScreenlockStateHandler::HardlockState state) {
261  const std::string user_id = GetUserEmail();
262  if (user_id.empty())
263    return;
264
265  SetHardlockStateForUser(user_id, state);
266}
267
268EasyUnlockScreenlockStateHandler::HardlockState
269EasyUnlockService::GetHardlockState() const {
270  EasyUnlockScreenlockStateHandler::HardlockState state;
271  if (GetPersistedHardlockState(&state))
272    return state;
273
274  return EasyUnlockScreenlockStateHandler::NO_HARDLOCK;
275}
276
277bool EasyUnlockService::GetPersistedHardlockState(
278    EasyUnlockScreenlockStateHandler::HardlockState* state) const {
279  std::string user_id = GetUserEmail();
280  if (user_id.empty())
281    return false;
282
283  PrefService* local_state = GetLocalState();
284  if (!local_state)
285    return false;
286
287  const base::DictionaryValue* dict =
288      local_state->GetDictionary(prefs::kEasyUnlockHardlockState);
289  int state_int;
290  if (dict && dict->GetIntegerWithoutPathExpansion(user_id, &state_int)) {
291    *state =
292        static_cast<EasyUnlockScreenlockStateHandler::HardlockState>(state_int);
293    return true;
294  }
295
296  return false;
297}
298
299void EasyUnlockService::ShowInitialUserState() {
300  if (!GetScreenlockStateHandler())
301    return;
302
303  EasyUnlockScreenlockStateHandler::HardlockState state;
304  bool has_persisted_state = GetPersistedHardlockState(&state);
305  if (!has_persisted_state)
306    return;
307
308  if (state == EasyUnlockScreenlockStateHandler::NO_HARDLOCK) {
309    // Show connecting icon early when there is a persisted non hardlock state.
310    UpdateScreenlockState(
311        EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING);
312  } else {
313    screenlock_state_handler_->MaybeShowHardlockUI();
314  }
315}
316
317EasyUnlockScreenlockStateHandler*
318    EasyUnlockService::GetScreenlockStateHandler() {
319  if (!IsAllowed())
320    return NULL;
321  if (!screenlock_state_handler_) {
322    screenlock_state_handler_.reset(new EasyUnlockScreenlockStateHandler(
323        GetUserEmail(),
324        GetHardlockState(),
325        ScreenlockBridge::Get()));
326  }
327  return screenlock_state_handler_.get();
328}
329
330bool EasyUnlockService::UpdateScreenlockState(
331    EasyUnlockScreenlockStateHandler::State state) {
332  EasyUnlockScreenlockStateHandler* handler = GetScreenlockStateHandler();
333  if (!handler)
334    return false;
335
336  handler->ChangeState(state);
337
338  if (state != EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED)
339    auth_attempt_.reset();
340  return true;
341}
342
343void EasyUnlockService::AttemptAuth(const std::string& user_id) {
344  auth_attempt_.reset(new EasyUnlockAuthAttempt(
345      profile_,
346      GetUserEmail(),
347      GetType() == TYPE_REGULAR ? EasyUnlockAuthAttempt::TYPE_UNLOCK
348                                : EasyUnlockAuthAttempt::TYPE_SIGNIN));
349  if (!auth_attempt_->Start(user_id))
350    auth_attempt_.reset();
351}
352
353void EasyUnlockService::FinalizeUnlock(bool success) {
354  if (auth_attempt_)
355    auth_attempt_->FinalizeUnlock(GetUserEmail(), success);
356  auth_attempt_.reset();
357}
358
359void EasyUnlockService::FinalizeSignin(const std::string& key) {
360  if (!auth_attempt_)
361    return;
362  std::string wrapped_secret = GetWrappedSecret();
363  if (!wrapped_secret.empty())
364    auth_attempt_->FinalizeSignin(GetUserEmail(), wrapped_secret, key);
365  auth_attempt_.reset();
366}
367
368void EasyUnlockService::CheckCryptohomeKeysAndMaybeHardlock() {
369#if defined(OS_CHROMEOS)
370  std::string user_id = GetUserEmail();
371  if (user_id.empty())
372    return;
373
374  const base::ListValue* device_list = GetRemoteDevices();
375  std::set<std::string> paired_devices;
376  if (device_list) {
377    chromeos::EasyUnlockDeviceKeyDataList parsed_paired;
378    chromeos::EasyUnlockKeyManager::RemoteDeviceListToDeviceDataList(
379        *device_list, &parsed_paired);
380    for (const auto& device_key_data : parsed_paired)
381      paired_devices.insert(device_key_data.psk);
382  }
383  if (paired_devices.empty()) {
384    SetHardlockState(EasyUnlockScreenlockStateHandler::NO_PAIRING);
385    return;
386  }
387
388  // No need to compare if a change is already recorded.
389  if (GetHardlockState() == EasyUnlockScreenlockStateHandler::PAIRING_CHANGED ||
390      GetHardlockState() == EasyUnlockScreenlockStateHandler::PAIRING_ADDED) {
391    return;
392  }
393
394  chromeos::EasyUnlockKeyManager* key_manager =
395      chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
396  DCHECK(key_manager);
397
398  key_manager->GetDeviceDataList(
399      chromeos::UserContext(user_id),
400      base::Bind(&EasyUnlockService::OnCryptohomeKeysFetchedForChecking,
401                 weak_ptr_factory_.GetWeakPtr(),
402                 user_id,
403                 paired_devices));
404#endif
405}
406
407void EasyUnlockService::SetTrialRun() {
408  DCHECK(GetType() == TYPE_REGULAR);
409
410  EasyUnlockScreenlockStateHandler* handler = GetScreenlockStateHandler();
411  if (handler)
412    handler->SetTrialRun();
413}
414
415void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
416  observers_.AddObserver(observer);
417}
418
419void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) {
420  observers_.RemoveObserver(observer);
421}
422
423void  EasyUnlockService::Shutdown() {
424  if (shut_down_)
425    return;
426  shut_down_ = true;
427
428  ShutdownInternal();
429
430  weak_ptr_factory_.InvalidateWeakPtrs();
431
432  ResetScreenlockState();
433  bluetooth_detector_.reset();
434#if defined(OS_CHROMEOS)
435  power_monitor_.reset();
436#endif
437}
438
439void EasyUnlockService::LoadApp() {
440  DCHECK(IsAllowed());
441
442#if defined(GOOGLE_CHROME_BUILD)
443  base::FilePath easy_unlock_path;
444#if defined(OS_CHROMEOS)
445  easy_unlock_path = base::FilePath("/usr/share/chromeos-assets/easy_unlock");
446#endif  // defined(OS_CHROMEOS)
447
448#ifndef NDEBUG
449  // Only allow app path override switch for debug build.
450  const CommandLine* command_line = CommandLine::ForCurrentProcess();
451  if (command_line->HasSwitch(switches::kEasyUnlockAppPath)) {
452    easy_unlock_path =
453        command_line->GetSwitchValuePath(switches::kEasyUnlockAppPath);
454  }
455#endif  // !defined(NDEBUG)
456
457  if (!easy_unlock_path.empty()) {
458    extensions::ComponentLoader* loader = GetComponentLoader(profile_);
459    if (!loader->Exists(extension_misc::kEasyUnlockAppId))
460      loader->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path);
461
462    ExtensionService* extension_service =
463        extensions::ExtensionSystem::Get(profile_)->extension_service();
464    extension_service->EnableExtension(extension_misc::kEasyUnlockAppId);
465
466    NotifyUserUpdated();
467  }
468#endif  // defined(GOOGLE_CHROME_BUILD)
469}
470
471void EasyUnlockService::DisableAppIfLoaded() {
472  extensions::ComponentLoader* loader = GetComponentLoader(profile_);
473  if (!loader->Exists(extension_misc::kEasyUnlockAppId))
474    return;
475
476  ExtensionService* extension_service =
477      extensions::ExtensionSystem::Get(profile_)->extension_service();
478  extension_service->DisableExtension(extension_misc::kEasyUnlockAppId,
479                                      extensions::Extension::DISABLE_RELOAD);
480}
481
482void EasyUnlockService::UnloadApp() {
483  GetComponentLoader(profile_)->Remove(extension_misc::kEasyUnlockAppId);
484}
485
486void EasyUnlockService::ReloadApp() {
487  // Make sure lock screen state set by the extension gets reset.
488  ResetScreenlockState();
489
490  if (!GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId))
491    return;
492  extensions::ExtensionSystem* extension_system =
493      extensions::ExtensionSystem::Get(profile_);
494  extension_system->extension_service()->ReloadExtension(
495      extension_misc::kEasyUnlockAppId);
496  NotifyUserUpdated();
497}
498
499void EasyUnlockService::UpdateAppState() {
500  if (IsAllowed()) {
501    LoadApp();
502
503#if defined(OS_CHROMEOS)
504    if (!power_monitor_)
505      power_monitor_.reset(new PowerMonitor(this));
506#endif
507  } else {
508    bool bluetooth_waking_up = false;
509#if defined(OS_CHROMEOS)
510    // If the service is not allowed due to bluetooth not being detected just
511    // after system suspend is done, give bluetooth more time to be detected
512    // before disabling the app (and resetting screenlock state).
513    bluetooth_waking_up =
514        power_monitor_.get() && power_monitor_->waking_up() &&
515        !bluetooth_detector_->IsPresent();
516#endif
517
518    if (!bluetooth_waking_up) {
519      DisableAppIfLoaded();
520      ResetScreenlockState();
521#if defined(OS_CHROMEOS)
522      power_monitor_.reset();
523#endif
524    }
525  }
526}
527
528void EasyUnlockService::NotifyUserUpdated() {
529  std::string user_id = GetUserEmail();
530  if (user_id.empty())
531    return;
532
533  // Notify the easy unlock app that the user info changed.
534  extensions::api::easy_unlock_private::UserInfo info;
535  info.user_id = user_id;
536  info.logged_in = GetType() == TYPE_REGULAR;
537  info.data_ready = info.logged_in || GetRemoteDevices() != NULL;
538
539  scoped_ptr<base::ListValue> args(new base::ListValue());
540  args->Append(info.ToValue().release());
541
542  scoped_ptr<extensions::Event> event(new extensions::Event(
543      extensions::api::easy_unlock_private::OnUserInfoUpdated::kEventName,
544      args.Pass()));
545
546  extensions::EventRouter::Get(profile_)->DispatchEventToExtension(
547       extension_misc::kEasyUnlockAppId, event.Pass());
548}
549
550void EasyUnlockService::NotifyTurnOffOperationStatusChanged() {
551  FOR_EACH_OBSERVER(
552      EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
553}
554
555void EasyUnlockService::ResetScreenlockState() {
556  screenlock_state_handler_.reset();
557  auth_attempt_.reset();
558}
559
560void EasyUnlockService::SetScreenlockHardlockedState(
561    EasyUnlockScreenlockStateHandler::HardlockState state) {
562  if (screenlock_state_handler_)
563    screenlock_state_handler_->SetHardlockState(state);
564  if (state != EasyUnlockScreenlockStateHandler::NO_HARDLOCK)
565    auth_attempt_.reset();
566}
567
568void EasyUnlockService::Initialize() {
569  InitializeInternal();
570
571#if defined(OS_CHROMEOS)
572  // Only start Bluetooth detection for ChromeOS since the feature is
573  // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
574  // previously introduced a performance regression: http://crbug.com/404482
575  // Make sure not to reintroduce a performance regression if re-enabling on
576  // additional platforms.
577  // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
578  bluetooth_detector_->Initialize();
579#endif  // defined(OS_CHROMEOS)
580}
581
582void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
583  UpdateAppState();
584}
585
586void EasyUnlockService::SetHardlockStateForUser(
587      const std::string& user_id,
588      EasyUnlockScreenlockStateHandler::HardlockState state) {
589  DCHECK(!user_id.empty());
590
591  PrefService* local_state = GetLocalState();
592  if (!local_state)
593    return;
594
595  DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
596  update->SetIntegerWithoutPathExpansion(user_id, static_cast<int>(state));
597
598  if (GetUserEmail() == user_id)
599    SetScreenlockHardlockedState(state);
600}
601
602#if defined(OS_CHROMEOS)
603void EasyUnlockService::OnCryptohomeKeysFetchedForChecking(
604    const std::string& user_id,
605    const std::set<std::string> paired_devices,
606    bool success,
607    const chromeos::EasyUnlockDeviceKeyDataList& key_data_list) {
608  DCHECK(!user_id.empty() && !paired_devices.empty());
609
610  if (!success) {
611    SetHardlockStateForUser(user_id,
612                            EasyUnlockScreenlockStateHandler::NO_PAIRING);
613    return;
614  }
615
616  std::set<std::string> devices_in_cryptohome;
617  for (const auto& device_key_data : key_data_list)
618    devices_in_cryptohome.insert(device_key_data.psk);
619
620  if (paired_devices != devices_in_cryptohome ||
621      GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING) {
622    SetHardlockStateForUser(
623        user_id,
624        devices_in_cryptohome.empty()
625            ? EasyUnlockScreenlockStateHandler::PAIRING_ADDED
626            : EasyUnlockScreenlockStateHandler::PAIRING_CHANGED);
627  }
628}
629#endif
630
631void EasyUnlockService::PrepareForSuspend() {
632  DisableAppIfLoaded();
633  if (screenlock_state_handler_ && screenlock_state_handler_->IsActive()) {
634    UpdateScreenlockState(
635        EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING);
636  }
637}
638
639