// Copyright (c) 2012 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 "device/bluetooth/bluetooth_task_manager_win.h"

#include <stddef.h>
#include <winsock2.h>

#include <memory>
#include <string>

#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "device/bluetooth/bluetooth_classic_win.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
#include "net/base/winsock_init.h"

namespace {

const int kNumThreadsInWorkerPool = 3;
const char kBluetoothThreadName[] = "BluetoothPollingThreadWin";
const int kMaxNumDeviceAddressChar = 127;
const int kServiceDiscoveryResultBufferSize = 5000;

// See http://goo.gl/iNTRQe: cTimeoutMultiplier: A value that indicates the time
// out for the inquiry, expressed in increments of 1.28 seconds. For example, an
// inquiry of 12.8 seconds has a cTimeoutMultiplier value of 10. The maximum
// value for this member is 48. When a value greater than 48 is used, the
// calling function immediately fails and returns
const int kMaxDeviceDiscoveryTimeoutMultiplier = 48;

typedef device::BluetoothTaskManagerWin::ServiceRecordState ServiceRecordState;

// Note: The string returned here must have the same format as
// BluetoothDevice::CanonicalizeAddress.
std::string BluetoothAddressToCanonicalString(const BLUETOOTH_ADDRESS& btha) {
  std::string result = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
                                          btha.rgBytes[5],
                                          btha.rgBytes[4],
                                          btha.rgBytes[3],
                                          btha.rgBytes[2],
                                          btha.rgBytes[1],
                                          btha.rgBytes[0]);
  DCHECK_EQ(result, device::BluetoothDevice::CanonicalizeAddress(result));
  return result;
}

bool BluetoothUUIDToWinBLEUUID(const device::BluetoothUUID& uuid,
                               BTH_LE_UUID* out_win_uuid) {
  if (!uuid.IsValid())
    return false;

  if (uuid.format() == device::BluetoothUUID::kFormat16Bit) {
    out_win_uuid->IsShortUuid = TRUE;
    unsigned int data = 0;
    int result = sscanf_s(uuid.value().c_str(), "%04x", &data);
    if (result != 1)
      return false;
    out_win_uuid->Value.ShortUuid = data;
  } else {
    out_win_uuid->IsShortUuid = FALSE;
    unsigned int data[11];
    int result =
        sscanf_s(uuid.value().c_str(),
                 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", &data[0],
                 &data[1], &data[2], &data[3], &data[4], &data[5], &data[6],
                 &data[7], &data[8], &data[9], &data[10]);
    if (result != 11)
      return false;
    out_win_uuid->Value.LongUuid.Data1 = data[0];
    out_win_uuid->Value.LongUuid.Data2 = data[1];
    out_win_uuid->Value.LongUuid.Data3 = data[2];
    out_win_uuid->Value.LongUuid.Data4[0] = data[3];
    out_win_uuid->Value.LongUuid.Data4[1] = data[4];
    out_win_uuid->Value.LongUuid.Data4[2] = data[5];
    out_win_uuid->Value.LongUuid.Data4[3] = data[6];
    out_win_uuid->Value.LongUuid.Data4[4] = data[7];
    out_win_uuid->Value.LongUuid.Data4[5] = data[8];
    out_win_uuid->Value.LongUuid.Data4[6] = data[9];
    out_win_uuid->Value.LongUuid.Data4[7] = data[10];
  }

  return true;
}

// Populates bluetooth adapter state using adapter_handle.
void GetAdapterState(HANDLE adapter_handle,
                     device::BluetoothTaskManagerWin::AdapterState* state) {
  std::string name;
  std::string address;
  bool powered = false;
  BLUETOOTH_RADIO_INFO adapter_info = {sizeof(BLUETOOTH_RADIO_INFO)};
  if (adapter_handle &&
      ERROR_SUCCESS ==
          device::win::BluetoothClassicWrapper::GetInstance()->GetRadioInfo(
              adapter_handle, &adapter_info)) {
    name = base::SysWideToUTF8(adapter_info.szName);
    address = BluetoothAddressToCanonicalString(adapter_info.address);
    powered =
        !!device::win::BluetoothClassicWrapper::GetInstance()->IsConnectable(
            adapter_handle);
  }
  state->name = name;
  state->address = address;
  state->powered = powered;
}

void GetDeviceState(const BLUETOOTH_DEVICE_INFO& device_info,
                    device::BluetoothTaskManagerWin::DeviceState* state) {
  state->name = base::SysWideToUTF8(device_info.szName);
  state->address = BluetoothAddressToCanonicalString(device_info.Address);
  state->bluetooth_class = device_info.ulClassofDevice;
  state->visible = true;
  state->connected = !!device_info.fConnected;
  state->authenticated = !!device_info.fAuthenticated;
}

