// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h"

#include <utility>
#include <vector>

#include "base/base64url.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h"
#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_crypto_delegate.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
#include "chrome/browser/signin/easy_unlock_service.h"
#include "chrome/browser/signin/easy_unlock_service_regular.h"
#include "chrome/browser/ui/proximity_auth/proximity_auth_error_bubble.h"
#include "chrome/common/extensions/api/easy_unlock_private.h"
#include "chrome/grit/generated_resources.h"
#include "components/proximity_auth/ble/bluetooth_low_energy_connection.h"
#include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h"
#include "components/proximity_auth/bluetooth_throttler_impl.h"
#include "components/proximity_auth/bluetooth_util.h"
#include "components/proximity_auth/cryptauth/cryptauth_device_manager.h"
#include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h"
#include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h"
#include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
#include "components/proximity_auth/cryptauth/secure_message_delegate.h"
#include "components/proximity_auth/logging/logging.h"
#include "components/proximity_auth/proximity_auth_client.h"
#include "components/proximity_auth/remote_device.h"
#include "components/proximity_auth/screenlock_bridge.h"
#include "components/proximity_auth/screenlock_state.h"
#include "components/proximity_auth/switches.h"
#include "components/signin/core/account_id/account_id.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/range/range.h"

#if defined(OS_CHROMEOS)
#include "ash/common/system/chromeos/devicetype_utils.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#endif

using proximity_auth::ScreenlockState;

namespace extensions {

namespace easy_unlock_private = api::easy_unlock_private;

namespace {

static base::LazyInstance<BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI> >
    g_factory = LAZY_INSTANCE_INITIALIZER;

// Utility method for getting the API's crypto delegate.
EasyUnlockPrivateCryptoDelegate* GetCryptoDelegate(
    content::BrowserContext* context) {
  return BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI>::Get(context)
      ->GetCryptoDelegate();
}

EasyUnlockPrivateConnectionManager* GetConnectionManager(
    content::BrowserContext* context) {
  return BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI>::Get(context)
      ->get_connection_manager();
}

ScreenlockState ToScreenlockState(easy_unlock_private::State state) {
  switch (state) {
    case easy_unlock_private::STATE_NO_BLUETOOTH:
      return ScreenlockState::NO_BLUETOOTH;
    case easy_unlock_private::STATE_BLUETOOTH_CONNECTING:
      return ScreenlockState::BLUETOOTH_CONNECTING;
    case easy_unlock_private::STATE_NO_PHONE:
      return ScreenlockState::NO_PHONE;
    case easy_unlock_private::STATE_PHONE_NOT_AUTHENTICATED:
      return ScreenlockState::PHONE_NOT_AUTHENTICATED;
    case easy_unlock_private::STATE_PHONE_LOCKED:
      return ScreenlockState::PHONE_LOCKED;
    case easy_unlock_private::STATE_PHONE_UNLOCKABLE:
      return ScreenlockState::PHONE_NOT_LOCKABLE;
    case easy_unlock_private::STATE_PHONE_UNSUPPORTED:
      return ScreenlockState::PHONE_UNSUPPORTED;
    case easy_unlock_private::STATE_RSSI_TOO_LOW:
      return ScreenlockState::RSSI_TOO_LOW;
    case easy_unlock_private::STATE_TX_POWER_TOO_HIGH:
      return ScreenlockState::TX_POWER_TOO_HIGH;
    case easy_unlock_private::STATE_PHONE_LOCKED_AND_TX_POWER_TOO_HIGH:
      return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
    case easy_unlock_private::STATE_AUTHENTICATED:
      return ScreenlockState::AUTHENTICATED;
    default:
      return ScreenlockState::INACTIVE;
  }
}

}  // namespace

// static
BrowserContextKeyedAPIFactory<EasyUnlockPrivateAPI>*
    EasyUnlockPrivateAPI::GetFactoryInstance() {
  return g_factory.Pointer();
}

EasyUnlockPrivateAPI::EasyUnlockPrivateAPI(content::BrowserContext* context)
    : connection_manager_(new EasyUnlockPrivateConnectionManager(context)) {}

EasyUnlockPrivateAPI::~EasyUnlockPrivateAPI() {}

EasyUnlockPrivateCryptoDelegate* EasyUnlockPrivateAPI::GetCryptoDelegate() {
  if (!crypto_delegate_)
    crypto_delegate_ = EasyUnlockPrivateCryptoDelegate::Create();
  return crypto_delegate_.get();
}

EasyUnlockPrivateGetStringsFunction::EasyUnlockPrivateGetStringsFunction() {
}
EasyUnlockPrivateGetStringsFunction::~EasyUnlockPrivateGetStringsFunction() {
}

ExtensionFunction::ResponseAction EasyUnlockPrivateGetStringsFunction::Run() {
  std::unique_ptr<base::DictionaryValue> strings(new base::DictionaryValue);

#if defined(OS_CHROMEOS)
  const base::string16 device_type = ash::GetChromeOSDeviceName();
#else
  // TODO(isherman): Set an appropriate device name for non-ChromeOS devices.
  const base::string16 device_type = base::ASCIIToUTF16("Chromeschnozzle");
#endif  // defined(OS_CHROMEOS)

#if defined(OS_CHROMEOS)
  const user_manager::UserManager* manager = user_manager::UserManager::Get();
  const user_manager::User* user = manager ? manager->GetActiveUser() : NULL;
  const std::string user_email_utf8 =
      user ? user->display_email() : std::string();
  const base::string16 user_email = base::UTF8ToUTF16(user_email_utf8);
#else
  // TODO(isherman): Set an appropriate user display email for non-ChromeOS
  // platforms.
  const base::string16 user_email = base::UTF8ToUTF16("superman@example.com");
#endif  // defined(OS_CHROMEOS)

  // Common strings.
  strings->SetString(
      "learnMoreLinkTitle",
      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_LEARN_MORE_LINK_TITLE));
  strings->SetString("deviceType", device_type);

