Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / easy_unlock_service.cc
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
38 namespace {
39
40 // Key name of the local device permit record dictonary in kEasyUnlockPairing.
41 const char kKeyPermitAccess[] = "permitAccess";
42
43 // Key name of the remote device list in kEasyUnlockPairing.
44 const char kKeyDevices[] = "devices";
45
46 // Key name of the phone public key in a device dictionary.
47 const char kKeyPhoneId[] = "permitRecord.id";
48
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();
55 }
56
57 }  // namespace
58
59 // static
60 EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
61   return EasyUnlockServiceFactory::GetForProfile(profile);
62 }
63
64 class 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
111 EasyUnlockService::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
122 EasyUnlockService::~EasyUnlockService() {
123 }
124
125 // static
126 void 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
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);
151
152   OpenApplication(AppLaunchParams(
153       profile_, extension, extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW));
154 }
155
156 bool 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
181 EasyUnlockScreenlockStateHandler*
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
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;
198   if (pairing_dict &&
199       pairing_dict->GetDictionary(kKeyPermitAccess, &permit_dict)) {
200     return permit_dict;
201   }
202
203   return NULL;
204 }
205
206 void EasyUnlockService::SetPermitAccess(const base::DictionaryValue& permit) {
207   DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
208                                       prefs::kEasyUnlockPairing);
209   pairing_update->SetWithoutPathExpansion(kKeyPermitAccess, permit.DeepCopy());
210 }
211
212 void EasyUnlockService::ClearPermitAccess() {
213   DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
214                                       prefs::kEasyUnlockPairing);
215   pairing_update->RemoveWithoutPathExpansion(kKeyPermitAccess, NULL);
216 }
217
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)) {
223     return devices;
224   }
225
226   return NULL;
227 }
228
229 void EasyUnlockService::SetRemoteDevices(const base::ListValue& devices) {
230   DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
231                                       prefs::kEasyUnlockPairing);
232   pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy());
233 }
234
235 void EasyUnlockService::ClearRemoteDevices() {
236   DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
237                                       prefs::kEasyUnlockPairing);
238   pairing_update->RemoveWithoutPathExpansion(kKeyDevices, NULL);
239 }
240
241 void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
242   observers_.AddObserver(observer);
243 }
244
245 void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) {
246   observers_.RemoveObserver(observer);
247 }
248
249 void 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
281 void EasyUnlockService::ResetTurnOffFlow() {
282   turn_off_flow_.reset();
283   SetTurnOffFlowStatus(IDLE);
284 }
285
286 void 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
304 void 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
330 void EasyUnlockService::UnloadApp() {
331   extensions::ComponentLoader* loader = GetComponentLoader(profile_);
332   if (loader->Exists(extension_misc::kEasyUnlockAppId))
333     loader->Remove(extension_misc::kEasyUnlockAppId);
334 }
335
336 void 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
347 void EasyUnlockService::OnPrefsChanged() {
348   UpdateAppState();
349 }
350
351 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
352   UpdateAppState();
353 }
354
355 void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
356   turn_off_flow_status_ = status;
357   FOR_EACH_OBSERVER(
358       EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
359 }
360
361 void 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 }