struct CharacteristicValueChangedRegistration {
  CharacteristicValueChangedRegistration();
  ~CharacteristicValueChangedRegistration();

  BLUETOOTH_GATT_EVENT_HANDLE win_event_handle;
  device::BluetoothTaskManagerWin::GattCharacteristicValueChangedCallback
      callback;
  // The task runner the callback should run on.
  scoped_refptr<base::SequencedTaskRunner> callback_task_runner;
};

CharacteristicValueChangedRegistration::
    CharacteristicValueChangedRegistration() {}
CharacteristicValueChangedRegistration::
    ~CharacteristicValueChangedRegistration() {}

// The key of CharacteristicValueChangedRegistrationMap is a
// GattCharacteristicValueChangedCallback pointer (cast to PVOID) to make it
// unique for different callbacks. It is also the context value passed into OS
// when registering event.
typedef std::unordered_map<
    PVOID,
    std::unique_ptr<CharacteristicValueChangedRegistration>>
    CharacteristicValueChangedRegistrationMap;

CharacteristicValueChangedRegistrationMap
    g_characteristic_value_changed_registrations;
base::Lock g_characteristic_value_changed_registrations_lock;

// Function to be registered to OS to monitor Bluetooth LE GATT event. It is
// invoked in BluetoothApis.dll thread.
void CALLBACK OnGetGattEventWin(BTH_LE_GATT_EVENT_TYPE type,
                                PVOID event_parameter,
                                PVOID context) {
  if (type != CharacteristicValueChangedEvent) {
    // Right now, only characteristic value changed event is supported.
    NOTREACHED();
    return;
  }

  BLUETOOTH_GATT_VALUE_CHANGED_EVENT* event =
      (BLUETOOTH_GATT_VALUE_CHANGED_EVENT*)event_parameter;
  PBTH_LE_GATT_CHARACTERISTIC_VALUE new_value_win = event->CharacteristicValue;
  std::unique_ptr<std::vector<uint8_t>> new_value(
      new std::vector<uint8_t>(new_value_win->DataSize));
  for (ULONG i = 0; i < new_value_win->DataSize; i++)
    (*new_value)[i] = new_value_win->Data[i];

  base::AutoLock auto_lock(g_characteristic_value_changed_registrations_lock);
  CharacteristicValueChangedRegistrationMap::const_iterator it =
      g_characteristic_value_changed_registrations.find(context);
  if (it == g_characteristic_value_changed_registrations.end())
    return;

  it->second->callback_task_runner->PostTask(
      FROM_HERE, base::Bind(it->second->callback, base::Passed(&new_value)));
}

}  // namespace

namespace device {

// static
const int BluetoothTaskManagerWin::kPollIntervalMs = 500;

BluetoothTaskManagerWin::AdapterState::AdapterState() : powered(false) {
}

BluetoothTaskManagerWin::AdapterState::~AdapterState() {
}

BluetoothTaskManagerWin::ServiceRecordState::ServiceRecordState() {
}

BluetoothTaskManagerWin::ServiceRecordState::~ServiceRecordState() {
}

BluetoothTaskManagerWin::DeviceState::DeviceState()
    : visible(false),
      connected(false),
      authenticated(false),
      bluetooth_class(0) {
}

BluetoothTaskManagerWin::DeviceState::~DeviceState() {
}

BluetoothTaskManagerWin::BluetoothTaskManagerWin(
    scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
    : ui_task_runner_(ui_task_runner),
      adapter_handle_(NULL),
      discovering_(false),
      current_logging_batch_count_(0) {}

BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
  win::BluetoothLowEnergyWrapper::DeleteInstance();
  win::BluetoothClassicWrapper::DeleteInstance();
}

BluetoothUUID BluetoothTaskManagerWin::BluetoothLowEnergyUuidToBluetoothUuid(
    const BTH_LE_UUID& bth_le_uuid) {
  if (bth_le_uuid.IsShortUuid) {
    std::string uuid_hex =
        base::StringPrintf("%04x", bth_le_uuid.Value.ShortUuid);
    return BluetoothUUID(uuid_hex);
  } else {
    return BluetoothUUID(base::StringPrintf(
        "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
        bth_le_uuid.Value.LongUuid.Data1, bth_le_uuid.Value.LongUuid.Data2,
        bth_le_uuid.Value.LongUuid.Data3, bth_le_uuid.Value.LongUuid.Data4[0],
        bth_le_uuid.Value.LongUuid.Data4[1],
        bth_le_uuid.Value.LongUuid.Data4[2],
        bth_le_uuid.Value.LongUuid.Data4[3],
        bth_le_uuid.Value.LongUuid.Data4[4],
        bth_le_uuid.Value.LongUuid.Data4[5],
        bth_le_uuid.Value.LongUuid.Data4[6],
        bth_le_uuid.Value.LongUuid.Data4[7]));
  }
}

void BluetoothTaskManagerWin::AddObserver(Observer* observer) {
  DCHECK(observer);
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  observers_.AddObserver(observer);
}

void BluetoothTaskManagerWin::RemoveObserver(Observer* observer) {
  DCHECK(observer);
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  observers_.RemoveObserver(observer);
}

void BluetoothTaskManagerWin::Initialize() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  worker_pool_ = new base::SequencedWorkerPool(
      kNumThreadsInWorkerPool, kBluetoothThreadName,
      base::TaskPriority::USER_VISIBLE);
  InitializeWithBluetoothTaskRunner(
      worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior(
          worker_pool_->GetSequenceToken(),
          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
}

void BluetoothTaskManagerWin::InitializeWithBluetoothTaskRunner(
    scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_ = bluetooth_task_runner;
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::StartPolling, this));
}