  // Setup notification strings.
  strings->SetString(
      "setupNotificationTitle",
      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_TITLE));
  strings->SetString(
      "setupNotificationMessage",
      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_NOTIFICATION_MESSAGE,
                                 device_type));
  strings->SetString(
      "setupNotificationButtonTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_NOTIFICATION_BUTTON_TITLE));

  // Chromebook added to Easy Unlock notification strings.
  strings->SetString(
      "chromebookAddedNotificationTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_TITLE));
  strings->SetString(
      "chromebookAddedNotificationMessage",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_MESSAGE,
          device_type));
  strings->SetString(
      "chromebookAddedNotificationAboutButton",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_ABOUT_BUTTON));

  // Shared "Learn more" button for the pairing changed and pairing change
  // applied notification.
  strings->SetString(
      "phoneChangedNotificationLearnMoreButton",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_NOTIFICATION_LEARN_MORE_BUTTON));

  // Pairing changed notification strings.
  strings->SetString(
      "phoneChangedNotificationTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_PAIRING_CHANGED_NOTIFICATION_TITLE));
  strings->SetString(
      "phoneChangedNotificationMessage",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_PAIRING_CHANGED_NOTIFICATION_MESSAGE,
          device_type));
  strings->SetString(
      "phoneChangedNotificationUpdateButton",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_PAIRING_CHANGED_NOTIFICATION_UPDATE_BUTTON));

  // Phone change applied notification strings.
  strings->SetString(
      "phoneChangeAppliedNotificationTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_PAIRING_CHANGE_APPLIED_NOTIFICATION_TITLE));
  strings->SetString(
      "phoneChangeAppliedNotificationMessage",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_PAIRING_CHANGE_APPLIED_NOTIFICATION_MESSAGE));

  // Setup dialog strings.
  // Step 1: Intro.
  strings->SetString(
      "setupIntroHeaderTitle",
      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_SETUP_INTRO_HEADER_TITLE));
  strings->SetString(
      "setupIntroHeaderText",
      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_INTRO_HEADER_TEXT,
                                 device_type,
                                 user_email));
  strings->SetString(
      "setupIntroFindPhoneButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_INTRO_FIND_PHONE_BUTTON_LABEL));
  strings->SetString(
      "setupIntroFindingPhoneButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_INTRO_FINDING_PHONE_BUTTON_LABEL));
  strings->SetString(
      "setupIntroRetryFindPhoneButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_INTRO_RETRY_FIND_PHONE_BUTTON_LABEL));
  strings->SetString(
      "setupIntroCloseFindPhoneButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_INTRO_CLOSE_FIND_PHONE_BUTTON_LABEL));
  strings->SetString(
      "setupIntroHowIsThisSecureLinkText",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_INTRO_HOW_IS_THIS_SECURE_LINK_TEXT));
  // Step 1.5: Phone found but is not secured with lock screen
  strings->SetString("setupSecurePhoneHeaderTitle",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_SECURE_PHONE_HEADER_TITLE));
  strings->SetString(
      "setupSecurePhoneHeaderText",
      l10n_util::GetStringFUTF16(IDS_EASY_UNLOCK_SETUP_SECURE_PHONE_HEADER_TEXT,
                                 device_type));
  strings->SetString("setupSecurePhoneButtonLabel",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_SECURE_PHONE_BUTTON_LABEL));
  strings->SetString("setupSecurePhoneLinkText",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_SECURE_PHONE_LINK_TEXT));
  // Step 2: Found a viable phone.
  strings->SetString(
      "setupFoundPhoneHeaderTitle",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_HEADER_TITLE, device_type));
  strings->SetString(
      "setupFoundPhoneHeaderText",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_HEADER_TEXT, device_type));
  strings->SetString(
      "setupFoundPhoneUseThisPhoneButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_USE_THIS_PHONE_BUTTON_LABEL));
  strings->SetString("setupFoundPhoneDeviceFormattedButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_DEVICE_FORMATTED_BUTTON_LABEL));
  strings->SetString(
      "setupFoundPhoneSwitchPhoneLinkLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_FOUND_PHONE_SWITCH_PHONE_LINK_LABEL));
  strings->SetString(
      "setupPairingPhoneFailedButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_PAIRING_PHONE_FAILED_BUTTON_LABEL));
  // Step 2.5: Recommend user to set up Android Smart Lock
  strings->SetString(
      "setupAndroidSmartLockHeaderTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_HEADER_TITLE));
  strings->SetString(
      "setupAndroidSmartLockHeaderText",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_HEADER_TEXT, device_type));
  strings->SetString(
      "setupAndroidSmartLockDoneButtonText",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_DONE_BUTTON_LABEL));
  strings->SetString(
      "setupAndroidSmartLockAboutLinkText",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_ABOUT_LINK_TEXT));
  // Step 3: Setup completed successfully.
  strings->SetString(
      "setupCompleteHeaderTitle",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_COMPLETE_HEADER_TITLE));
  strings->SetString(
      "setupCompleteHeaderText",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_COMPLETE_HEADER_TEXT));
  strings->SetString(
      "setupCompleteTryItOutButtonLabel",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_COMPLETE_TRY_IT_OUT_BUTTON_LABEL));
  strings->SetString(
      "setupCompleteSettingsLinkText",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_COMPLETE_SETTINGS_LINK_TEXT));
  // Step 4: Post lockscreen confirmation.
  strings->SetString("setupPostLockDismissButtonLabel",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_POST_LOCK_DISMISS_BUTTON_LABEL));

  // Error strings.
  strings->SetString(
      "setupErrorBluetoothUnavailable",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_SETUP_ERROR_BLUETOOTH_UNAVAILBLE, device_type));
  strings->SetString(
      "setupErrorOffline",
      l10n_util::GetStringFUTF16(
          IDS_EASY_UNLOCK_SETUP_ERROR_OFFLINE, device_type));
  strings->SetString(
      "setupErrorRemoteSoftwareOutOfDate",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_ERROR_REMOTE_SOFTWARE_OUT_OF_DATE));
  strings->SetString(
      "setupErrorRemoteSoftwareOutOfDateGeneric",
      l10n_util::GetStringUTF16(
          IDS_EASY_UNLOCK_SETUP_ERROR_REMOTE_SOFTWARE_OUT_OF_DATE_GENERIC));
  strings->SetString(
      "setupErrorFindingPhone",
      l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_SETUP_ERROR_FINDING_PHONE));
  strings->SetString("setupErrorSyncPhoneState",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_ERROR_SYNC_PHONE_STATE_FAILED));
  strings->SetString("setupErrorConnectingToPhone",
                     l10n_util::GetStringUTF16(
                         IDS_EASY_UNLOCK_SETUP_ERROR_CONNECTING_TO_PHONE));

  return RespondNow(OneArgument(std::move(strings)));
}

