1// 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"
15#include "base/message_loop/message_loop.h"
16#include "base/sequenced_task_runner.h"
17#include "base/strings/stringprintf.h"
18#include "base/strings/sys_string_conversions.h"
19#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
191void BluetoothTaskManagerWin::OnDevicesUpdated(
192    const ScopedVector<DeviceState>* devices) {
193  DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
194  FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer, observers_,
195                    DevicesUpdated(*devices));
196}
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();
214    HANDLE temp_adapter_handle;
215    HBLUETOOTH_RADIO_FIND handle = BluetoothFindFirstRadio(
216        &adapter_param, &temp_adapter_handle);
217
218    if (handle) {
219      adapter_handle_.Set(temp_adapter_handle);
220      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
296  ScopedVector<DeviceState>* device_list = new ScopedVector<DeviceState>();
297  SearchDevices(timeout, false, device_list);
298  if (device_list->empty()) {
299    delete device_list;
300  } else {
301    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,
333      base::Bind(&BluetoothTaskManagerWin::OnDevicesUpdated,
334                 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
345      1,  // return remembered devicess
346      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
417