void BluetoothTaskManagerWin::StartPolling() {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());

  if (device::bluetooth_init_win::HasBluetoothStack()) {
    PollAdapter();
  } else {
    // IF the bluetooth stack is not available, we still send an empty state
    // to BluetoothAdapter so that it is marked initialized, but the adapter
    // will not be present.
    AdapterState* state = new AdapterState();
    ui_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged,
                 this,
                 base::Owned(state)));
  }
}

void BluetoothTaskManagerWin::Shutdown() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  if (worker_pool_.get())
    worker_pool_->Shutdown();
}

void BluetoothTaskManagerWin::PostSetPoweredBluetoothTask(
    bool powered,
    const base::Closure& callback,
    const BluetoothAdapter::ErrorCallback& error_callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::SetPowered,
                 this,
                 powered,
                 callback,
                 error_callback));
}

void BluetoothTaskManagerWin::PostStartDiscoveryTask() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::StartDiscovery, this));
}

void BluetoothTaskManagerWin::PostStopDiscoveryTask() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::StopDiscovery, this));
}

void BluetoothTaskManagerWin::LogPollingError(const char* message,
                                              int win32_error) {
  const int kLogPeriodInMilliseconds = 60 * 1000;
  const int kMaxMessagesPerLogPeriod = 10;

  // Check if we need to discard this message
  if (!current_logging_batch_ticks_.is_null()) {
    if (base::TimeTicks::Now() - current_logging_batch_ticks_ <=
        base::TimeDelta::FromMilliseconds(kLogPeriodInMilliseconds)) {
      if (current_logging_batch_count_ >= kMaxMessagesPerLogPeriod)
        return;
    } else {
      // The batch expired, reset it to "null".
      current_logging_batch_ticks_ = base::TimeTicks();
    }
  }

  // Keep track of this batch of messages
  if (current_logging_batch_ticks_.is_null()) {
    current_logging_batch_ticks_ = base::TimeTicks::Now();
    current_logging_batch_count_ = 0;
  }
  ++current_logging_batch_count_;

  // Log the message
  if (win32_error == 0)
    LOG(WARNING) << message;
  else
    LOG(WARNING) << message << ": "
                 << logging::SystemErrorCodeToString(win32_error);
}

void BluetoothTaskManagerWin::OnAdapterStateChanged(const AdapterState* state) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  for (auto& observer : observers_)
    observer.AdapterStateChanged(*state);
}

void BluetoothTaskManagerWin::OnDiscoveryStarted(bool success) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  for (auto& observer : observers_)
    observer.DiscoveryStarted(success);
}

void BluetoothTaskManagerWin::OnDiscoveryStopped() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  for (auto& observer : observers_)
    observer.DiscoveryStopped();
}

void BluetoothTaskManagerWin::OnDevicesPolled(
    const ScopedVector<DeviceState>* devices) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  for (auto& observer : observers_)
    observer.DevicesPolled(*devices);
}

void BluetoothTaskManagerWin::PollAdapter() {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());

  // Skips updating the adapter info if the adapter is in discovery mode.
  if (!discovering_) {
    const BLUETOOTH_FIND_RADIO_PARAMS adapter_param =
        { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
    HANDLE temp_adapter_handle;
    HBLUETOOTH_RADIO_FIND handle =
        win::BluetoothClassicWrapper::GetInstance()->FindFirstRadio(
            &adapter_param, &temp_adapter_handle);

    if (handle) {
      adapter_handle_ = temp_adapter_handle;
      GetKnownDevices();
      win::BluetoothClassicWrapper::GetInstance()->FindRadioClose(handle);
    }

    PostAdapterStateToUi();
  }

  // Re-poll.
  bluetooth_task_runner_->PostDelayedTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::PollAdapter,
                 this),
      base::TimeDelta::FromMilliseconds(kPollIntervalMs));
}

