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/memory/ref_counted.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/component_loader.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
19 #include "chrome/browser/signin/easy_unlock_service_factory.h"
20 #include "chrome/browser/signin/easy_unlock_service_observer.h"
21 #include "chrome/browser/signin/easy_unlock_toggle_flow.h"
22 #include "chrome/browser/signin/screenlock_bridge.h"
23 #include "chrome/browser/ui/extensions/application_launch.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h"
26 #include "components/pref_registry/pref_registry_syncable.h"
27 #include "device/bluetooth/bluetooth_adapter.h"
28 #include "device/bluetooth/bluetooth_adapter_factory.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/common/one_shot_event.h"
31 #include "grit/browser_resources.h"
33 #if defined(OS_CHROMEOS)
34 #include "chrome/browser/chromeos/profiles/profile_helper.h"
35 #include "components/user_manager/user_manager.h"
40 // Key name of the local device permit record dictonary in kEasyUnlockPairing.
41 const char kKeyPermitAccess[] = "permitAccess";
43 // Key name of the remote device list in kEasyUnlockPairing.
44 const char kKeyDevices[] = "devices";
46 // Key name of the phone public key in a device dictionary.
47 const char kKeyPhoneId[] = "permitRecord.id";
49 extensions::ComponentLoader* GetComponentLoader(
50 content::BrowserContext* context) {
51 extensions::ExtensionSystem* extension_system =
52 extensions::ExtensionSystem::Get(context);
53 ExtensionService* extension_service = extension_system->extension_service();
54 return extension_service->component_loader();
60 EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
61 return EasyUnlockServiceFactory::GetForProfile(profile);
64 class EasyUnlockService::BluetoothDetector
65 : public device::BluetoothAdapter::Observer {
67 explicit BluetoothDetector(EasyUnlockService* service)
69 weak_ptr_factory_(this) {
72 virtual ~BluetoothDetector() {
74 adapter_->RemoveObserver(this);
78 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
81 device::BluetoothAdapterFactory::GetAdapter(
82 base::Bind(&BluetoothDetector::OnAdapterInitialized,
83 weak_ptr_factory_.GetWeakPtr()));
86 bool IsPresent() const {
87 return adapter_ && adapter_->IsPresent();
90 // device::BluetoothAdapter::Observer:
91 virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter,
92 bool present) OVERRIDE {
93 service_->OnBluetoothAdapterPresentChanged();
97 void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
99 adapter_->AddObserver(this);
100 service_->OnBluetoothAdapterPresentChanged();
103 // Owner of this class and should out-live this class.
104 EasyUnlockService* service_;
105 scoped_refptr<device::BluetoothAdapter> adapter_;
106 base::WeakPtrFactory<BluetoothDetector> weak_ptr_factory_;
108 DISALLOW_COPY_AND_ASSIGN(BluetoothDetector);
111 EasyUnlockService::EasyUnlockService(Profile* profile)
113 bluetooth_detector_(new BluetoothDetector(this)),
114 turn_off_flow_status_(IDLE),
115 weak_ptr_factory_(this) {
116 extensions::ExtensionSystem::Get(profile_)->ready().Post(
118 base::Bind(&EasyUnlockService::Initialize,
119 weak_ptr_factory_.GetWeakPtr()));
122 EasyUnlockService::~EasyUnlockService() {
126 void EasyUnlockService::RegisterProfilePrefs(
127 user_prefs::PrefRegistrySyncable* registry) {
128 registry->RegisterBooleanPref(
129 prefs::kEasyUnlockEnabled,
131 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
132 registry->RegisterBooleanPref(
133 prefs::kEasyUnlockShowTutorial,
135 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
136 registry->RegisterDictionaryPref(
137 prefs::kEasyUnlockPairing,
138 new base::DictionaryValue(),
139 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
140 registry->RegisterBooleanPref(
141 prefs::kEasyUnlockAllowed,
143 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
146 void EasyUnlockService::LaunchSetup() {
147 ExtensionService* service =
148 extensions::ExtensionSystem::Get(profile_)->extension_service();
149 const extensions::Extension* extension =
150 service->GetExtensionById(extension_misc::kEasyUnlockAppId, false);
152 OpenApplication(AppLaunchParams(
153 profile_, extension, extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW));
156 bool EasyUnlockService::IsAllowed() {
157 #if defined(OS_CHROMEOS)
158 if (!user_manager::UserManager::Get()->IsLoggedInAsRegularUser())
161 if (!chromeos::ProfileHelper::IsPrimaryProfile(profile_))
164 if (!profile_->GetPrefs()->GetBoolean(prefs::kEasyUnlockAllowed))
167 // It is disabled when the trial exists and is in "Disable" group.
168 if (base::FieldTrialList::FindFullName("EasyUnlock") == "Disable")
171 if (!bluetooth_detector_->IsPresent())
176 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
181 EasyUnlockScreenlockStateHandler*
182 EasyUnlockService::GetScreenlockStateHandler() {
185 if (!screenlock_state_handler_) {
186 screenlock_state_handler_.reset(new EasyUnlockScreenlockStateHandler(
187 ScreenlockBridge::GetAuthenticatedUserEmail(profile_),
188 profile_->GetPrefs(),
189 ScreenlockBridge::Get()));
191 return screenlock_state_handler_.get();
194 const base::DictionaryValue* EasyUnlockService::GetPermitAccess() const {
195 const base::DictionaryValue* pairing_dict =
196 profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
197 const base::DictionaryValue* permit_dict = NULL;
199 pairing_dict->GetDictionary(kKeyPermitAccess, &permit_dict)) {
206 void EasyUnlockService::SetPermitAccess(const base::DictionaryValue& permit) {
207 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
208 prefs::kEasyUnlockPairing);
209 pairing_update->SetWithoutPathExpansion(kKeyPermitAccess, permit.DeepCopy());
212 void EasyUnlockService::ClearPermitAccess() {
213 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
214 prefs::kEasyUnlockPairing);
215 pairing_update->RemoveWithoutPathExpansion(kKeyPermitAccess, NULL);
218 const base::ListValue* EasyUnlockService::GetRemoteDevices() const {
219 const base::DictionaryValue* pairing_dict =
220 profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
221 const base::ListValue* devices = NULL;
222 if (pairing_dict && pairing_dict->GetList(kKeyDevices, &devices)) {
229 void EasyUnlockService::SetRemoteDevices(const base::ListValue& devices) {
230 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
231 prefs::kEasyUnlockPairing);
232 pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy());
235 void EasyUnlockService::ClearRemoteDevices() {
236 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
237 prefs::kEasyUnlockPairing);
238 pairing_update->RemoveWithoutPathExpansion(kKeyDevices, NULL);
241 void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
242 observers_.AddObserver(observer);
245 void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) {
246 observers_.RemoveObserver(observer);
249 void EasyUnlockService::RunTurnOffFlow() {
250 if (turn_off_flow_status_ == PENDING)
253 SetTurnOffFlowStatus(PENDING);
255 // Currently there should only be one registered phone.
256 // TODO(xiyuan): Revisit this when server supports toggle for all or
257 // there are multiple phones.
258 const base::DictionaryValue* pairing_dict =
259 profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
260 const base::ListValue* devices_list = NULL;
261 const base::DictionaryValue* first_device = NULL;
262 std::string phone_public_key;
263 if (!pairing_dict || !pairing_dict->GetList(kKeyDevices, &devices_list) ||
264 !devices_list || !devices_list->GetDictionary(0, &first_device) ||
266 !first_device->GetString(kKeyPhoneId, &phone_public_key)) {
267 LOG(WARNING) << "Bad easy unlock pairing data, wiping out local data";
268 OnTurnOffFlowFinished(true);
272 turn_off_flow_.reset(new EasyUnlockToggleFlow(
276 base::Bind(&EasyUnlockService::OnTurnOffFlowFinished,
277 base::Unretained(this))));
278 turn_off_flow_->Start();
281 void EasyUnlockService::ResetTurnOffFlow() {
282 turn_off_flow_.reset();
283 SetTurnOffFlowStatus(IDLE);
286 void EasyUnlockService::Initialize() {
287 registrar_.Init(profile_->GetPrefs());
289 prefs::kEasyUnlockAllowed,
290 base::Bind(&EasyUnlockService::OnPrefsChanged, base::Unretained(this)));
293 #if defined(OS_CHROMEOS)
294 // Only start Bluetooth detection for ChromeOS since the feature is
295 // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
296 // previously introduced a performance regression: http://crbug.com/404482
297 // Make sure not to reintroduce a performance regression if re-enabling on
298 // additional platforms.
299 // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
300 bluetooth_detector_->Initialize();
301 #endif // defined(OS_CHROMEOS)
304 void EasyUnlockService::LoadApp() {
307 #if defined(GOOGLE_CHROME_BUILD)
308 base::FilePath easy_unlock_path;
309 #if defined(OS_CHROMEOS)
310 easy_unlock_path = base::FilePath("/usr/share/chromeos-assets/easy_unlock");
311 #endif // defined(OS_CHROMEOS)
314 // Only allow app path override switch for debug build.
315 const CommandLine* command_line = CommandLine::ForCurrentProcess();
316 if (command_line->HasSwitch(switches::kEasyUnlockAppPath)) {
318 command_line->GetSwitchValuePath(switches::kEasyUnlockAppPath);
320 #endif // !defined(NDEBUG)
322 if (!easy_unlock_path.empty()) {
323 extensions::ComponentLoader* loader = GetComponentLoader(profile_);
324 if (!loader->Exists(extension_misc::kEasyUnlockAppId))
325 loader->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path);
327 #endif // defined(GOOGLE_CHROME_BUILD)
330 void EasyUnlockService::UnloadApp() {
331 extensions::ComponentLoader* loader = GetComponentLoader(profile_);
332 if (loader->Exists(extension_misc::kEasyUnlockAppId))
333 loader->Remove(extension_misc::kEasyUnlockAppId);
336 void EasyUnlockService::UpdateAppState() {
341 // Reset the screenlock state handler to make sure Screenlock state set
342 // by Easy Unlock app is reset.
343 screenlock_state_handler_.reset();
347 void EasyUnlockService::OnPrefsChanged() {
351 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
355 void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
356 turn_off_flow_status_ = status;
358 EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
361 void EasyUnlockService::OnTurnOffFlowFinished(bool success) {
362 turn_off_flow_.reset();
365 SetTurnOffFlowStatus(FAIL);
369 ClearRemoteDevices();
370 SetTurnOffFlowStatus(IDLE);
372 // Make sure lock screen state set by the extension gets reset.
373 screenlock_state_handler_.reset();
375 if (GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId)) {
376 extensions::ExtensionSystem* extension_system =
377 extensions::ExtensionSystem::Get(profile_);
378 extension_system->extension_service()->ReloadExtension(
379 extension_misc::kEasyUnlockAppId);