easy_unlock_private_api.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/extensions/api/easy_unlock_private/easy_unlock_private_api.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/values.h"
11#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_bluetooth_util.h"
12#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_crypto_delegate.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
15#include "chrome/browser/signin/easy_unlock_service.h"
16#include "chrome/common/extensions/api/easy_unlock_private.h"
17#include "chrome/grit/generated_resources.h"
18#include "extensions/browser/browser_context_keyed_api_factory.h"
19#include "ui/base/l10n/l10n_util.h"
20
21#if defined(OS_CHROMEOS)
22#include "chrome/browser/chromeos/chromeos_utils.h"
23#endif
24
25namespace extensions {
26namespace api {
27
28namespace {
29
30static base::LazyInstance<BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI> >
31    g_factory = LAZY_INSTANCE_INITIALIZER;
32
33// Utility method for getting the API's crypto delegate.
34EasyUnlockPrivateCryptoDelegate* GetCryptoDelegate(
35    content::BrowserContext* context) {
36  return BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI>::Get(context)
37             ->crypto_delegate();
38}
39
40EasyUnlockScreenlockStateHandler* GetScreenlockStateHandler(
41    content::BrowserContext* context) {
42  return EasyUnlockService::Get(Profile::FromBrowserContext(context))
43      ->GetScreenlockStateHandler();
44}
45
46EasyUnlockScreenlockStateHandler::State ToScreenlockStateHandlerState(
47    easy_unlock_private::State state) {
48  switch (state) {
49    case easy_unlock_private::STATE_NO_BLUETOOTH:
50      return EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH;
51    case easy_unlock_private::STATE_BLUETOOTH_CONNECTING:
52      return EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING;
53    case easy_unlock_private::STATE_NO_PHONE:
54      return EasyUnlockScreenlockStateHandler::STATE_NO_PHONE;
55    case easy_unlock_private::STATE_PHONE_NOT_AUTHENTICATED:
56      return EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED;
57    case easy_unlock_private::STATE_PHONE_LOCKED:
58      return EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED;
59    case easy_unlock_private::STATE_PHONE_UNLOCKABLE:
60      return EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE;
61    case easy_unlock_private::STATE_PHONE_NOT_NEARBY:
62      return EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY;
63    case easy_unlock_private::STATE_PHONE_UNSUPPORTED:
64      return EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED;
65    case easy_unlock_private::STATE_AUTHENTICATED:
66      return EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED;
67    default:
68      return EasyUnlockScreenlockStateHandler::STATE_INACTIVE;
69  }
70}
71
72}  // namespace
73
74// static
75BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI>*
76    EasyUnlockPrivateAPI::GetFactoryInstance() {
77  return g_factory.Pointer();
78}
79
80EasyUnlockPrivateAPI::EasyUnlockPrivateAPI(content::BrowserContext* context)
81    : crypto_delegate_(EasyUnlockPrivateCryptoDelegate::Create()) {
82}
83
84EasyUnlockPrivateAPI::~EasyUnlockPrivateAPI() {}
85
86EasyUnlockPrivateGetStringsFunction::EasyUnlockPrivateGetStringsFunction() {
87}
88EasyUnlockPrivateGetStringsFunction::~EasyUnlockPrivateGetStringsFunction() {
89}
90
91bool EasyUnlockPrivateGetStringsFunction::RunSync() {
92  scoped_ptr<base::DictionaryValue> strings(new base::DictionaryValue);
93
94#if defined(OS_CHROMEOS)
95  const base::string16 device_type = chromeos::GetChromeDeviceType();
96#else
97  // TODO(isherman): Set an appropriate device name for non-ChromeOS devices.
98  const base::string16 device_type = base::ASCIIToUTF16("Chromeschnozzle");
99#endif  // defined(OS_CHROMEOS)
100
101  // Common strings.
102  strings->SetString(
103      "learnMoreLinkTitle",
104      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_LEARN_MORE_LINK_TITLE));
105
106  // Setup notification strings.
107  strings->SetString(
108      "setupNotificationTitle",
109      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_TITLE,
110                                 device_type));
111  strings->SetString(
112      "setupNotificationMessage",
113      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_MESSAGE,
114                                 device_type));
115  strings->SetString(
116      "setupNotificationButtonTitle",
117      l10n_util::GetStringUTF16(
118          IDS_EASY_UNLOCK_SETUP_NOTIFICATION_BUTTON_TITLE));
119
120  // Success notification strings.
121  strings->SetString(
122      "successNotificationTitle",
123      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_SUCCESS_NOTIFICATION_TITLE));
124  strings->SetString(
125      "successNotificationMessage",
126      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SUCCESS_NOTIFICATION_MESSAGE,
127                                 device_type));
128
129  // Chromebook added to Easy Unlock notification strings.
130  strings->SetString(
131      "chromebookAddedNotificationTitle",
132      l10n_util::GetStringUTF16(
133          IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_TITLE));
134  strings->SetString(
135     "chromebookAddedNotificationMessage",
136     l10n_util::GetStringFUTF16(
137          IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_MESSAGE,
138          device_type));
139
140  // Setup dialog strings.
141  // Step 1: Intro.
142  strings->SetString(
143      "setupIntroHeaderTitle",
144      l10n_util::GetStringFUTF16(
145          IDS_EASY_UNLOCK_SETUP_INTRO_HEADER_TITLE, device_type));
146  strings->SetString(
147      "setupIntroHeaderText",
148      l10n_util::GetStringFUTF16(
149          IDS_EASY_UNLOCK_SETUP_INTRO_HEADER_TEXT, device_type));
150  strings->SetString(
151      "setupIntroHeaderFootnote",
152      l10n_util::GetStringUTF16(
153          IDS_EASY_UNLOCK_SETUP_INTRO_HEADER_FOOTNOTE));
154  strings->SetString(
155      "setupIntroFindPhoneButtonLabel",
156      l10n_util::GetStringUTF16(
157          IDS_EASY_UNLOCK_SETUP_INTRO_FIND_PHONE_BUTTON_LABEL));
158  strings->SetString(
159      "setupIntroFindingPhoneButtonLabel",
160      l10n_util::GetStringUTF16(
161          IDS_EASY_UNLOCK_SETUP_INTRO_FINDING_PHONE_BUTTON_LABEL));
162  strings->SetString(
163      "setupIntroRetryFindPhoneButtonLabel",
164      l10n_util::GetStringUTF16(
165          IDS_EASY_UNLOCK_SETUP_INTRO_RETRY_FIND_PHONE_BUTTON_LABEL));
166  strings->SetString(
167      "setupIntroHowIsThisSecureLinkText",
168      l10n_util::GetStringUTF16(
169          IDS_EASY_UNLOCK_SETUP_INTRO_HOW_IS_THIS_SECURE_LINK_TEXT));
170  // Step 2: Found a viable phone.
171  strings->SetString(
172      "setupFoundPhoneHeaderTitle",
173      l10n_util::GetStringFUTF16(
174          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_HEADER_TITLE, device_type));
175  strings->SetString(
176      "setupFoundPhoneHeaderText",
177      l10n_util::GetStringFUTF16(
178          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_HEADER_TEXT, device_type));
179  strings->SetString(
180      "setupFoundPhoneUseThisPhoneButtonLabel",
181      l10n_util::GetStringUTF16(
182          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_USE_THIS_PHONE_BUTTON_LABEL));
183  strings->SetString(
184      "setupPairingPhoneFailedButtonLabel",
185      l10n_util::GetStringUTF16(
186          IDS_EASY_UNLOCK_SETUP_PAIRING_PHONE_FAILED_BUTTON_LABEL));
187  // Step 3: Setup completed successfully.
188  strings->SetString(
189      "setupCompleteHeaderTitle",
190      l10n_util::GetStringUTF16(
191          IDS_EASY_UNLOCK_SETUP_COMPLETE_HEADER_TITLE));
192  strings->SetString(
193      "setupCompleteHeaderText",
194      l10n_util::GetStringUTF16(
195          IDS_EASY_UNLOCK_SETUP_COMPLETE_HEADER_TEXT));
196  strings->SetString(
197      "setupCompleteTryItOutButtonLabel",
198      l10n_util::GetStringUTF16(
199          IDS_EASY_UNLOCK_SETUP_COMPLETE_TRY_IT_OUT_BUTTON_LABEL));
200  strings->SetString(
201      "setupCompleteSettingsLinkText",
202      l10n_util::GetStringUTF16(
203          IDS_EASY_UNLOCK_SETUP_COMPLETE_SETTINGS_LINK_TEXT));
204
205  // Error strings.
206  strings->SetString(
207      "setupErrorBluetoothUnavailable",
208      l10n_util::GetStringFUTF16(
209          IDS_EASY_UNLOCK_SETUP_ERROR_BLUETOOTH_UNAVAILBLE, device_type));
210  strings->SetString(
211      "setupErrorOffline",
212      l10n_util::GetStringFUTF16(
213          IDS_EASY_UNLOCK_SETUP_ERROR_OFFLINE, device_type));
214  strings->SetString(
215      "setupErrorFindingPhone",
216      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_SETUP_ERROR_FINDING_PHONE));
217  strings->SetString(
218      "setupErrorBluetoothConnectionFailed",
219      l10n_util::GetStringFUTF16(
220          IDS_EASY_UNLOCK_SETUP_ERROR_BLUETOOTH_CONNECTION_FAILED,
221          device_type));
222  strings->SetString(
223      "setupErrorConnectionToPhoneTimeout",
224       l10n_util::GetStringFUTF16(
225           IDS_EASY_UNLOCK_SETUP_ERROR_CONNECT_TO_PHONE_TIMEOUT,
226           device_type));
227  strings->SetString(
228      "setupErrorSyncPhoneState",
229       l10n_util::GetStringUTF16(
230           IDS_EASY_UNLOCK_SETUP_ERROR_SYNC_PHONE_STATE_FAILED));
231  strings->SetString(
232      "setupErrorConnectingToPhone",
233      l10n_util::GetStringFUTF16(
234          IDS_EASY_UNLOCK_SETUP_ERROR_CONNECTING_TO_PHONE, device_type));
235
236  // TODO(isherman): Remove these strings once the app has been updated.
237  strings->SetString(
238      "notificationTitle",
239      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_TITLE,
240                                 device_type));
241  strings->SetString(
242      "notificationMessage",
243      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_MESSAGE,
244                                 device_type));
245  strings->SetString(
246      "notificationButtonTitle",
247      l10n_util::GetStringUTF16(
248          IDS_EASY_UNLOCK_SETUP_NOTIFICATION_BUTTON_TITLE));
249
250  SetResult(strings.release());
251  return true;
252}
253
254EasyUnlockPrivatePerformECDHKeyAgreementFunction::
255EasyUnlockPrivatePerformECDHKeyAgreementFunction() {}
256
257EasyUnlockPrivatePerformECDHKeyAgreementFunction::
258~EasyUnlockPrivatePerformECDHKeyAgreementFunction() {}
259
260bool EasyUnlockPrivatePerformECDHKeyAgreementFunction::RunAsync() {
261  scoped_ptr<easy_unlock_private::PerformECDHKeyAgreement::Params> params =
262      easy_unlock_private::PerformECDHKeyAgreement::Params::Create(*args_);
263  EXTENSION_FUNCTION_VALIDATE(params);
264
265  GetCryptoDelegate(browser_context())->PerformECDHKeyAgreement(
266      params->private_key,
267      params->public_key,
268      base::Bind(&EasyUnlockPrivatePerformECDHKeyAgreementFunction::OnData,
269                 this));
270  return true;
271}
272
273void EasyUnlockPrivatePerformECDHKeyAgreementFunction::OnData(
274    const std::string& secret_key) {
275  // TODO(tbarzic): Improve error handling.
276  if (!secret_key.empty()) {
277    results_ = easy_unlock_private::PerformECDHKeyAgreement::Results::Create(
278        secret_key);
279  }
280  SendResponse(true);
281}
282
283EasyUnlockPrivateGenerateEcP256KeyPairFunction::
284EasyUnlockPrivateGenerateEcP256KeyPairFunction() {}
285
286EasyUnlockPrivateGenerateEcP256KeyPairFunction::
287~EasyUnlockPrivateGenerateEcP256KeyPairFunction() {}
288
289bool EasyUnlockPrivateGenerateEcP256KeyPairFunction::RunAsync() {
290  GetCryptoDelegate(browser_context())->GenerateEcP256KeyPair(
291      base::Bind(&EasyUnlockPrivateGenerateEcP256KeyPairFunction::OnData,
292                 this));
293  return true;
294}
295
296void EasyUnlockPrivateGenerateEcP256KeyPairFunction::OnData(
297    const std::string& private_key,
298    const std::string& public_key) {
299  // TODO(tbarzic): Improve error handling.
300  if (!public_key.empty() && !private_key.empty()) {
301    results_ = easy_unlock_private::GenerateEcP256KeyPair::Results::Create(
302        public_key, private_key);
303  }
304  SendResponse(true);
305}
306
307EasyUnlockPrivateCreateSecureMessageFunction::
308EasyUnlockPrivateCreateSecureMessageFunction() {}
309
310EasyUnlockPrivateCreateSecureMessageFunction::
311~EasyUnlockPrivateCreateSecureMessageFunction() {}
312
313bool EasyUnlockPrivateCreateSecureMessageFunction::RunAsync() {
314  scoped_ptr<easy_unlock_private::CreateSecureMessage::Params> params =
315      easy_unlock_private::CreateSecureMessage::Params::Create(*args_);
316  EXTENSION_FUNCTION_VALIDATE(params);
317
318  GetCryptoDelegate(browser_context())->CreateSecureMessage(
319      params->payload,
320      params->key,
321      params->options.associated_data ?
322          *params->options.associated_data : std::string(),
323      params->options.public_metadata ?
324          *params->options.public_metadata : std::string(),
325      params->options.verification_key_id ?
326          *params->options.verification_key_id : std::string(),
327      params->options.encrypt_type,
328      params->options.sign_type,
329      base::Bind(&EasyUnlockPrivateCreateSecureMessageFunction::OnData,
330                 this));
331  return true;
332}
333
334void EasyUnlockPrivateCreateSecureMessageFunction::OnData(
335    const std::string& message) {
336  // TODO(tbarzic): Improve error handling.
337  if (!message.empty()) {
338    results_ = easy_unlock_private::CreateSecureMessage::Results::Create(
339        message);
340  }
341  SendResponse(true);
342}
343
344EasyUnlockPrivateUnwrapSecureMessageFunction::
345EasyUnlockPrivateUnwrapSecureMessageFunction() {}
346
347EasyUnlockPrivateUnwrapSecureMessageFunction::
348~EasyUnlockPrivateUnwrapSecureMessageFunction() {}
349
350bool EasyUnlockPrivateUnwrapSecureMessageFunction::RunAsync() {
351  scoped_ptr<easy_unlock_private::UnwrapSecureMessage::Params> params =
352      easy_unlock_private::UnwrapSecureMessage::Params::Create(*args_);
353  EXTENSION_FUNCTION_VALIDATE(params);
354
355  GetCryptoDelegate(browser_context())->UnwrapSecureMessage(
356      params->secure_message,
357      params->key,
358      params->options.associated_data ?
359          *params->options.associated_data : std::string(),
360      params->options.encrypt_type,
361      params->options.sign_type,
362      base::Bind(&EasyUnlockPrivateUnwrapSecureMessageFunction::OnData,
363                 this));
364  return true;
365}
366
367void EasyUnlockPrivateUnwrapSecureMessageFunction::OnData(
368    const std::string& data) {
369  // TODO(tbarzic): Improve error handling.
370  if (!data.empty())
371    results_ = easy_unlock_private::UnwrapSecureMessage::Results::Create(data);
372  SendResponse(true);
373}
374
375EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::
376    EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction() {}
377
378EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::
379    ~EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction() {}
380
381bool EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::RunAsync() {
382  scoped_ptr<easy_unlock_private::SeekBluetoothDeviceByAddress::Params> params(
383      easy_unlock_private::SeekBluetoothDeviceByAddress::Params::Create(
384          *args_));
385  EXTENSION_FUNCTION_VALIDATE(params.get());
386
387  easy_unlock::SeekBluetoothDeviceByAddress(
388      params->device_address,
389      base::Bind(
390          &EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::
391              OnSeekCompleted,
392          this));
393  return true;
394}
395
396void EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekCompleted(
397    const easy_unlock::SeekDeviceResult& seek_result) {
398  if (seek_result.success) {
399    SendResponse(true);
400  } else {
401    SetError(seek_result.error_message);
402    SendResponse(false);
403  }
404}
405
406EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
407    EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}
408
409EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
410    ~EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}
411
412void EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
413    ConnectToService(device::BluetoothDevice* device,
414                     const device::BluetoothUUID& uuid) {
415  easy_unlock::ConnectToBluetoothServiceInsecurely(
416      device,
417      uuid,
418      base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
419                     OnConnect,
420                 this),
421      base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
422                     OnConnectError,
423                 this));
424}
425
426EasyUnlockPrivateUpdateScreenlockStateFunction::
427    EasyUnlockPrivateUpdateScreenlockStateFunction() {}
428
429EasyUnlockPrivateUpdateScreenlockStateFunction::
430    ~EasyUnlockPrivateUpdateScreenlockStateFunction() {}
431
432bool EasyUnlockPrivateUpdateScreenlockStateFunction::RunSync() {
433  scoped_ptr<easy_unlock_private::UpdateScreenlockState::Params> params(
434      easy_unlock_private::UpdateScreenlockState::Params::Create(*args_));
435  EXTENSION_FUNCTION_VALIDATE(params.get());
436
437  EasyUnlockScreenlockStateHandler* screenlock_state_handler =
438      GetScreenlockStateHandler(browser_context());
439  if (screenlock_state_handler) {
440    screenlock_state_handler->ChangeState(
441        ToScreenlockStateHandlerState(params->state));
442    return true;
443  }
444
445  SetError("Not allowed");
446  return false;
447}
448
449EasyUnlockPrivateSetPermitAccessFunction::
450    EasyUnlockPrivateSetPermitAccessFunction() {
451}
452
453EasyUnlockPrivateSetPermitAccessFunction::
454    ~EasyUnlockPrivateSetPermitAccessFunction() {
455}
456
457bool EasyUnlockPrivateSetPermitAccessFunction::RunSync() {
458  scoped_ptr<easy_unlock_private::SetPermitAccess::Params> params(
459      easy_unlock_private::SetPermitAccess::Params::Create(*args_));
460  EXTENSION_FUNCTION_VALIDATE(params.get());
461
462  Profile* profile = Profile::FromBrowserContext(browser_context());
463  EasyUnlockService::Get(profile)
464      ->SetPermitAccess(*params->permit_access.ToValue());
465
466  return true;
467}
468
469EasyUnlockPrivateGetPermitAccessFunction::
470    EasyUnlockPrivateGetPermitAccessFunction() {
471}
472
473EasyUnlockPrivateGetPermitAccessFunction::
474    ~EasyUnlockPrivateGetPermitAccessFunction() {
475}
476
477bool EasyUnlockPrivateGetPermitAccessFunction::RunSync() {
478  Profile* profile = Profile::FromBrowserContext(browser_context());
479  const base::DictionaryValue* permit_value =
480      EasyUnlockService::Get(profile)->GetPermitAccess();
481  if (permit_value) {
482    scoped_ptr<easy_unlock_private::PermitRecord> permit =
483        easy_unlock_private::PermitRecord::FromValue(*permit_value);
484    results_ = easy_unlock_private::GetPermitAccess::Results::Create(*permit);
485  }
486
487  return true;
488}
489
490EasyUnlockPrivateClearPermitAccessFunction::
491    EasyUnlockPrivateClearPermitAccessFunction() {
492}
493
494EasyUnlockPrivateClearPermitAccessFunction::
495    ~EasyUnlockPrivateClearPermitAccessFunction() {
496}
497
498bool EasyUnlockPrivateClearPermitAccessFunction::RunSync() {
499  Profile* profile = Profile::FromBrowserContext(browser_context());
500  EasyUnlockService::Get(profile)->ClearPermitAccess();
501  return true;
502}
503
504EasyUnlockPrivateSetRemoteDevicesFunction::
505    EasyUnlockPrivateSetRemoteDevicesFunction() {
506}
507
508EasyUnlockPrivateSetRemoteDevicesFunction::
509    ~EasyUnlockPrivateSetRemoteDevicesFunction() {
510}
511
512bool EasyUnlockPrivateSetRemoteDevicesFunction::RunSync() {
513  scoped_ptr<easy_unlock_private::SetRemoteDevices::Params> params(
514      easy_unlock_private::SetRemoteDevices::Params::Create(*args_));
515  EXTENSION_FUNCTION_VALIDATE(params.get());
516
517  Profile* profile = Profile::FromBrowserContext(browser_context());
518  if (params->devices.empty()) {
519    EasyUnlockService::Get(profile)->ClearRemoteDevices();
520  } else {
521    base::ListValue devices;
522    for (size_t i = 0; i < params->devices.size(); ++i) {
523      devices.Append(params->devices[i]->ToValue().release());
524    }
525    EasyUnlockService::Get(profile)->SetRemoteDevices(devices);
526  }
527
528  return true;
529}
530
531EasyUnlockPrivateGetRemoteDevicesFunction::
532    EasyUnlockPrivateGetRemoteDevicesFunction() {
533}
534
535EasyUnlockPrivateGetRemoteDevicesFunction::
536    ~EasyUnlockPrivateGetRemoteDevicesFunction() {
537}
538
539bool EasyUnlockPrivateGetRemoteDevicesFunction::RunSync() {
540  Profile* profile = Profile::FromBrowserContext(browser_context());
541  const base::ListValue* devices =
542      EasyUnlockService::Get(profile)->GetRemoteDevices();
543  SetResult(devices ? devices->DeepCopy() : new base::ListValue());
544  return true;
545}
546
547}  // namespace api
548}  // namespace extensions
549