void BluetoothTaskManagerWin::PostAdapterStateToUi() {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  AdapterState* state = new AdapterState();
  GetAdapterState(adapter_handle_, state);
  ui_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged,
                 this,
                 base::Owned(state)));
}

void BluetoothTaskManagerWin::SetPowered(
    bool powered,
    const base::Closure& callback,
    const BluetoothAdapter::ErrorCallback& error_callback) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  bool success = false;
  if (adapter_handle_) {
    if (!powered) {
      win::BluetoothClassicWrapper::GetInstance()->EnableDiscovery(
          adapter_handle_, false);
    }
    success = !!win::BluetoothClassicWrapper::GetInstance()
                    ->EnableIncomingConnections(adapter_handle_, powered);
  }

  if (success) {
    PostAdapterStateToUi();
    ui_task_runner_->PostTask(FROM_HERE, callback);
  } else {
    ui_task_runner_->PostTask(FROM_HERE, error_callback);
  }
}

void BluetoothTaskManagerWin::StartDiscovery() {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  ui_task_runner_->PostTask(
      FROM_HERE, base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStarted, this,
                            !!adapter_handle_));
  if (!adapter_handle_)
    return;
  discovering_ = true;

  DiscoverDevices(1);
}

void BluetoothTaskManagerWin::StopDiscovery() {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  discovering_ = false;
  ui_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
}

void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  if (!discovering_ || !adapter_handle_) {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
    return;
  }

  std::unique_ptr<ScopedVector<DeviceState>> device_list(
      new ScopedVector<DeviceState>());
  if (SearchDevices(timeout_multiplier, false, device_list.get())) {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled,
                   this,
                   base::Owned(device_list.release())));
  }

  if (timeout_multiplier < kMaxDeviceDiscoveryTimeoutMultiplier)
    ++timeout_multiplier;
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(
          &BluetoothTaskManagerWin::DiscoverDevices, this, timeout_multiplier));
}

void BluetoothTaskManagerWin::GetKnownDevices() {
  std::unique_ptr<ScopedVector<DeviceState>> device_list(
      new ScopedVector<DeviceState>());
  if (SearchDevices(1, true, device_list.get())) {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled,
                   this,
                   base::Owned(device_list.release())));
  }
}

bool BluetoothTaskManagerWin::SearchDevices(
    int timeout_multiplier,
    bool search_cached_devices_only,
    ScopedVector<DeviceState>* device_list) {
  return SearchClassicDevices(
             timeout_multiplier, search_cached_devices_only, device_list) &&
         SearchLowEnergyDevices(device_list) &&
         DiscoverServices(device_list, search_cached_devices_only);
}

bool BluetoothTaskManagerWin::SearchClassicDevices(
    int timeout_multiplier,
    bool search_cached_devices_only,
    ScopedVector<DeviceState>* device_list) {
  // Issues a device inquiry and waits for |timeout_multiplier| * 1.28 seconds.
  BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params;
  ZeroMemory(&device_search_params, sizeof(device_search_params));
  device_search_params.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
  device_search_params.fReturnAuthenticated = 1;
  device_search_params.fReturnRemembered = 1;
  device_search_params.fReturnUnknown = (search_cached_devices_only ? 0 : 1);
  device_search_params.fReturnConnected = 1;
  device_search_params.fIssueInquiry = (search_cached_devices_only ? 0 : 1);
  device_search_params.cTimeoutMultiplier = timeout_multiplier;

  BLUETOOTH_DEVICE_INFO device_info;
  ZeroMemory(&device_info, sizeof(device_info));
  device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
  HBLUETOOTH_DEVICE_FIND handle =
      win::BluetoothClassicWrapper::GetInstance()->FindFirstDevice(
          &device_search_params, &device_info);
  if (!handle) {
    int last_error = win::BluetoothClassicWrapper::GetInstance()->LastError();
    if (last_error == ERROR_NO_MORE_ITEMS) {
      return true;  // No devices is not an error.
    }
    LogPollingError("Error calling BluetoothFindFirstDevice", last_error);
    return false;
  }

  while (true) {
    DeviceState* device_state = new DeviceState();
    GetDeviceState(device_info, device_state);
    device_list->push_back(device_state);

    // Reset device info before next call (as a safety precaution).
    ZeroMemory(&device_info, sizeof(device_info));
    device_info.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
    if (!win::BluetoothClassicWrapper::GetInstance()->FindNextDevice(
            handle, &device_info)) {
      int last_error = win::BluetoothClassicWrapper::GetInstance()->LastError();
      if (last_error == ERROR_NO_MORE_ITEMS) {
        break;  // No more items is expected error when done enumerating.
      }
      LogPollingError("Error calling BluetoothFindNextDevice", last_error);
      win::BluetoothClassicWrapper::GetInstance()->FindDeviceClose(handle);
      return false;
    }
  }

  if (!win::BluetoothClassicWrapper::GetInstance()->FindDeviceClose(handle)) {
    LogPollingError("Error calling BluetoothFindDeviceClose",
                    win::BluetoothClassicWrapper::GetInstance()->LastError());
    return false;
  }
  return true;
}