EasyUnlockPrivatePerformECDHKeyAgreementFunction::
EasyUnlockPrivatePerformECDHKeyAgreementFunction() {}

EasyUnlockPrivatePerformECDHKeyAgreementFunction::
~EasyUnlockPrivatePerformECDHKeyAgreementFunction() {}

bool EasyUnlockPrivatePerformECDHKeyAgreementFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::PerformECDHKeyAgreement::Params> params =
      easy_unlock_private::PerformECDHKeyAgreement::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  GetCryptoDelegate(browser_context())->PerformECDHKeyAgreement(
      *params,
      base::Bind(&EasyUnlockPrivatePerformECDHKeyAgreementFunction::OnData,
                 this));
  return true;
}

void EasyUnlockPrivatePerformECDHKeyAgreementFunction::OnData(
    const std::string& secret_key) {
  // TODO(tbarzic): Improve error handling.
  if (!secret_key.empty()) {
    results_ = easy_unlock_private::PerformECDHKeyAgreement::Results::Create(
        std::vector<char>(secret_key.begin(), secret_key.end()));
  }
  SendResponse(true);
}

EasyUnlockPrivateGenerateEcP256KeyPairFunction::
EasyUnlockPrivateGenerateEcP256KeyPairFunction() {}

EasyUnlockPrivateGenerateEcP256KeyPairFunction::
~EasyUnlockPrivateGenerateEcP256KeyPairFunction() {}

bool EasyUnlockPrivateGenerateEcP256KeyPairFunction::RunAsync() {
  GetCryptoDelegate(browser_context())->GenerateEcP256KeyPair(
      base::Bind(&EasyUnlockPrivateGenerateEcP256KeyPairFunction::OnData,
                 this));
  return true;
}

void EasyUnlockPrivateGenerateEcP256KeyPairFunction::OnData(
    const std::string& private_key,
    const std::string& public_key) {
  // TODO(tbarzic): Improve error handling.
  if (!public_key.empty() && !private_key.empty()) {
    results_ = easy_unlock_private::GenerateEcP256KeyPair::Results::Create(
        std::vector<char>(public_key.begin(), public_key.end()),
        std::vector<char>(private_key.begin(), private_key.end()));
  }
  SendResponse(true);
}

EasyUnlockPrivateCreateSecureMessageFunction::
EasyUnlockPrivateCreateSecureMessageFunction() {}

EasyUnlockPrivateCreateSecureMessageFunction::
~EasyUnlockPrivateCreateSecureMessageFunction() {}

bool EasyUnlockPrivateCreateSecureMessageFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::CreateSecureMessage::Params> params =
      easy_unlock_private::CreateSecureMessage::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  GetCryptoDelegate(browser_context())->CreateSecureMessage(
      *params,
      base::Bind(&EasyUnlockPrivateCreateSecureMessageFunction::OnData,
                 this));
  return true;
}

void EasyUnlockPrivateCreateSecureMessageFunction::OnData(
    const std::string& message) {
  // TODO(tbarzic): Improve error handling.
  if (!message.empty()) {
    results_ = easy_unlock_private::CreateSecureMessage::Results::Create(
        std::vector<char>(message.begin(), message.end()));
  }
  SendResponse(true);
}

