easy_unlock_service.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/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" 32 33#if defined(OS_CHROMEOS) 34#include "chrome/browser/chromeos/profiles/profile_helper.h" 35#include "components/user_manager/user_manager.h" 36#endif 37 38namespace { 39 40// Key name of the local device permit record dictonary in kEasyUnlockPairing. 41const char kKeyPermitAccess[] = "permitAccess"; 42 43// Key name of the remote device list in kEasyUnlockPairing. 44const char kKeyDevices[] = "devices"; 45 46// Key name of the phone public key in a device dictionary. 47const char kKeyPhoneId[] = "permitRecord.id"; 48 49extensions::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(); 55} 56 57} // namespace 58 59// static 60EasyUnlockService* EasyUnlockService::Get(Profile* profile) { 61 return EasyUnlockServiceFactory::GetForProfile(profile); 62} 63 64class EasyUnlockService::BluetoothDetector 65 : public device::BluetoothAdapter::Observer { 66 public: 67 explicit BluetoothDetector(EasyUnlockService* service) 68 : service_(service), 69 weak_ptr_factory_(this) { 70 } 71 72 virtual ~BluetoothDetector() { 73 if (adapter_) 74 adapter_->RemoveObserver(this); 75 } 76 77 void Initialize() { 78 if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) 79 return; 80 81 device::BluetoothAdapterFactory::GetAdapter( 82 base::Bind(&BluetoothDetector::OnAdapterInitialized, 83 weak_ptr_factory_.GetWeakPtr())); 84 } 85 86 bool IsPresent() const { 87 return adapter_ && adapter_->IsPresent(); 88 } 89 90 // device::BluetoothAdapter::Observer: 91 virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter, 92 bool present) OVERRIDE { 93 service_->OnBluetoothAdapterPresentChanged(); 94 } 95 96 private: 97 void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) { 98 adapter_ = adapter; 99 adapter_->AddObserver(this); 100 service_->OnBluetoothAdapterPresentChanged(); 101 } 102 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_; 107 108 DISALLOW_COPY_AND_ASSIGN(BluetoothDetector); 109}; 110 111EasyUnlockService::EasyUnlockService(Profile* profile) 112 : 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( 117 FROM_HERE, 118 base::Bind(&EasyUnlockService::Initialize, 119 weak_ptr_factory_.GetWeakPtr())); 120} 121 122EasyUnlockService::~EasyUnlockService() { 123} 124 125// static 126void EasyUnlockService::RegisterProfilePrefs( 127 user_prefs::PrefRegistrySyncable* registry) { 128 registry->RegisterBooleanPref( 129 prefs::kEasyUnlockEnabled, 130 false, 131 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 132 registry->RegisterBooleanPref( 133 prefs::kEasyUnlockShowTutorial, 134 true, 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, 142 true, 143 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 144} 145 146void EasyUnlockService::LaunchSetup() { 147 ExtensionService* service = 148 extensions::ExtensionSystem::Get(profile_)->extension_service(); 149 const extensions::Extension* extension = 150 service->GetExtensionById(extension_misc::kEasyUnlockAppId, false); 151 152 OpenApplication(AppLaunchParams( 153 profile_, extension, extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW)); 154} 155 156bool EasyUnlockService::IsAllowed() { 157#if defined(OS_CHROMEOS) 158 if (!user_manager::UserManager::Get()->IsLoggedInAsRegularUser()) 159 return false; 160 161 if (!chromeos::ProfileHelper::IsPrimaryProfile(profile_)) 162 return false; 163 164 if (!profile_->GetPrefs()->GetBoolean(prefs::kEasyUnlockAllowed)) 165 return false; 166 167 // It is disabled when the trial exists and is in "Disable" group. 168 if (base::FieldTrialList::FindFullName("EasyUnlock") == "Disable") 169 return false; 170 171 if (!bluetooth_detector_->IsPresent()) 172 return false; 173 174 return true; 175#else 176 // TODO(xiyuan): Revisit when non-chromeos platforms are supported. 177 return false; 178#endif 179} 180 181EasyUnlockScreenlockStateHandler* 182 EasyUnlockService::GetScreenlockStateHandler() { 183 if (!IsAllowed()) 184 return NULL; 185 if (!screenlock_state_handler_) { 186 screenlock_state_handler_.reset(new EasyUnlockScreenlockStateHandler( 187 ScreenlockBridge::GetAuthenticatedUserEmail(profile_), 188 profile_->GetPrefs(), 189 ScreenlockBridge::Get())); 190 } 191 return screenlock_state_handler_.get(); 192} 193 194const base::DictionaryValue* EasyUnlockService::GetPermitAccess() const { 195 const base::DictionaryValue* pairing_dict = 196 profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing); 197 const base::DictionaryValue* permit_dict = NULL; 198 if (pairing_dict && 199 pairing_dict->GetDictionary(kKeyPermitAccess, &permit_dict)) { 200 return permit_dict; 201 } 202 203 return NULL; 204} 205 206void EasyUnlockService::SetPermitAccess(const base::DictionaryValue& permit) { 207 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(), 208 prefs::kEasyUnlockPairing); 209 pairing_update->SetWithoutPathExpansion(kKeyPermitAccess, permit.DeepCopy()); 210} 211 212void EasyUnlockService::ClearPermitAccess() { 213 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(), 214 prefs::kEasyUnlockPairing); 215 pairing_update->RemoveWithoutPathExpansion(kKeyPermitAccess, NULL); 216} 217 218const 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)) { 223 return devices; 224 } 225 226 return NULL; 227} 228 229void EasyUnlockService::SetRemoteDevices(const base::ListValue& devices) { 230 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(), 231 prefs::kEasyUnlockPairing); 232 pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy()); 233} 234 235void EasyUnlockService::ClearRemoteDevices() { 236 DictionaryPrefUpdate pairing_update(profile_->GetPrefs(), 237 prefs::kEasyUnlockPairing); 238 pairing_update->RemoveWithoutPathExpansion(kKeyDevices, NULL); 239} 240 241void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) { 242 observers_.AddObserver(observer); 243} 244 245void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) { 246 observers_.RemoveObserver(observer); 247} 248 249void EasyUnlockService::RunTurnOffFlow() { 250 if (turn_off_flow_status_ == PENDING) 251 return; 252 253 SetTurnOffFlowStatus(PENDING); 254 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) || 265 !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); 269 return; 270 } 271 272 turn_off_flow_.reset(new EasyUnlockToggleFlow( 273 profile_, 274 phone_public_key, 275 false, 276 base::Bind(&EasyUnlockService::OnTurnOffFlowFinished, 277 base::Unretained(this)))); 278 turn_off_flow_->Start(); 279} 280 281void EasyUnlockService::ResetTurnOffFlow() { 282 turn_off_flow_.reset(); 283 SetTurnOffFlowStatus(IDLE); 284} 285 286void EasyUnlockService::Initialize() { 287 registrar_.Init(profile_->GetPrefs()); 288 registrar_.Add( 289 prefs::kEasyUnlockAllowed, 290 base::Bind(&EasyUnlockService::OnPrefsChanged, base::Unretained(this))); 291 OnPrefsChanged(); 292 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) 302} 303 304void EasyUnlockService::LoadApp() { 305 DCHECK(IsAllowed()); 306 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) 312 313#ifndef NDEBUG 314 // Only allow app path override switch for debug build. 315 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 316 if (command_line->HasSwitch(switches::kEasyUnlockAppPath)) { 317 easy_unlock_path = 318 command_line->GetSwitchValuePath(switches::kEasyUnlockAppPath); 319 } 320#endif // !defined(NDEBUG) 321 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); 326 } 327#endif // defined(GOOGLE_CHROME_BUILD) 328} 329 330void EasyUnlockService::UnloadApp() { 331 extensions::ComponentLoader* loader = GetComponentLoader(profile_); 332 if (loader->Exists(extension_misc::kEasyUnlockAppId)) 333 loader->Remove(extension_misc::kEasyUnlockAppId); 334} 335 336void EasyUnlockService::UpdateAppState() { 337 if (IsAllowed()) { 338 LoadApp(); 339 } else { 340 UnloadApp(); 341 // Reset the screenlock state handler to make sure Screenlock state set 342 // by Easy Unlock app is reset. 343 screenlock_state_handler_.reset(); 344 } 345} 346 347void EasyUnlockService::OnPrefsChanged() { 348 UpdateAppState(); 349} 350 351void EasyUnlockService::OnBluetoothAdapterPresentChanged() { 352 UpdateAppState(); 353} 354 355void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) { 356 turn_off_flow_status_ = status; 357 FOR_EACH_OBSERVER( 358 EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged()); 359} 360 361void EasyUnlockService::OnTurnOffFlowFinished(bool success) { 362 turn_off_flow_.reset(); 363 364 if (!success) { 365 SetTurnOffFlowStatus(FAIL); 366 return; 367 } 368 369 ClearRemoteDevices(); 370 SetTurnOffFlowStatus(IDLE); 371 372 // Make sure lock screen state set by the extension gets reset. 373 screenlock_state_handler_.reset(); 374 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); 380 } 381} 382