| // 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.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/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(); |
| HBLUETOOTH_RADIO_FIND handle = BluetoothFindFirstRadio( |
| &adapter_param, adapter_handle_.Receive()); |
| |
| if (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 |