Upstream version 9.38.198.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       false,
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   bluetooth_detector_->Initialize();
294 }
295
296 void EasyUnlockService::LoadApp() {
297   DCHECK(IsAllowed());
298
299 #if defined(GOOGLE_CHROME_BUILD)
300   base::FilePath easy_unlock_path;
301 #if defined(OS_CHROMEOS)
302   easy_unlock_path = base::FilePath("/usr/share/chromeos-assets/easy_unlock");
303 #endif  // defined(OS_CHROMEOS)
304
305 #ifndef NDEBUG
306   // Only allow app path override switch for debug build.
307   const CommandLine* command_line = CommandLine::ForCurrentProcess();
308   if (command_line->HasSwitch(switches::kEasyUnlockAppPath)) {
309     easy_unlock_path =
310         command_line->GetSwitchValuePath(switches::kEasyUnlockAppPath);
311   }
312 #endif  // !defined(NDEBUG)
313
314   if (!easy_unlock_path.empty()) {
315     extensions::ComponentLoader* loader = GetComponentLoader(profile_);
316     if (!loader->Exists(extension_misc::kEasyUnlockAppId))
317       loader->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path);
318   }
319 #endif  // defined(GOOGLE_CHROME_BUILD)
320 }
321
322 void EasyUnlockService::UnloadApp() {
323   extensions::ComponentLoader* loader = GetComponentLoader(profile_);
324   if (loader->Exists(extension_misc::kEasyUnlockAppId))
325     loader->Remove(extension_misc::kEasyUnlockAppId);
326 }
327
328 void EasyUnlockService::UpdateAppState() {
329   if (IsAllowed()) {
330     LoadApp();
331   } else {
332     UnloadApp();
333     // Reset the screenlock state handler to make sure Screenlock state set
334     // by Easy Unlock app is reset.
335     screenlock_state_handler_.reset();
336   }
337 }
338
339 void EasyUnlockService::OnPrefsChanged() {
340   UpdateAppState();
341 }
342
343 void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
344   UpdateAppState();
345 }
346
347 void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
348   turn_off_flow_status_ = status;
349   FOR_EACH_OBSERVER(
350       EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
351 }
352
353 void EasyUnlockService::OnTurnOffFlowFinished(bool success) {
354   turn_off_flow_.reset();
355
356   if (!success) {
357     SetTurnOffFlowStatus(FAIL);
358     return;
359   }
360
361   ClearRemoteDevices();
362   SetTurnOffFlowStatus(IDLE);
363
364   if (GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId)) {
365     extensions::ExtensionSystem* extension_system =
366         extensions::ExtensionSystem::Get(profile_);
367     extension_system->extension_service()->ReloadExtension(
368         extension_misc::kEasyUnlockAppId);
369   }
370 }