EasyUnlockPrivateUnwrapSecureMessageFunction::
EasyUnlockPrivateUnwrapSecureMessageFunction() {}

EasyUnlockPrivateUnwrapSecureMessageFunction::
~EasyUnlockPrivateUnwrapSecureMessageFunction() {}

bool EasyUnlockPrivateUnwrapSecureMessageFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::UnwrapSecureMessage::Params> params =
      easy_unlock_private::UnwrapSecureMessage::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  GetCryptoDelegate(browser_context())->UnwrapSecureMessage(
      *params,
      base::Bind(&EasyUnlockPrivateUnwrapSecureMessageFunction::OnData,
                 this));
  return true;
}

void EasyUnlockPrivateUnwrapSecureMessageFunction::OnData(
    const std::string& data) {
  // TODO(tbarzic): Improve error handling.
  if (!data.empty()) {
    results_ = easy_unlock_private::UnwrapSecureMessage::Results::Create(
        std::vector<char>(data.begin(), data.end()));
  }
  SendResponse(true);
}

EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::
    EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction() {}

EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::
    ~EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction() {}

bool EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::SeekBluetoothDeviceByAddress::Params>
      params(easy_unlock_private::SeekBluetoothDeviceByAddress::Params::Create(
          *args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  proximity_auth::bluetooth_util::SeekDeviceByAddress(
      params->device_address,
      base::Bind(
          &EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekSuccess,
          this),
      base::Bind(
          &EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekFailure,
          this),
      content::BrowserThread::GetBlockingPool()
          ->GetTaskRunnerWithShutdownBehavior(
              base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)
          .get());
  return true;
}

void EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekSuccess() {
  SendResponse(true);
}

void EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekFailure(
    const std::string& error_message) {
  SetError(error_message);
  SendResponse(false);
}

EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
    EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}

EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
    ~EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}

void EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
    ConnectToService(device::BluetoothDevice* device,
                     const device::BluetoothUUID& uuid) {
  device->ConnectToServiceInsecurely(
      uuid,
      base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
                     OnConnect,
                 this),
      base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
                     OnConnectError,
                 this));
}

EasyUnlockPrivateUpdateScreenlockStateFunction::
    EasyUnlockPrivateUpdateScreenlockStateFunction() {}

EasyUnlockPrivateUpdateScreenlockStateFunction::
    ~EasyUnlockPrivateUpdateScreenlockStateFunction() {}

ExtensionFunction::ResponseAction
EasyUnlockPrivateUpdateScreenlockStateFunction::Run() {
  std::unique_ptr<easy_unlock_private::UpdateScreenlockState::Params> params(
      easy_unlock_private::UpdateScreenlockState::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  Profile* profile = Profile::FromBrowserContext(browser_context());
  if (EasyUnlockService::Get(profile)->UpdateScreenlockState(
          ToScreenlockState(params->state))) {
    return RespondNow(NoArguments());
  }
  return RespondNow(Error("Not allowed"));
}

EasyUnlockPrivateSetPermitAccessFunction::
    EasyUnlockPrivateSetPermitAccessFunction() {
}

EasyUnlockPrivateSetPermitAccessFunction::
    ~EasyUnlockPrivateSetPermitAccessFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetPermitAccessFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetPermitAccess::Params> params(
      easy_unlock_private::SetPermitAccess::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService::Get(profile)
      ->SetPermitAccess(*params->permit_access.ToValue());
  return RespondNow(NoArguments());
}

EasyUnlockPrivateGetPermitAccessFunction::
    EasyUnlockPrivateGetPermitAccessFunction() {
}

EasyUnlockPrivateGetPermitAccessFunction::
    ~EasyUnlockPrivateGetPermitAccessFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateGetPermitAccessFunction::Run() {
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)) {
    return GetPermitAccessForExperiment();
  }

  Profile* profile = Profile::FromBrowserContext(browser_context());
  const base::DictionaryValue* permit_value =
      EasyUnlockService::Get(profile)->GetPermitAccess();

  if (permit_value) {
    std::unique_ptr<easy_unlock_private::PermitRecord> permit =
        easy_unlock_private::PermitRecord::FromValue(*permit_value);
    return RespondNow(ArgumentList(
        easy_unlock_private::GetPermitAccess::Results::Create(*permit)));
  }

  return RespondNow(NoArguments());
}