bool BluetoothTaskManagerWin::SearchLowEnergyDevices(
    ScopedVector<DeviceState>* device_list) {
  if (!win::BluetoothLowEnergyWrapper::GetInstance()
           ->IsBluetoothLowEnergySupported()) {
    return true;  // Bluetooth LE not supported is not an error.
  }

  ScopedVector<win::BluetoothLowEnergyDeviceInfo> btle_devices;
  std::string error;
  bool success =
      win::BluetoothLowEnergyWrapper::GetInstance()
          ->EnumerateKnownBluetoothLowEnergyDevices(&btle_devices, &error);
  if (!success) {
    LogPollingError(error.c_str(), 0);
    return false;
  }

  for (ScopedVector<win::BluetoothLowEnergyDeviceInfo>::iterator iter =
           btle_devices.begin();
       iter != btle_devices.end();
       ++iter) {
    win::BluetoothLowEnergyDeviceInfo* device_info = (*iter);
    DeviceState* device_state = new DeviceState();
    device_state->name = device_info->friendly_name;
    device_state->address =
        BluetoothAddressToCanonicalString(device_info->address);
    device_state->visible = device_info->visible;
    device_state->authenticated = device_info->authenticated;
    device_state->connected = device_info->connected;
    device_state->path = device_info->path;
    device_list->push_back(device_state);
  }
  return true;
}

bool BluetoothTaskManagerWin::DiscoverServices(
    ScopedVector<DeviceState>* device_list,
    bool search_cached_services_only) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  net::EnsureWinsockInit();
  for (ScopedVector<DeviceState>::iterator iter = device_list->begin();
      iter != device_list->end();
      ++iter) {
    DeviceState* device = (*iter);
    ScopedVector<ServiceRecordState>* service_record_states =
        &(*iter)->service_record_states;

    if ((*iter)->is_bluetooth_classic()) {
      if (!DiscoverClassicDeviceServices(device->address,
                                         L2CAP_PROTOCOL_UUID,
                                         search_cached_services_only,
                                         service_record_states)) {
        return false;
      }
    } else {
      if (!DiscoverLowEnergyDeviceServices(device->path,
                                           service_record_states)) {
        return false;
      }
      if (!SearchForGattServiceDevicePaths(device->address,
                                           service_record_states)) {
        return false;
      }
    }
  }
  return true;
}

bool BluetoothTaskManagerWin::DiscoverClassicDeviceServices(
    const std::string& device_address,
    const GUID& protocol_uuid,
    bool search_cached_services_only,
    ScopedVector<ServiceRecordState>* service_record_states) {
  int error_code =
      DiscoverClassicDeviceServicesWorker(device_address,
                                          protocol_uuid,
                                          search_cached_services_only,
                                          service_record_states);
  // If the device is "offline", no services are returned when specifying
  // "LUP_FLUSHCACHE". Try again without flushing the cache so that the list
  // of previously known services is returned.
  if (!search_cached_services_only &&
      (error_code == WSASERVICE_NOT_FOUND || error_code == WSANO_DATA)) {
    error_code = DiscoverClassicDeviceServicesWorker(
        device_address, protocol_uuid, true, service_record_states);
  }

  return (error_code == ERROR_SUCCESS);
}

