root/device/bluetooth/bluetooth_task_manager_win.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetAdapterState
  2. GetDeviceState
  3. discovering_
  4. AddObserver
  5. RemoveObserver
  6. Initialize
  7. InitializeWithBluetoothTaskRunner
  8. StartPolling
  9. Shutdown
  10. PostSetPoweredBluetoothTask
  11. PostStartDiscoveryTask
  12. PostStopDiscoveryTask
  13. OnAdapterStateChanged
  14. OnDiscoveryStarted
  15. OnDiscoveryStopped
  16. OnDevicesUpdated
  17. OnDevicesDiscovered
  18. PollAdapter
  19. PostAdapterStateToUi
  20. SetPowered
  21. StartDiscovery
  22. StopDiscovery
  23. DiscoverDevices
  24. GetKnownDevices
  25. SearchDevices
  26. DiscoverServices

// 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 <winsock2.h>

#include <string>

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.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 "base/win/scoped_handle.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;
const int kMaxDeviceDiscoveryTimeout = 48;

// 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), 0 };
  if (adapter_handle &&
      ERROR_SUCCESS == BluetoothGetRadioInfo(adapter_handle,
                                             &adapter_info)) {
    name = base::SysWideToUTF8(adapter_info.szName);
    address = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
        adapter_info.address.rgBytes[5],
        adapter_info.address.rgBytes[4],
        adapter_info.address.rgBytes[3],
        adapter_info.address.rgBytes[2],
        adapter_info.address.rgBytes[1],
        adapter_info.address.rgBytes[0]);
    powered = !!BluetoothIsConnectable(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 = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
      device_info.Address.rgBytes[5],
      device_info.Address.rgBytes[4],
      device_info.Address.rgBytes[3],
      device_info.Address.rgBytes[2],
      device_info.Address.rgBytes[1],
      device_info.Address.rgBytes[0]);
  state->bluetooth_class = device_info.ulClassofDevice;
  state->visible = true;
  state->connected = !!device_info.fConnected;
  state->authenticated = !!device_info.fAuthenticated;
}

}  // namespace

namespace device {

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

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

BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
}

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);
  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_)
    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::OnAdapterStateChanged(const AdapterState* state) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
                    AdapterStateChanged(*state));
}

void BluetoothTaskManagerWin::OnDiscoveryStarted(bool success) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
                    DiscoveryStarted(success));
}

void BluetoothTaskManagerWin::OnDiscoveryStopped() {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
                    DiscoveryStopped());
}

void BluetoothTaskManagerWin::OnDevicesUpdated(
    const ScopedVector<DeviceState>* devices) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
                    DevicesUpdated(*devices));
}

void BluetoothTaskManagerWin::OnDevicesDiscovered(
    const ScopedVector<DeviceState>* devices) {
  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
                    DevicesDiscovered(*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) };
    if (adapter_handle_)
      adapter_handle_.Close();
    HANDLE temp_adapter_handle;
    HBLUETOOTH_RADIO_FIND handle = BluetoothFindFirstRadio(
        &adapter_param, &temp_adapter_handle);

    if (handle) {
      adapter_handle_.Set(temp_adapter_handle);
      GetKnownDevices();
      BluetoothFindRadioClose(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)
      BluetoothEnableDiscovery(adapter_handle_, false);
    success = !!BluetoothEnableIncomingConnections(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) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  if (!discovering_ || !adapter_handle_) {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
    return;
  }

  ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
  SearchDevices(timeout, false, device_list);
  if (device_list->empty()) {
    delete device_list;
  } else {
    DiscoverServices(device_list);
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDevicesDiscovered,
                   this,
                   base::Owned(device_list)));
  }

  if (timeout < kMaxDeviceDiscoveryTimeout) {
    bluetooth_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::DiscoverDevices,
                   this,
                   timeout + 1));
  } else {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
    discovering_ = false;
  }
}

void BluetoothTaskManagerWin::GetKnownDevices() {
  ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
  SearchDevices(1, true, device_list);
  if (device_list->empty()) {
    delete device_list;
    return;
  }
  DiscoverServices(device_list);
  ui_task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&BluetoothTaskManagerWin::OnDevicesUpdated,
                 this,
                 base::Owned(device_list)));
}

void BluetoothTaskManagerWin::SearchDevices(
    int timeout,
    bool search_cached_devices_only,
    ScopedVector<DeviceState>* device_list) {
  BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params = {
      sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS),
      1,  // return authenticated devices
      1,  // return remembered devicess
      search_cached_devices_only ? 0 : 1,  // return unknown devices
      1,  // return connected devices
      search_cached_devices_only ? 0 : 1,  // issue a new inquiry
      timeout,  // timeout for the inquiry in increments of 1.28 seconds
      adapter_handle_
  };

  BLUETOOTH_DEVICE_INFO device_info = { sizeof(BLUETOOTH_DEVICE_INFO), 0 };
  // Issues a device inquiry and waits for |timeout| * 1.28 seconds.
  HBLUETOOTH_DEVICE_FIND handle =
      BluetoothFindFirstDevice(&device_search_params, &device_info);
  if (handle) {
    do {
      DeviceState* device_state = new DeviceState();
      GetDeviceState(device_info, device_state);
      device_list->push_back(device_state);
    } while (BluetoothFindNextDevice(handle, &device_info));

    BluetoothFindDeviceClose(handle);
  }
}

void BluetoothTaskManagerWin::DiscoverServices(
    ScopedVector<DeviceState>* device_list) {
  DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
  net::EnsureWinsockInit();
  for (ScopedVector<DeviceState>::iterator iter = device_list->begin();
      iter != device_list->end();
      ++iter) {
    const std::string device_address = (*iter)->address;
    ScopedVector<ServiceRecordState>* service_record_states =
        &(*iter)->service_record_states;
    WSAQUERYSET sdp_query;
    ZeroMemory(&sdp_query, sizeof(sdp_query));
    sdp_query.dwSize = sizeof(sdp_query);
    GUID protocol = L2CAP_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;
    HANDLE sdp_handle;
    if (ERROR_SUCCESS !=
        WSALookupServiceBegin(&sdp_query, LUP_RETURN_ALL, &sdp_handle)) {
      return;
    }
    char sdp_buffer[kServiceDiscoveryResultBufferSize];
    LPWSAQUERYSET sdp_result_data = reinterpret_cast<LPWSAQUERYSET>(sdp_buffer);
    DWORD sdp_buffer_size = sizeof(sdp_buffer);
    while (ERROR_SUCCESS == WSALookupServiceNext(sdp_handle,
                                                 LUP_RETURN_ALL,
                                                 &sdp_buffer_size,
                                                 sdp_result_data)) {
      ServiceRecordState* service_record_state = new ServiceRecordState();
      service_record_state->name =
          base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
      service_record_state->address = device_address;
      for (uint64 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);
    }
    WSALookupServiceEnd(sdp_handle);
  }
}

}  // namespace device

/* [<][>][^][v][top][bottom][index][help] */