void EasyUnlockPrivateGetPermitAccessFunction::GetKeyPairForExperiment(
    std::string* user_public_key,
    std::string* user_private_key) {
  Profile* profile = Profile::FromBrowserContext(browser_context());
  proximity_auth::CryptAuthEnrollmentManager* enrollment_manager =
      EasyUnlockService::Get(profile)
          ->proximity_auth_client()
          ->GetCryptAuthEnrollmentManager();
  base::Base64UrlEncode(enrollment_manager->GetUserPublicKey(),
                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                        user_public_key);
  base::Base64UrlEncode(enrollment_manager->GetUserPrivateKey(),
                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                        user_private_key);
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateGetPermitAccessFunction::GetPermitAccessForExperiment() {
  // Check that we are inside a user session.
  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService* easy_unlock_service = EasyUnlockService::Get(profile);
  if (easy_unlock_service->GetType() != EasyUnlockService::TYPE_REGULAR) {
    return RespondNow(
        Error("This function must be called inside a user session."));
  }

  std::string b64_public_key, b64_private_key;
  GetKeyPairForExperiment(&b64_public_key, &b64_private_key);

  // Fill in the permit access JSON dictionary.
  proximity_auth::ProximityAuthClient* client =
      easy_unlock_service->proximity_auth_client();
  std::unique_ptr<base::DictionaryValue> permit_access(
      new base::DictionaryValue());
  permit_access->SetString("permitId",
                           "permit://google.com/" + client->GetAccountId());
  permit_access->SetString("id", b64_public_key);
  permit_access->SetString("type", "access");
  permit_access->SetString("data", b64_private_key);

  PA_LOG(INFO) << "Returning permit access for "
               << "chrome.easyUnlockPrivate.getPermitAccess:\n"
               << "  id: " << b64_public_key;

  return RespondNow(OneArgument(std::move(permit_access)));
}

EasyUnlockPrivateClearPermitAccessFunction::
    EasyUnlockPrivateClearPermitAccessFunction() {
}

EasyUnlockPrivateClearPermitAccessFunction::
    ~EasyUnlockPrivateClearPermitAccessFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateClearPermitAccessFunction::Run() {
  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService::Get(profile)->ClearPermitAccess();
  return RespondNow(NoArguments());
}

EasyUnlockPrivateSetRemoteDevicesFunction::
    EasyUnlockPrivateSetRemoteDevicesFunction() {
}

EasyUnlockPrivateSetRemoteDevicesFunction::
    ~EasyUnlockPrivateSetRemoteDevicesFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetRemoteDevicesFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetRemoteDevices::Params> params(
      easy_unlock_private::SetRemoteDevices::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  Profile* profile = Profile::FromBrowserContext(browser_context());
  base::ListValue devices;
  for (const easy_unlock_private::Device& device : params->devices)
    devices.Append(device.ToValue());

  // Store the BLE device if we are trying out the BLE experiment.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)) {
    EasyUnlockService::Get(profile)->SetRemoteBleDevices(devices);
  } else {
    EasyUnlockService::Get(profile)->SetRemoteDevices(devices);
  }

  return RespondNow(NoArguments());
}

EasyUnlockPrivateGetRemoteDevicesFunction::
    EasyUnlockPrivateGetRemoteDevicesFunction() {
}

EasyUnlockPrivateGetRemoteDevicesFunction::
    ~EasyUnlockPrivateGetRemoteDevicesFunction() {
}

bool EasyUnlockPrivateGetRemoteDevicesFunction::RunAsync() {
  // Return the remote devices stored with the native CryptAuthDeviceManager if
  // we are trying out the BLE experiment.
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)) {
    ReturnDevicesForExperiment();
  } else {
    Profile* profile = Profile::FromBrowserContext(browser_context());
    const base::ListValue* devices =
        EasyUnlockService::Get(profile)->GetRemoteDevices();
    SetResult(devices ? devices->CreateDeepCopy()
                      : base::MakeUnique<base::ListValue>());
    SendResponse(true);
  }

  return true;
}

std::string EasyUnlockPrivateGetRemoteDevicesFunction::GetUserPrivateKey() {
  Profile* profile = Profile::FromBrowserContext(browser_context());
  proximity_auth::ProximityAuthClient* client =
      EasyUnlockService::Get(profile)->proximity_auth_client();
  proximity_auth::CryptAuthEnrollmentManager* enrollment_manager =
      client->GetCryptAuthEnrollmentManager();
  return enrollment_manager->GetUserPrivateKey();
}

std::vector<cryptauth::ExternalDeviceInfo>
EasyUnlockPrivateGetRemoteDevicesFunction::GetUnlockKeys() {
  Profile* profile = Profile::FromBrowserContext(browser_context());
  proximity_auth::ProximityAuthClient* client =
      EasyUnlockService::Get(profile)->proximity_auth_client();
  proximity_auth::CryptAuthDeviceManager* device_manager =
      client->GetCryptAuthDeviceManager();
  return device_manager->unlock_keys();
}

void EasyUnlockPrivateGetRemoteDevicesFunction::ReturnDevicesForExperiment() {
  // Check that we are inside a user profile.
  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService* easy_unlock_service = EasyUnlockService::Get(profile);
  if (easy_unlock_service->GetType() != EasyUnlockService::TYPE_REGULAR) {
    SetError("This function must be called inside a user session.");
    SendResponse(true);
    return;
  }

  // Get the synced unlock key data.
  proximity_auth::ProximityAuthClient* client =
      easy_unlock_service->proximity_auth_client();

  permit_id_ = "permit://google.com/easyunlock/v1/" + client->GetAccountId();
  secure_message_delegate_ = client->CreateSecureMessageDelegate();
  std::vector<cryptauth::ExternalDeviceInfo> unlock_keys = GetUnlockKeys();
  expected_devices_count_ = unlock_keys.size();

  remote_devices_.reset(new base::ListValue());
  if (expected_devices_count_ == 0) {
    SetResult(std::move(remote_devices_));
    SendResponse(true);
    return;
  }

  // If there is a BLE unlock key, then don't return anything, so the app does
  // not try the classic Bluetooth protocol.
  for (const auto& unlock_key : unlock_keys) {
    if (unlock_key.bluetooth_address().empty()) {
      SetResult(std::move(remote_devices_));
      SendResponse(true);
      return;
    }
  }

  // Derive the PSKs for the user's unlock keys.
  PA_LOG(INFO) << "Deriving PSKs for "
               << "chrome.easyUnlockPrivate.getRemoteDevices.\n"
               << "Expecting " << expected_devices_count_ << " devices.";
  for (const auto& unlock_key : unlock_keys) {
    secure_message_delegate_->DeriveKey(
        GetUserPrivateKey(), unlock_key.public_key(),
        base::Bind(
            &EasyUnlockPrivateGetRemoteDevicesFunction::OnPSKDerivedForDevice,
            this, unlock_key));
  }
}

