blob: 3347f0f17534fd3e501a4addf56b0cbfca2453fc [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "device/bluetooth/bluetooth_task_manager_win.h"
6
7#include <winsock2.h>
8
9#include <string>
10
11#include "base/basictypes.h"
12#include "base/bind.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_vector.h"
Ben Murdoch9ab55632013-07-18 11:57:30 +010015#include "base/message_loop/message_loop.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "base/sequenced_task_runner.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010017#include "base/strings/stringprintf.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010018#include "base/strings/sys_string_conversions.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#include "base/threading/sequenced_worker_pool.h"
20#include "base/win/scoped_handle.h"
21#include "device/bluetooth/bluetooth_init_win.h"
22#include "device/bluetooth/bluetooth_service_record_win.h"
23#include "net/base/winsock_init.h"
24
25namespace {
26
27const int kNumThreadsInWorkerPool = 3;
28const char kBluetoothThreadName[] = "BluetoothPollingThreadWin";
29const int kMaxNumDeviceAddressChar = 127;
30const int kServiceDiscoveryResultBufferSize = 5000;
31const int kMaxDeviceDiscoveryTimeout = 48;
32
33// Populates bluetooth adapter state using adapter_handle.
34void GetAdapterState(HANDLE adapter_handle,
35 device::BluetoothTaskManagerWin::AdapterState* state) {
36 std::string name;
37 std::string address;
38 bool powered = false;
39 BLUETOOTH_RADIO_INFO adapter_info = { sizeof(BLUETOOTH_RADIO_INFO), 0 };
40 if (adapter_handle &&
41 ERROR_SUCCESS == BluetoothGetRadioInfo(adapter_handle,
42 &adapter_info)) {
43 name = base::SysWideToUTF8(adapter_info.szName);
44 address = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
45 adapter_info.address.rgBytes[5],
46 adapter_info.address.rgBytes[4],
47 adapter_info.address.rgBytes[3],
48 adapter_info.address.rgBytes[2],
49 adapter_info.address.rgBytes[1],
50 adapter_info.address.rgBytes[0]);
51 powered = !!BluetoothIsConnectable(adapter_handle);
52 }
53 state->name = name;
54 state->address = address;
55 state->powered = powered;
56}
57
58void GetDeviceState(const BLUETOOTH_DEVICE_INFO& device_info,
59 device::BluetoothTaskManagerWin::DeviceState* state) {
60 state->name = base::SysWideToUTF8(device_info.szName);
61 state->address = base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
62 device_info.Address.rgBytes[5],
63 device_info.Address.rgBytes[4],
64 device_info.Address.rgBytes[3],
65 device_info.Address.rgBytes[2],
66 device_info.Address.rgBytes[1],
67 device_info.Address.rgBytes[0]);
68 state->bluetooth_class = device_info.ulClassofDevice;
69 state->visible = true;
70 state->connected = !!device_info.fConnected;
71 state->authenticated = !!device_info.fAuthenticated;
72}
73
74} // namespace
75
76namespace device {
77
78// static
79const int BluetoothTaskManagerWin::kPollIntervalMs = 500;
80
81BluetoothTaskManagerWin::BluetoothTaskManagerWin(
82 scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
83 : ui_task_runner_(ui_task_runner),
84 discovering_(false) {
85}
86
87BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
88}
89
90void BluetoothTaskManagerWin::AddObserver(Observer* observer) {
91 DCHECK(observer);
92 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
93 observers_.AddObserver(observer);
94}
95
96void BluetoothTaskManagerWin::RemoveObserver(Observer* observer) {
97 DCHECK(observer);
98 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
99 observers_.RemoveObserver(observer);
100}
101
102void BluetoothTaskManagerWin::Initialize() {
103 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
104 worker_pool_ = new base::SequencedWorkerPool(kNumThreadsInWorkerPool,
105 kBluetoothThreadName);
106 InitializeWithBluetoothTaskRunner(
107 worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior(
108 worker_pool_->GetSequenceToken(),
109 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
110}
111
112void BluetoothTaskManagerWin::InitializeWithBluetoothTaskRunner(
113 scoped_refptr<base::SequencedTaskRunner> bluetooth_task_runner) {
114 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
115 bluetooth_task_runner_ = bluetooth_task_runner;
116 bluetooth_task_runner_->PostTask(
117 FROM_HERE,
118 base::Bind(&BluetoothTaskManagerWin::StartPolling, this));
119}
120
121void BluetoothTaskManagerWin::StartPolling() {
122 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
123
124 if (device::bluetooth_init_win::HasBluetoothStack()) {
125 PollAdapter();
126 } else {
127 // IF the bluetooth stack is not available, we still send an empty state
128 // to BluetoothAdapter so that it is marked initialized, but the adapter
129 // will not be present.
130 AdapterState* state = new AdapterState();
131 ui_task_runner_->PostTask(
132 FROM_HERE,
133 base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged,
134 this,
135 base::Owned(state)));
136 }
137}
138
139void BluetoothTaskManagerWin::Shutdown() {
140 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
141 if (worker_pool_)
142 worker_pool_->Shutdown();
143}
144
145void BluetoothTaskManagerWin::PostSetPoweredBluetoothTask(
146 bool powered,
147 const base::Closure& callback,
148 const BluetoothAdapter::ErrorCallback& error_callback) {
149 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
150 bluetooth_task_runner_->PostTask(
151 FROM_HERE,
152 base::Bind(&BluetoothTaskManagerWin::SetPowered,
153 this,
154 powered,
155 callback,
156 error_callback));
157}
158
159void BluetoothTaskManagerWin::PostStartDiscoveryTask() {
160 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
161 bluetooth_task_runner_->PostTask(
162 FROM_HERE,
163 base::Bind(&BluetoothTaskManagerWin::StartDiscovery, this));
164}
165
166void BluetoothTaskManagerWin::PostStopDiscoveryTask() {
167 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
168 bluetooth_task_runner_->PostTask(
169 FROM_HERE,
170 base::Bind(&BluetoothTaskManagerWin::StopDiscovery, this));
171}
172
173void BluetoothTaskManagerWin::OnAdapterStateChanged(const AdapterState* state) {
174 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
175 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
176 AdapterStateChanged(*state));
177}
178
179void BluetoothTaskManagerWin::OnDiscoveryStarted(bool success) {
180 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
181 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
182 DiscoveryStarted(success));
183}
184
185void BluetoothTaskManagerWin::OnDiscoveryStopped() {
186 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
187 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
188 DiscoveryStopped());
189}
190
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100191void BluetoothTaskManagerWin::OnDevicesUpdated(
192 const ScopedVector<DeviceState>* devices) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000193 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
194 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100195 DevicesUpdated(*devices));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000196}
197
198void BluetoothTaskManagerWin::OnDevicesDiscovered(
199 const ScopedVector<DeviceState>* devices) {
200 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
201 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
202 DevicesDiscovered(*devices));
203}
204
205void BluetoothTaskManagerWin::PollAdapter() {
206 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
207
208 // Skips updating the adapter info if the adapter is in discovery mode.
209 if (!discovering_) {
210 const BLUETOOTH_FIND_RADIO_PARAMS adapter_param =
211 { sizeof(BLUETOOTH_FIND_RADIO_PARAMS) };
212 if (adapter_handle_)
213 adapter_handle_.Close();
Torne (Richard Coles)f2477e02013-11-28 11:55:43 +0000214 HANDLE temp_adapter_handle;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000215 HBLUETOOTH_RADIO_FIND handle = BluetoothFindFirstRadio(
Torne (Richard Coles)f2477e02013-11-28 11:55:43 +0000216 &adapter_param, &temp_adapter_handle);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000217
218 if (handle) {
Torne (Richard Coles)f2477e02013-11-28 11:55:43 +0000219 adapter_handle_.Set(temp_adapter_handle);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000220 GetKnownDevices();
221 BluetoothFindRadioClose(handle);
222 }
223 PostAdapterStateToUi();
224 }
225
226 // Re-poll.
227 bluetooth_task_runner_->PostDelayedTask(
228 FROM_HERE,
229 base::Bind(&BluetoothTaskManagerWin::PollAdapter,
230 this),
231 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
232}
233
234void BluetoothTaskManagerWin::PostAdapterStateToUi() {
235 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
236 AdapterState* state = new AdapterState();
237 GetAdapterState(adapter_handle_, state);
238 ui_task_runner_->PostTask(
239 FROM_HERE,
240 base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged,
241 this,
242 base::Owned(state)));
243}
244
245void BluetoothTaskManagerWin::SetPowered(
246 bool powered,
247 const base::Closure& callback,
248 const BluetoothAdapter::ErrorCallback& error_callback) {
249 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
250 bool success = false;
251 if (adapter_handle_) {
252 if (!powered)
253 BluetoothEnableDiscovery(adapter_handle_, false);
254 success = !!BluetoothEnableIncomingConnections(adapter_handle_, powered);
255 }
256
257 if (success) {
258 PostAdapterStateToUi();
259 ui_task_runner_->PostTask(FROM_HERE, callback);
260 } else {
261 ui_task_runner_->PostTask(FROM_HERE, error_callback);
262 }
263}
264
265void BluetoothTaskManagerWin::StartDiscovery() {
266 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
267 ui_task_runner_->PostTask(
268 FROM_HERE,
269 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStarted,
270 this,
271 !!adapter_handle_));
272 if (!adapter_handle_)
273 return;
274 discovering_ = true;
275
276 DiscoverDevices(1);
277}
278
279void BluetoothTaskManagerWin::StopDiscovery() {
280 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
281 discovering_ = false;
282 ui_task_runner_->PostTask(
283 FROM_HERE,
284 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
285}
286
287void BluetoothTaskManagerWin::DiscoverDevices(int timeout) {
288 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
289 if (!discovering_ || !adapter_handle_) {
290 ui_task_runner_->PostTask(
291 FROM_HERE,
292 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
293 return;
294 }
295
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000296 ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
297 SearchDevices(timeout, false, device_list);
298 if (device_list->empty()) {
299 delete device_list;
300 } else {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000301 DiscoverServices(device_list);
302 ui_task_runner_->PostTask(
303 FROM_HERE,
304 base::Bind(&BluetoothTaskManagerWin::OnDevicesDiscovered,
305 this,
306 base::Owned(device_list)));
307 }
308
309 if (timeout < kMaxDeviceDiscoveryTimeout) {
310 bluetooth_task_runner_->PostTask(
311 FROM_HERE,
312 base::Bind(&BluetoothTaskManagerWin::DiscoverDevices,
313 this,
314 timeout + 1));
315 } else {
316 ui_task_runner_->PostTask(
317 FROM_HERE,
318 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped, this));
319 discovering_ = false;
320 }
321}
322
323void BluetoothTaskManagerWin::GetKnownDevices() {
324 ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
325 SearchDevices(1, true, device_list);
326 if (device_list->empty()) {
327 delete device_list;
328 return;
329 }
330 DiscoverServices(device_list);
331 ui_task_runner_->PostTask(
332 FROM_HERE,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100333 base::Bind(&BluetoothTaskManagerWin::OnDevicesUpdated,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000334 this,
335 base::Owned(device_list)));
336}
337
338void BluetoothTaskManagerWin::SearchDevices(
339 int timeout,
340 bool search_cached_devices_only,
341 ScopedVector<DeviceState>* device_list) {
342 BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params = {
343 sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS),
344 1, // return authenticated devices
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100345 1, // return remembered devicess
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000346 search_cached_devices_only ? 0 : 1, // return unknown devices
347 1, // return connected devices
348 search_cached_devices_only ? 0 : 1, // issue a new inquiry
349 timeout, // timeout for the inquiry in increments of 1.28 seconds
350 adapter_handle_
351 };
352
353 BLUETOOTH_DEVICE_INFO device_info = { sizeof(BLUETOOTH_DEVICE_INFO), 0 };
354 // Issues a device inquiry and waits for |timeout| * 1.28 seconds.
355 HBLUETOOTH_DEVICE_FIND handle =
356 BluetoothFindFirstDevice(&device_search_params, &device_info);
357 if (handle) {
358 do {
359 DeviceState* device_state = new DeviceState();
360 GetDeviceState(device_info, device_state);
361 device_list->push_back(device_state);
362 } while (BluetoothFindNextDevice(handle, &device_info));
363
364 BluetoothFindDeviceClose(handle);
365 }
366}
367
368void BluetoothTaskManagerWin::DiscoverServices(
369 ScopedVector<DeviceState>* device_list) {
370 DCHECK(bluetooth_task_runner_->RunsTasksOnCurrentThread());
371 net::EnsureWinsockInit();
372 for (ScopedVector<DeviceState>::iterator iter = device_list->begin();
373 iter != device_list->end();
374 ++iter) {
375 const std::string device_address = (*iter)->address;
376 ScopedVector<ServiceRecordState>* service_record_states =
377 &(*iter)->service_record_states;
378 WSAQUERYSET sdp_query;
379 ZeroMemory(&sdp_query, sizeof(sdp_query));
380 sdp_query.dwSize = sizeof(sdp_query);
381 GUID protocol = L2CAP_PROTOCOL_UUID;
382 sdp_query.lpServiceClassId = &protocol;
383 sdp_query.dwNameSpace = NS_BTH;
384 wchar_t device_address_context[kMaxNumDeviceAddressChar];
385 std::size_t length =
386 base::SysUTF8ToWide("(" + device_address + ")").copy(
387 device_address_context, kMaxNumDeviceAddressChar);
388 device_address_context[length] = NULL;
389 sdp_query.lpszContext = device_address_context;
390 HANDLE sdp_handle;
391 if (ERROR_SUCCESS !=
392 WSALookupServiceBegin(&sdp_query, LUP_RETURN_ALL, &sdp_handle)) {
393 return;
394 }
395 char sdp_buffer[kServiceDiscoveryResultBufferSize];
396 LPWSAQUERYSET sdp_result_data = reinterpret_cast<LPWSAQUERYSET>(sdp_buffer);
397 DWORD sdp_buffer_size = sizeof(sdp_buffer);
398 while (ERROR_SUCCESS == WSALookupServiceNext(sdp_handle,
399 LUP_RETURN_ALL,
400 &sdp_buffer_size,
401 sdp_result_data)) {
402 ServiceRecordState* service_record_state = new ServiceRecordState();
403 service_record_state->name =
404 base::SysWideToUTF8(sdp_result_data->lpszServiceInstanceName);
405 service_record_state->address = device_address;
406 for (uint64 i = 0; i < sdp_result_data->lpBlob->cbSize; i++) {
407 service_record_state->sdp_bytes.push_back(
408 sdp_result_data->lpBlob->pBlobData[i]);
409 }
410 service_record_states->push_back(service_record_state);
411 }
412 WSALookupServiceEnd(sdp_handle);
413 }
414}
415
416} // namespace device