int BluetoothTaskManagerWin::DiscoverClassicDeviceServicesWorker(
    const std::string& device_address,
    const GUID& protocol_uuid,
    bool search_cached_services_only,
    ScopedVector<ServiceRecordState>* service_record_states) {
  // Bluetooth and WSAQUERYSET for Service Inquiry. See http://goo.gl/2v9pyt.
  WSAQUERYSET sdp_query;
  ZeroMemory(&sdp_query, sizeof(sdp_query));
  sdp_query.dwSize = sizeof(sdp_query);
  GUID protocol = protocol_uuid;
  sdp_query.lpServiceClassId = &protocol;
  sdp_query.dwNameSpace = NS_BTH;
  wchar_t device_address_context[kMaxNumDeviceAddressChar];
  std::size_t length = base::SysUTF8ToWide("(" + device_address + ")").copy(
      device_address_context, kMaxNumDeviceAddressChar);
  device_address_context[length] = NULL;
  sdp_query.lpszContext = device_address_context;
  DWORD control_flags = LUP_RETURN_ALL;
  // See http://goo.gl/t1Hulo: "Applications should generally specify
  // LUP_FLUSHCACHE. This flag instructs the system to ignore any cached
  // information and establish an over-the-air SDP connection to the specified
  // device to perform the SDP search. This non-cached operation may take
  // several seconds (whereas a cached search returns quickly)."
  // In summary, we need to specify LUP_FLUSHCACHE if we want to obtain the list
  // of services for devices which have not been discovered before.
  if (!search_cached_services_only)
    control_flags |= LUP_FLUSHCACHE;
  HANDLE sdp_handle;
  if (ERROR_SUCCESS !=
      WSALookupServiceBegin(&sdp_query, control_flags, &sdp_handle)) {
    int last_error = WSAGetLastError();
    // If the device is "offline", no services are returned when specifying
    // "LUP_FLUSHCACHE". Don't log error in that case.
    if (!search_cached_services_only &&
        (last_error == WSASERVICE_NOT_FOUND || last_error == WSANO_DATA)) {
      return last_error;
    }
    LogPollingError("Error calling WSALookupServiceBegin", last_error);
    return last_error;
  }
  char sdp_buffer[kServiceDiscoveryResultBufferSize];
  LPWSAQUERYSET sdp_result_data = reinterpret_cast<LPWSAQUERYSET>(sdp_buffer);
  while (true) {
    DWORD sdp_buffer_size = sizeof(sdp_buffer);
    if (ERROR_SUCCESS !=
        WSALookupServiceNext(
            sdp_handle, control_flags, &sdp_buffer_size, sdp_result_data)) {
      int last_error = WSAGetLastError();
      if (last_error == WSA_E_NO_MORE || last_error == WSAENOMORE) {
        break;
      }
      LogPollingError("Error calling WSALookupServiceNext", last_error);
      WSALookupServiceEnd(sdp_handle);
      return last_error;
    }
    ServiceRecordState* service_record_state = new ServiceRecordState();
    service_record_state->name =
        base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
    for (uint64_t i = 0; i < sdp_result_data->lpBlob->cbSize; i++) {
      service_record_state->sdp_bytes.push_back(
          sdp_result_data->lpBlob->pBlobData[i]);
    }
    service_record_states->push_back(service_record_state);
  }
  if (ERROR_SUCCESS != WSALookupServiceEnd(sdp_handle)) {
    int last_error = WSAGetLastError();
    LogPollingError("Error calling WSALookupServiceEnd", last_error);
    return last_error;
  }

  return ERROR_SUCCESS;
}

bool BluetoothTaskManagerWin::DiscoverLowEnergyDeviceServices(
    const base::FilePath& device_path,
    ScopedVector<ServiceRecordState>* service_record_states) {
  if (!win::BluetoothLowEnergyWrapper::GetInstance()
           ->IsBluetoothLowEnergySupported()) {
    return true;  // Bluetooth LE not supported is not an error.
  }

  std::string error;
  ScopedVector<win::BluetoothLowEnergyServiceInfo> services;
  bool success = win::BluetoothLowEnergyWrapper::GetInstance()
                     ->EnumerateKnownBluetoothLowEnergyServices(
                         device_path, &services, &error);
  if (!success) {
    LogPollingError(error.c_str(), 0);
    return false;
  }

  for (ScopedVector<win::BluetoothLowEnergyServiceInfo>::iterator iter2 =
           services.begin();
       iter2 != services.end();
       ++iter2) {
    ServiceRecordState* service_state = new ServiceRecordState();
    service_state->gatt_uuid =
        BluetoothLowEnergyUuidToBluetoothUuid((*iter2)->uuid);
    service_state->attribute_handle = (*iter2)->attribute_handle;
    service_record_states->push_back(service_state);
  }
  return true;
}

// Each GATT service of a BLE device will be listed on the machine as a BLE
// device interface with a matching service attribute handle. This interface
// lists all GATT service devices and matches them back to correspond GATT
// service of the BLE device according to their address and included service
// attribute handles, as we did not find a more neat way to bond them.
bool BluetoothTaskManagerWin::SearchForGattServiceDevicePaths(
    const std::string device_address,
    ScopedVector<ServiceRecordState>* service_record_states) {
  std::string error;

  // List all known GATT service devices on the machine.
  ScopedVector<win::BluetoothLowEnergyDeviceInfo> gatt_service_devices;
  bool success = win::BluetoothLowEnergyWrapper::GetInstance()
                     ->EnumerateKnownBluetoothLowEnergyGattServiceDevices(
                         &gatt_service_devices, &error);
  if (!success) {
    LogPollingError(error.c_str(), 0);
    return false;
  }

  for (auto* gatt_service_device : gatt_service_devices) {
    // Only care about the service devices with |device_address|.
    if (BluetoothAddressToCanonicalString(gatt_service_device->address) !=
        device_address) {
      continue;
    }

    // Discover this service device's contained services.
    ScopedVector<win::BluetoothLowEnergyServiceInfo> gatt_services;
    if (!win::BluetoothLowEnergyWrapper::GetInstance()
             ->EnumerateKnownBluetoothLowEnergyServices(
                 gatt_service_device->path, &gatt_services, &error)) {
      LogPollingError(error.c_str(), 0);
      continue;
    }

    // Usually each service device correspond to one Gatt service.
    if (gatt_services.size() > 1) {
      LOG(WARNING) << "This GATT service device contains more than one ("
                   << gatt_services.size() << ") services";
    }

    // Associate service device to corresponding service record. Attribute
    // handle is unique on one device.
    for (auto* gatt_service : gatt_services) {
      for (auto* service_record_state : *service_record_states) {
        if (service_record_state->attribute_handle ==
            gatt_service->attribute_handle) {
          service_record_state->path = gatt_service_device->path;
          break;
        }
      }
    }
  }

  return true;
}