void EasyUnlockPrivateGetRemoteDevicesFunction::OnPSKDerivedForDevice(
    const cryptauth::ExternalDeviceInfo& device,
    const std::string& persistent_symmetric_key) {
  std::string b64_public_key, b64_psk;
  base::Base64UrlEncode(device.public_key(),
                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                        &b64_public_key);
  base::Base64UrlEncode(persistent_symmetric_key,
                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                        &b64_psk);

  // Fill in the JSON dictionary containing a single unlock key's data.
  std::unique_ptr<base::DictionaryValue> device_dictionary(
      new base::DictionaryValue());
  device_dictionary->SetString("name", device.friendly_device_name());
  device_dictionary->SetString("bluetoothAddress", device.bluetooth_address());
  device_dictionary->SetString("psk", b64_psk);

  // Fill in the permit license for the unlock key.
  std::unique_ptr<base::DictionaryValue> permit_license(
      new base::DictionaryValue());
  permit_license->SetString("permitId", permit_id_);
  permit_license->SetString("id", b64_public_key);
  permit_license->SetString("type", "license");
  permit_license->SetString("data", b64_public_key);
  device_dictionary->Set("permitRecord", std::move(permit_license));

  remote_devices_->Append(std::move(device_dictionary));

  // If all PSKs are derived, then return from the API call.
  PA_LOG(INFO) << "Derived PSK for " << b64_public_key << ": "
               << remote_devices_->GetSize() << "/" << expected_devices_count_;
  if (remote_devices_->GetSize() == expected_devices_count_) {
    SetResult(std::move(remote_devices_));
    SendResponse(true);
  }
}

EasyUnlockPrivateGetSignInChallengeFunction::
    EasyUnlockPrivateGetSignInChallengeFunction() {
}

EasyUnlockPrivateGetSignInChallengeFunction::
    ~EasyUnlockPrivateGetSignInChallengeFunction() {
}

bool EasyUnlockPrivateGetSignInChallengeFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::GetSignInChallenge::Params> params(
      easy_unlock_private::GetSignInChallenge::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

#if defined(OS_CHROMEOS)
  Profile* profile = Profile::FromBrowserContext(browser_context());
  const std::string challenge =
      EasyUnlockService::Get(profile)->GetChallenge();
  if (!challenge.empty() && !params->nonce.empty()) {
    EasyUnlockTpmKeyManager* key_manager =
        EasyUnlockTpmKeyManagerFactory::GetInstance()->Get(profile);
    if (!key_manager) {
      SetError("No EasyUnlockTpmKeyManager.");
      return false;
    }
    key_manager->SignUsingTpmKey(
        EasyUnlockService::Get(profile)->GetAccountId(),
        std::string(params->nonce.begin(), params->nonce.end()),
        base::Bind(&EasyUnlockPrivateGetSignInChallengeFunction::OnDone, this,
                   challenge));
  } else {
    OnDone(challenge, std::string());
  }
  return true;
#else  // if !defined(OS_CHROMEOS)
  SetError("Sign-in not supported.");
  return false;
#endif  // defined(OS_CHROMEOS)
}

void EasyUnlockPrivateGetSignInChallengeFunction::OnDone(
    const std::string& challenge,
    const std::string& signed_nonce) {
  results_ = easy_unlock_private::GetSignInChallenge::Results::Create(
      std::vector<char>(challenge.begin(), challenge.end()),
      std::vector<char>(signed_nonce.begin(), signed_nonce.end()));
  SendResponse(true);
}

EasyUnlockPrivateTrySignInSecretFunction::
    EasyUnlockPrivateTrySignInSecretFunction() {
}

