// 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 #include #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/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 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 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* devices) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_, DevicesUpdated(*devices)); } void BluetoothTaskManagerWin::OnDevicesDiscovered( const ScopedVector* 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* device_list = new ScopedVector(); 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* device_list = new ScopedVector(); 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* 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* device_list) { DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread()); net::EnsureWinsockInit(); for (ScopedVector::iterator iter = device_list->begin(); iter != device_list->end(); ++iter) { const std::string device_address = (*iter)->address; ScopedVector* 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(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