void BluetoothTaskManagerWin::GetGattIncludedCharacteristics(
    base::FilePath service_path,
    BluetoothUUID uuid,
    uint16_t attribute_handle,
    const GetGattIncludedCharacteristicsCallback& callback) {
  HRESULT hr = S_OK;
  std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC> win_characteristics_info;
  uint16_t number_of_charateristics = 0;

  BTH_LE_GATT_SERVICE win_service;
  if (BluetoothUUIDToWinBLEUUID(uuid, &(win_service.ServiceUuid))) {
    win_service.AttributeHandle = attribute_handle;
    hr = win::BluetoothLowEnergyWrapper::GetInstance()
             ->ReadCharacteristicsOfAService(service_path, &win_service,
                                             &win_characteristics_info,
                                             &number_of_charateristics);
  } else {
    hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  }

  ui_task_runner_->PostTask(
      FROM_HERE, base::Bind(callback, base::Passed(&win_characteristics_info),
                            number_of_charateristics, hr));
}

void BluetoothTaskManagerWin::GetGattIncludedDescriptors(
    base::FilePath service_path,
    BTH_LE_GATT_CHARACTERISTIC characteristic,
    const GetGattIncludedDescriptorsCallback& callback) {
  std::unique_ptr<BTH_LE_GATT_DESCRIPTOR> win_descriptors_info;
  uint16_t number_of_descriptors = 0;

  HRESULT hr =
      win::BluetoothLowEnergyWrapper::GetInstance()
          ->ReadDescriptorsOfACharacteristic(
              service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
              &win_descriptors_info, &number_of_descriptors);

  ui_task_runner_->PostTask(
      FROM_HERE, base::Bind(callback, base::Passed(&win_descriptors_info),
                            number_of_descriptors, hr));
}

void BluetoothTaskManagerWin::ReadGattCharacteristicValue(
    base::FilePath service_path,
    BTH_LE_GATT_CHARACTERISTIC characteristic,
    const ReadGattCharacteristicValueCallback& callback) {
  std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC_VALUE> win_characteristic_value;
  HRESULT hr =
      win::BluetoothLowEnergyWrapper::GetInstance()->ReadCharacteristicValue(
          service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
          &win_characteristic_value);

  ui_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(callback, base::Passed(&win_characteristic_value), hr));
}

void BluetoothTaskManagerWin::WriteGattCharacteristicValue(
    base::FilePath service_path,
    BTH_LE_GATT_CHARACTERISTIC characteristic,
    std::vector<uint8_t> new_value,
    const HResultCallback& callback) {
  ULONG length = (ULONG)(sizeof(ULONG) + new_value.size());
  std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC_VALUE> win_new_value(
      (PBTH_LE_GATT_CHARACTERISTIC_VALUE)(new UCHAR[length]));
  win_new_value->DataSize = (ULONG)new_value.size();
  for (ULONG i = 0; i < new_value.size(); i++)
    win_new_value->Data[i] = new_value[i];

  HRESULT hr =
      win::BluetoothLowEnergyWrapper::GetInstance()->WriteCharacteristicValue(
          service_path, (PBTH_LE_GATT_CHARACTERISTIC)(&characteristic),
          win_new_value.get());

  ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, hr));
}