EasyUnlockPrivateTrySignInSecretFunction::
    ~EasyUnlockPrivateTrySignInSecretFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateTrySignInSecretFunction::Run() {
  std::unique_ptr<easy_unlock_private::TrySignInSecret::Params> params(
      easy_unlock_private::TrySignInSecret::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService::Get(profile)->FinalizeSignin(std::string(
      params->sign_in_secret.begin(), params->sign_in_secret.end()));
  return RespondNow(NoArguments());
}

EasyUnlockPrivateGetUserInfoFunction::EasyUnlockPrivateGetUserInfoFunction() {
}

EasyUnlockPrivateGetUserInfoFunction::~EasyUnlockPrivateGetUserInfoFunction() {
}

ExtensionFunction::ResponseAction EasyUnlockPrivateGetUserInfoFunction::Run() {
  EasyUnlockService* service =
      EasyUnlockService::Get(Profile::FromBrowserContext(browser_context()));
  std::vector<easy_unlock_private::UserInfo> users;
  const AccountId& account_id = service->GetAccountId();
  if (account_id.is_valid()) {
    easy_unlock_private::UserInfo user;
    user.user_id = account_id.GetUserEmail();
    user.logged_in = service->GetType() == EasyUnlockService::TYPE_REGULAR;
    user.data_ready = user.logged_in || service->GetRemoteDevices() != NULL;

    EasyUnlockService::UserSettings user_settings =
        EasyUnlockService::GetUserSettings(account_id);
    user.require_close_proximity = user_settings.require_close_proximity;

    user.device_user_id = proximity_auth::CalculateDeviceUserId(
        EasyUnlockService::GetDeviceId(), account_id.GetUserEmail());

    user.ble_discovery_enabled =
        base::CommandLine::ForCurrentProcess()->HasSwitch(
            proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery);
    users.push_back(std::move(user));
  }
  return RespondNow(
      ArgumentList(easy_unlock_private::GetUserInfo::Results::Create(users)));
}

EasyUnlockPrivateGetConnectionInfoFunction::
    EasyUnlockPrivateGetConnectionInfoFunction() {
}

EasyUnlockPrivateGetConnectionInfoFunction::
    ~EasyUnlockPrivateGetConnectionInfoFunction() {
}

bool EasyUnlockPrivateGetConnectionInfoFunction::DoWork(
    scoped_refptr<device::BluetoothAdapter> adapter) {
  std::unique_ptr<easy_unlock_private::GetConnectionInfo::Params> params =
      easy_unlock_private::GetConnectionInfo::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  device::BluetoothDevice* device = adapter->GetDevice(params->device_address);

  std::string error;
  if (!device)
    error = "Invalid Bluetooth device.";
  else if (!device->IsConnected())
    error = "Bluetooth device not connected.";

  if (!error.empty()) {
    SetError(error);
    SendResponse(false);
    return true;
  }

  device->GetConnectionInfo(base::Bind(
      &EasyUnlockPrivateGetConnectionInfoFunction::OnConnectionInfo, this));
  return false;
}

void EasyUnlockPrivateGetConnectionInfoFunction::OnConnectionInfo(
    const device::BluetoothDevice::ConnectionInfo& connection_info) {
  std::unique_ptr<base::ListValue> results(new base::ListValue());
  results->AppendInteger(connection_info.rssi);
  results->AppendInteger(connection_info.transmit_power);
  results->AppendInteger(connection_info.max_transmit_power);
  SetResultList(std::move(results));
  SendResponse(true);
}

EasyUnlockPrivateShowErrorBubbleFunction::
    EasyUnlockPrivateShowErrorBubbleFunction() {
}

EasyUnlockPrivateShowErrorBubbleFunction::
    ~EasyUnlockPrivateShowErrorBubbleFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateShowErrorBubbleFunction::Run() {
  content::WebContents* web_contents = GetAssociatedWebContents();
  if (!web_contents)
    return RespondNow(Error("A foreground app window is required."));

  std::unique_ptr<easy_unlock_private::ShowErrorBubble::Params> params(
      easy_unlock_private::ShowErrorBubble::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  if (params->link_range.start < 0 ||
      params->link_range.end < 0 ||
      base::saturated_cast<size_t>(params->link_range.end) >
          params->message.size()) {
    return RespondNow(Error("Invalid link range."));
  }

#if defined(TOOLKIT_VIEWS)
  gfx::Rect anchor_rect(
      params->anchor_rect.left, params->anchor_rect.top,
      params->anchor_rect.width, params->anchor_rect.height);
  anchor_rect +=
      web_contents->GetContainerBounds().OffsetFromOrigin();
  ShowProximityAuthErrorBubble(
      base::UTF8ToUTF16(params->message),
      gfx::Range(params->link_range.start, params->link_range.end),
      GURL(params->link_target), anchor_rect, web_contents);
  return RespondNow(NoArguments());
#else
  return RespondNow(Error("Not supported on non-Views platforms."));
#endif
}

EasyUnlockPrivateHideErrorBubbleFunction::
    EasyUnlockPrivateHideErrorBubbleFunction() {
}

EasyUnlockPrivateHideErrorBubbleFunction::
    ~EasyUnlockPrivateHideErrorBubbleFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateHideErrorBubbleFunction::Run() {
#if defined(TOOLKIT_VIEWS)
  HideProximityAuthErrorBubble();
  return RespondNow(NoArguments());
#else
  return RespondNow(Error("Not supported on non-Views platforms."));
#endif
}

EasyUnlockPrivateSetAutoPairingResultFunction::
    EasyUnlockPrivateSetAutoPairingResultFunction() {
}

EasyUnlockPrivateSetAutoPairingResultFunction::
    ~EasyUnlockPrivateSetAutoPairingResultFunction() {
}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetAutoPairingResultFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetAutoPairingResult::Params> params =
      easy_unlock_private::SetAutoPairingResult::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  std::string error_message;
  if (params->result.error_message)
    error_message = *params->result.error_message;

  Profile* profile = Profile::FromBrowserContext(browser_context());
  EasyUnlockService::Get(profile)
      ->SetAutoPairingResult(params->result.success, error_message);

  return RespondNow(NoArguments());
}

EasyUnlockPrivateFindSetupConnectionFunction::
    EasyUnlockPrivateFindSetupConnectionFunction()
    : bluetooth_throttler_(new proximity_auth::BluetoothThrottlerImpl(
          base::MakeUnique<base::DefaultTickClock>())) {}

EasyUnlockPrivateFindSetupConnectionFunction::
    ~EasyUnlockPrivateFindSetupConnectionFunction() {
  // |connection_finder_| has a raw pointer to |bluetooth_throttler_|, so it
  // should be destroyed first.
  connection_finder_.reset();
  bluetooth_throttler_.reset();
}

void EasyUnlockPrivateFindSetupConnectionFunction::
    OnConnectionFinderTimedOut() {
  SetError("No connection found.");
  connection_finder_.reset();
  SendResponse(false);
}

void EasyUnlockPrivateFindSetupConnectionFunction::OnConnectionFound(
    std::unique_ptr<proximity_auth::Connection> connection) {
  // Connection are not persistent by default.
  std::string device_address = connection->remote_device().bluetooth_address;
  bool persistent = false;
  int connection_id =
      GetConnectionManager(browser_context())
          ->AddConnection(extension(), std::move(connection), persistent);
  results_ = easy_unlock_private::FindSetupConnection::Results::Create(
      connection_id, device_address);
  SendResponse(true);
}

bool EasyUnlockPrivateFindSetupConnectionFunction::RunAsync() {
  std::unique_ptr<easy_unlock_private::FindSetupConnection::Params> params =
      easy_unlock_private::FindSetupConnection::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);

  // Creates a BLE connection finder to look for any device advertising
  // |params->setup_service_uuid|.
  connection_finder_.reset(
      new proximity_auth::BluetoothLowEnergyConnectionFinder(
          proximity_auth::RemoteDevice(), params->setup_service_uuid,
          proximity_auth::BluetoothLowEnergyConnectionFinder::FIND_ANY_DEVICE,
          nullptr, bluetooth_throttler_.get(), 3));

  connection_finder_->Find(base::Bind(
      &EasyUnlockPrivateFindSetupConnectionFunction::OnConnectionFound, this));

  timer_.reset(new base::OneShotTimer());
  timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(params->time_out),
                base::Bind(&EasyUnlockPrivateFindSetupConnectionFunction::
                               OnConnectionFinderTimedOut,
                           this));

  return true;
}

EasyUnlockPrivateSetupConnectionStatusFunction::
    EasyUnlockPrivateSetupConnectionStatusFunction() {}

EasyUnlockPrivateSetupConnectionStatusFunction::
    ~EasyUnlockPrivateSetupConnectionStatusFunction() {}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetupConnectionStatusFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetupConnectionStatus::Params> params =
      easy_unlock_private::SetupConnectionStatus::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);
  api::easy_unlock_private::ConnectionStatus status =
      GetConnectionManager(browser_context())
          ->ConnectionStatus(extension(), params->connection_id);
  if (status == api::easy_unlock_private::CONNECTION_STATUS_NONE)
    return RespondNow(Error("Invalid connectionId"));
  return RespondNow(ArgumentList(
      easy_unlock_private::SetupConnectionStatus::Results::Create(status)));
}

EasyUnlockPrivateSetupConnectionDisconnectFunction::
    EasyUnlockPrivateSetupConnectionDisconnectFunction() {}

EasyUnlockPrivateSetupConnectionDisconnectFunction::
    ~EasyUnlockPrivateSetupConnectionDisconnectFunction() {}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetupConnectionDisconnectFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetupConnectionDisconnect::Params>
      params = easy_unlock_private::SetupConnectionDisconnect::Params::Create(
          *args_);
  EXTENSION_FUNCTION_VALIDATE(params);
  if (!GetConnectionManager(browser_context())
           ->Disconnect(extension(), params->connection_id)) {
    return RespondNow(Error("Invalid connectionId."));
  }
  return RespondNow(NoArguments());
}

EasyUnlockPrivateSetupConnectionSendFunction::
    EasyUnlockPrivateSetupConnectionSendFunction() {}

EasyUnlockPrivateSetupConnectionSendFunction::
    ~EasyUnlockPrivateSetupConnectionSendFunction() {}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetupConnectionSendFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetupConnectionSend::Params> params =
      easy_unlock_private::SetupConnectionSend::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params);
  std::string payload(params->data.begin(), params->data.end());
  if (!GetConnectionManager(browser_context())
           ->SendMessage(extension(), params->connection_id, payload)) {
    return RespondNow(Error("Invalid connectionId."));
  }
  return RespondNow(NoArguments());
}

EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::
    EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction() {}

EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::
    ~EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction() {}

ExtensionFunction::ResponseAction
EasyUnlockPrivateSetupConnectionGetDeviceAddressFunction::Run() {
  std::unique_ptr<easy_unlock_private::SetupConnectionGetDeviceAddress::Params>
      params =
          easy_unlock_private::SetupConnectionGetDeviceAddress::Params::Create(
              *args_);
  EXTENSION_FUNCTION_VALIDATE(params);
  std::string device_address =
      GetConnectionManager(browser_context())
          ->GetDeviceAddress(extension(), params->connection_id);
  if (device_address.empty())
    return RespondNow(Error("Invalid connectionId."));
  return RespondNow(ArgumentList(
      easy_unlock_private::SetupConnectionGetDeviceAddress::Results::Create(
          device_address)));
}

}  // namespace extensions