void BluetoothTaskManagerWin::RegisterGattCharacteristicValueChangedEvent(
    base::FilePath service_path,
    BTH_LE_GATT_CHARACTERISTIC characteristic,
    BTH_LE_GATT_DESCRIPTOR ccc_descriptor,
    const GattEventRegistrationCallback& callback,
    const GattCharacteristicValueChangedCallback& registered_callback) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  BLUETOOTH_GATT_EVENT_HANDLE win_event_handle = NULL;

  BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION win_event_parameter;
  memcpy(&(win_event_parameter.Characteristics[0]), &characteristic,
         sizeof(BTH_LE_GATT_CHARACTERISTIC));
  win_event_parameter.NumCharacteristics = 1;
  PVOID user_event_handle = (PVOID)&registered_callback;
  HRESULT hr =
      win::BluetoothLowEnergyWrapper::GetInstance()->RegisterGattEvents(
          service_path, CharacteristicValueChangedEvent, &win_event_parameter,
          &OnGetGattEventWin, user_event_handle, &win_event_handle);

  // Sets the Client Characteristic Configuration descriptor.
  if (SUCCEEDED(hr)) {
    BTH_LE_GATT_DESCRIPTOR_VALUE new_cccd_value;
    RtlZeroMemory(&new_cccd_value, sizeof(new_cccd_value));
    new_cccd_value.DescriptorType = ClientCharacteristicConfiguration;
    if (characteristic.IsNotifiable) {
      new_cccd_value.ClientCharacteristicConfiguration
          .IsSubscribeToNotification = TRUE;
    } else {
      new_cccd_value.ClientCharacteristicConfiguration.IsSubscribeToIndication =
          TRUE;
    }

    hr = win::BluetoothLowEnergyWrapper::GetInstance()->WriteDescriptorValue(
        service_path, (PBTH_LE_GATT_DESCRIPTOR)(&ccc_descriptor),
        &new_cccd_value);
  }

  if (SUCCEEDED(hr)) {
    std::unique_ptr<CharacteristicValueChangedRegistration> registration(
        new CharacteristicValueChangedRegistration());
    registration->win_event_handle = win_event_handle;
    registration->callback = registered_callback;
    registration->callback_task_runner = ui_task_runner_;
    base::AutoLock auto_lock(g_characteristic_value_changed_registrations_lock);
    g_characteristic_value_changed_registrations[user_event_handle] =
        std::move(registration);
  }

  ui_task_runner_->PostTask(FROM_HERE,
                            base::Bind(callback, user_event_handle, hr));
}

void BluetoothTaskManagerWin::UnregisterGattCharacteristicValueChangedEvent(
    PVOID event_handle) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());

  base::AutoLock auto_lock(g_characteristic_value_changed_registrations_lock);
  CharacteristicValueChangedRegistrationMap::const_iterator it =
      g_characteristic_value_changed_registrations.find(event_handle);
  if (it != g_characteristic_value_changed_registrations.end()) {
    win::BluetoothLowEnergyWrapper::GetInstance()->UnregisterGattEvent(
        it->second->win_event_handle);
    g_characteristic_value_changed_registrations.erase(event_handle);
  }
}

void BluetoothTaskManagerWin::PostGetGattIncludedCharacteristics(
    const base::FilePath& service_path,
    const BluetoothUUID& uuid,
    uint16_t attribute_handle,
    const GetGattIncludedCharacteristicsCallback& callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::GetGattIncludedCharacteristics, this,
                 service_path, uuid, attribute_handle, callback));
}

void BluetoothTaskManagerWin::PostGetGattIncludedDescriptors(
    const base::FilePath& service_path,
    const PBTH_LE_GATT_CHARACTERISTIC characteristic,
    const GetGattIncludedDescriptorsCallback& callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::GetGattIncludedDescriptors, this,
                 service_path, *characteristic, callback));
}

void BluetoothTaskManagerWin::PostReadGattCharacteristicValue(
    const base::FilePath& service_path,
    const PBTH_LE_GATT_CHARACTERISTIC characteristic,
    const ReadGattCharacteristicValueCallback& callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::ReadGattCharacteristicValue, this,
                 service_path, *characteristic, callback));
}

void BluetoothTaskManagerWin::PostWriteGattCharacteristicValue(
    const base::FilePath& service_path,
    const PBTH_LE_GATT_CHARACTERISTIC characteristic,
    const std::vector<uint8_t>& new_value,
    const HResultCallback& callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::WriteGattCharacteristicValue, this,
                 service_path, *characteristic, new_value, callback));
}

void BluetoothTaskManagerWin::PostRegisterGattCharacteristicValueChangedEvent(
    const base::FilePath& service_path,
    const PBTH_LE_GATT_CHARACTERISTIC characteristic,
    const PBTH_LE_GATT_DESCRIPTOR ccc_descriptor,
    const GattEventRegistrationCallback& callback,
    const GattCharacteristicValueChangedCallback& registered_callback) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(
          &BluetoothTaskManagerWin::RegisterGattCharacteristicValueChangedEvent,
          this, service_path, *characteristic, *ccc_descriptor, callback,
          registered_callback));
}

void BluetoothTaskManagerWin::PostUnregisterGattCharacteristicValueChangedEvent(
    PVOID event_handle) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  bluetooth_task_runner_->PostTask(
      FROM_HERE, base::Bind(&BluetoothTaskManagerWin::
                                UnregisterGattCharacteristicValueChangedEvent,
                            this, event_handle));
}

}  // namespace device
