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.
5 #include "device/bluetooth/bluetooth_task_manager_win.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/win/scoped_handle.h"
20 #include "device/bluetooth/bluetooth_device.h"
21 #include "device/bluetooth/bluetooth_init_win.h"
22 #include "device/bluetooth/bluetooth_low_energy_win.h"
23 #include "device/bluetooth/bluetooth_service_record_win.h"
24 #include "net/base/winsock_init.h"
28 const int kNumThreadsInWorkerPool
= 3;
29 const char kBluetoothThreadName
[] = "BluetoothPollingThreadWin";
30 const int kMaxNumDeviceAddressChar
= 127;
31 const int kServiceDiscoveryResultBufferSize
= 5000;
33 // See http://goo.gl/iNTRQe: cTimeoutMultiplier: A value that indicates the time
34 // out for the inquiry, expressed in increments of 1.28 seconds. For example, an
35 // inquiry of 12.8 seconds has a cTimeoutMultiplier value of 10. The maximum
36 // value for this member is 48. When a value greater than 48 is used, the
37 // calling function immediately fails and returns
38 const int kMaxDeviceDiscoveryTimeoutMultiplier
= 48;
40 typedef device::BluetoothTaskManagerWin::ServiceRecordState ServiceRecordState
;
42 // Note: The string returned here must have the same format as
43 // BluetoothDevice::CanonicalizeAddress.
44 std::string
BluetoothAddressToCanonicalString(const BLUETOOTH_ADDRESS
& btha
) {
45 std::string result
= base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
52 DCHECK_EQ(result
, device::BluetoothDevice::CanonicalizeAddress(result
));
56 device::BluetoothUUID
BluetoothLowEnergyUuidToBluetoothUuid(
57 const BTH_LE_UUID
& bth_le_uuid
) {
58 if (bth_le_uuid
.IsShortUuid
) {
59 std::string uuid_hex
=
60 base::StringPrintf("%04x", bth_le_uuid
.Value
.ShortUuid
);
61 return device::BluetoothUUID(uuid_hex
);
63 return device::BluetoothUUID(
64 base::StringPrintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
65 bth_le_uuid
.Value
.LongUuid
.Data1
,
66 bth_le_uuid
.Value
.LongUuid
.Data2
,
67 bth_le_uuid
.Value
.LongUuid
.Data3
,
68 bth_le_uuid
.Value
.LongUuid
.Data4
[0],
69 bth_le_uuid
.Value
.LongUuid
.Data4
[1],
70 bth_le_uuid
.Value
.LongUuid
.Data4
[2],
71 bth_le_uuid
.Value
.LongUuid
.Data4
[3],
72 bth_le_uuid
.Value
.LongUuid
.Data4
[4],
73 bth_le_uuid
.Value
.LongUuid
.Data4
[5],
74 bth_le_uuid
.Value
.LongUuid
.Data4
[6],
75 bth_le_uuid
.Value
.LongUuid
.Data4
[7]));
79 // Populates bluetooth adapter state using adapter_handle.
80 void GetAdapterState(HANDLE adapter_handle
,
81 device::BluetoothTaskManagerWin::AdapterState
* state
) {
85 BLUETOOTH_RADIO_INFO adapter_info
= { sizeof(BLUETOOTH_RADIO_INFO
), 0 };
87 ERROR_SUCCESS
== BluetoothGetRadioInfo(adapter_handle
,
89 name
= base::SysWideToUTF8(adapter_info
.szName
);
90 address
= BluetoothAddressToCanonicalString(adapter_info
.address
);
91 powered
= !!BluetoothIsConnectable(adapter_handle
);
94 state
->address
= address
;
95 state
->powered
= powered
;
98 void GetDeviceState(const BLUETOOTH_DEVICE_INFO
& device_info
,
99 device::BluetoothTaskManagerWin::DeviceState
* state
) {
100 state
->name
= base::SysWideToUTF8(device_info
.szName
);
101 state
->address
= BluetoothAddressToCanonicalString(device_info
.Address
);
102 state
->bluetooth_class
= device_info
.ulClassofDevice
;
103 state
->visible
= true;
104 state
->connected
= !!device_info
.fConnected
;
105 state
->authenticated
= !!device_info
.fAuthenticated
;
113 const int BluetoothTaskManagerWin::kPollIntervalMs
= 500;
115 BluetoothTaskManagerWin::AdapterState::AdapterState() : powered(false) {
118 BluetoothTaskManagerWin::AdapterState::~AdapterState() {
121 BluetoothTaskManagerWin::ServiceRecordState::ServiceRecordState() {
124 BluetoothTaskManagerWin::ServiceRecordState::~ServiceRecordState() {
127 BluetoothTaskManagerWin::DeviceState::DeviceState()
128 : bluetooth_class(0),
131 authenticated(false) {
134 BluetoothTaskManagerWin::DeviceState::~DeviceState() {
137 BluetoothTaskManagerWin::BluetoothTaskManagerWin(
138 scoped_refptr
<base::SequencedTaskRunner
> ui_task_runner
)
139 : ui_task_runner_(ui_task_runner
),
141 current_logging_batch_count_(0) {
144 BluetoothTaskManagerWin::~BluetoothTaskManagerWin() {
147 void BluetoothTaskManagerWin::AddObserver(Observer
* observer
) {
149 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
150 observers_
.AddObserver(observer
);
153 void BluetoothTaskManagerWin::RemoveObserver(Observer
* observer
) {
155 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
156 observers_
.RemoveObserver(observer
);
159 void BluetoothTaskManagerWin::Initialize() {
160 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
161 worker_pool_
= new base::SequencedWorkerPool(kNumThreadsInWorkerPool
,
162 kBluetoothThreadName
);
163 InitializeWithBluetoothTaskRunner(
164 worker_pool_
->GetSequencedTaskRunnerWithShutdownBehavior(
165 worker_pool_
->GetSequenceToken(),
166 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
));
169 void BluetoothTaskManagerWin::InitializeWithBluetoothTaskRunner(
170 scoped_refptr
<base::SequencedTaskRunner
> bluetooth_task_runner
) {
171 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
172 bluetooth_task_runner_
= bluetooth_task_runner
;
173 bluetooth_task_runner_
->PostTask(
175 base::Bind(&BluetoothTaskManagerWin::StartPolling
, this));
178 void BluetoothTaskManagerWin::StartPolling() {
179 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
181 if (device::bluetooth_init_win::HasBluetoothStack()) {
184 // IF the bluetooth stack is not available, we still send an empty state
185 // to BluetoothAdapter so that it is marked initialized, but the adapter
186 // will not be present.
187 AdapterState
* state
= new AdapterState();
188 ui_task_runner_
->PostTask(
190 base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged
,
192 base::Owned(state
)));
196 void BluetoothTaskManagerWin::Shutdown() {
197 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
199 worker_pool_
->Shutdown();
202 void BluetoothTaskManagerWin::PostSetPoweredBluetoothTask(
204 const base::Closure
& callback
,
205 const BluetoothAdapter::ErrorCallback
& error_callback
) {
206 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
207 bluetooth_task_runner_
->PostTask(
209 base::Bind(&BluetoothTaskManagerWin::SetPowered
,
216 void BluetoothTaskManagerWin::PostStartDiscoveryTask() {
217 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
218 bluetooth_task_runner_
->PostTask(
220 base::Bind(&BluetoothTaskManagerWin::StartDiscovery
, this));
223 void BluetoothTaskManagerWin::PostStopDiscoveryTask() {
224 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
225 bluetooth_task_runner_
->PostTask(
227 base::Bind(&BluetoothTaskManagerWin::StopDiscovery
, this));
230 void BluetoothTaskManagerWin::LogPollingError(const char* message
,
232 const int kLogPeriodInMilliseconds
= 60 * 1000;
233 const int kMaxMessagesPerLogPeriod
= 10;
235 // Check if we need to discard this message
236 if (!current_logging_batch_ticks_
.is_null()) {
237 if (base::TimeTicks::Now() - current_logging_batch_ticks_
<=
238 base::TimeDelta::FromMilliseconds(kLogPeriodInMilliseconds
)) {
239 if (current_logging_batch_count_
>= kMaxMessagesPerLogPeriod
)
242 // The batch expired, reset it to "null".
243 current_logging_batch_ticks_
= base::TimeTicks();
247 // Keep track of this batch of messages
248 if (current_logging_batch_ticks_
.is_null()) {
249 current_logging_batch_ticks_
= base::TimeTicks::Now();
250 current_logging_batch_count_
= 0;
252 ++current_logging_batch_count_
;
255 if (win32_error
== 0)
256 LOG(WARNING
) << message
;
258 LOG(WARNING
) << message
<< ": "
259 << logging::SystemErrorCodeToString(win32_error
);
262 void BluetoothTaskManagerWin::OnAdapterStateChanged(const AdapterState
* state
) {
263 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
264 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer
, observers_
,
265 AdapterStateChanged(*state
));
268 void BluetoothTaskManagerWin::OnDiscoveryStarted(bool success
) {
269 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
270 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer
, observers_
,
271 DiscoveryStarted(success
));
274 void BluetoothTaskManagerWin::OnDiscoveryStopped() {
275 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
276 FOR_EACH_OBSERVER(BluetoothTaskManagerWin::Observer
, observers_
,
280 void BluetoothTaskManagerWin::OnDevicesPolled(
281 const ScopedVector
<DeviceState
>* devices
) {
282 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
284 BluetoothTaskManagerWin::Observer
, observers_
, DevicesPolled(*devices
));
287 void BluetoothTaskManagerWin::PollAdapter() {
288 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
290 // Skips updating the adapter info if the adapter is in discovery mode.
292 const BLUETOOTH_FIND_RADIO_PARAMS adapter_param
=
293 { sizeof(BLUETOOTH_FIND_RADIO_PARAMS
) };
295 adapter_handle_
.Close();
296 HANDLE temp_adapter_handle
;
297 HBLUETOOTH_RADIO_FIND handle
= BluetoothFindFirstRadio(
298 &adapter_param
, &temp_adapter_handle
);
301 adapter_handle_
.Set(temp_adapter_handle
);
303 BluetoothFindRadioClose(handle
);
306 PostAdapterStateToUi();
310 bluetooth_task_runner_
->PostDelayedTask(
312 base::Bind(&BluetoothTaskManagerWin::PollAdapter
,
314 base::TimeDelta::FromMilliseconds(kPollIntervalMs
));
317 void BluetoothTaskManagerWin::PostAdapterStateToUi() {
318 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
319 AdapterState
* state
= new AdapterState();
320 GetAdapterState(adapter_handle_
, state
);
321 ui_task_runner_
->PostTask(
323 base::Bind(&BluetoothTaskManagerWin::OnAdapterStateChanged
,
325 base::Owned(state
)));
328 void BluetoothTaskManagerWin::SetPowered(
330 const base::Closure
& callback
,
331 const BluetoothAdapter::ErrorCallback
& error_callback
) {
332 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
333 bool success
= false;
334 if (adapter_handle_
) {
336 BluetoothEnableDiscovery(adapter_handle_
, false);
337 success
= !!BluetoothEnableIncomingConnections(adapter_handle_
, powered
);
341 PostAdapterStateToUi();
342 ui_task_runner_
->PostTask(FROM_HERE
, callback
);
344 ui_task_runner_
->PostTask(FROM_HERE
, error_callback
);
348 void BluetoothTaskManagerWin::StartDiscovery() {
349 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
350 ui_task_runner_
->PostTask(
352 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStarted
,
355 if (!adapter_handle_
)
362 void BluetoothTaskManagerWin::StopDiscovery() {
363 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
364 discovering_
= false;
365 ui_task_runner_
->PostTask(
367 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped
, this));
370 void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier
) {
371 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
372 if (!discovering_
|| !adapter_handle_
) {
373 ui_task_runner_
->PostTask(
375 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped
, this));
379 scoped_ptr
<ScopedVector
<DeviceState
> > device_list(
380 new ScopedVector
<DeviceState
>());
381 if (SearchDevices(timeout_multiplier
, false, device_list
.get())) {
382 ui_task_runner_
->PostTask(
384 base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled
,
386 base::Owned(device_list
.release())));
389 if (timeout_multiplier
< kMaxDeviceDiscoveryTimeoutMultiplier
)
390 ++timeout_multiplier
;
391 bluetooth_task_runner_
->PostTask(
394 &BluetoothTaskManagerWin::DiscoverDevices
, this, timeout_multiplier
));
397 void BluetoothTaskManagerWin::GetKnownDevices() {
398 scoped_ptr
<ScopedVector
<DeviceState
> > device_list(
399 new ScopedVector
<DeviceState
>());
400 if (SearchDevices(1, true, device_list
.get())) {
401 ui_task_runner_
->PostTask(
403 base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled
,
405 base::Owned(device_list
.release())));
409 bool BluetoothTaskManagerWin::SearchDevices(
410 int timeout_multiplier
,
411 bool search_cached_devices_only
,
412 ScopedVector
<DeviceState
>* device_list
) {
413 return SearchClassicDevices(
414 timeout_multiplier
, search_cached_devices_only
, device_list
) &&
415 SearchLowEnergyDevices(device_list
) &&
416 DiscoverServices(device_list
, search_cached_devices_only
);
419 bool BluetoothTaskManagerWin::SearchClassicDevices(
420 int timeout_multiplier
,
421 bool search_cached_devices_only
,
422 ScopedVector
<DeviceState
>* device_list
) {
423 // Issues a device inquiry and waits for |timeout_multiplier| * 1.28 seconds.
424 BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params
;
425 ZeroMemory(&device_search_params
, sizeof(device_search_params
));
426 device_search_params
.dwSize
= sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS
);
427 device_search_params
.fReturnAuthenticated
= 1;
428 device_search_params
.fReturnRemembered
= 1;
429 device_search_params
.fReturnUnknown
= (search_cached_devices_only
? 0 : 1);
430 device_search_params
.fReturnConnected
= 1;
431 device_search_params
.fIssueInquiry
= (search_cached_devices_only
? 0 : 1);
432 device_search_params
.cTimeoutMultiplier
= timeout_multiplier
;
434 BLUETOOTH_DEVICE_INFO device_info
;
435 ZeroMemory(&device_info
, sizeof(device_info
));
436 device_info
.dwSize
= sizeof(BLUETOOTH_DEVICE_INFO
);
437 HBLUETOOTH_DEVICE_FIND handle
=
438 BluetoothFindFirstDevice(&device_search_params
, &device_info
);
440 int last_error
= GetLastError();
441 if (last_error
== ERROR_NO_MORE_ITEMS
) {
442 return true; // No devices is not an error.
444 LogPollingError("Error calling BluetoothFindFirstDevice", last_error
);
449 DeviceState
* device_state
= new DeviceState();
450 GetDeviceState(device_info
, device_state
);
451 device_list
->push_back(device_state
);
453 // Reset device info before next call (as a safety precaution).
454 ZeroMemory(&device_info
, sizeof(device_info
));
455 device_info
.dwSize
= sizeof(BLUETOOTH_DEVICE_INFO
);
456 if (!BluetoothFindNextDevice(handle
, &device_info
)) {
457 int last_error
= GetLastError();
458 if (last_error
== ERROR_NO_MORE_ITEMS
) {
459 break; // No more items is expected error when done enumerating.
461 LogPollingError("Error calling BluetoothFindNextDevice", last_error
);
462 BluetoothFindDeviceClose(handle
);
467 if (!BluetoothFindDeviceClose(handle
)) {
468 LogPollingError("Error calling BluetoothFindDeviceClose", GetLastError());
474 bool BluetoothTaskManagerWin::SearchLowEnergyDevices(
475 ScopedVector
<DeviceState
>* device_list
) {
476 if (!win::IsBluetoothLowEnergySupported())
477 return true; // Bluetooth LE not supported is not an error.
479 ScopedVector
<win::BluetoothLowEnergyDeviceInfo
> btle_devices
;
482 win::EnumerateKnownBluetoothLowEnergyDevices(&btle_devices
, &error
);
484 LogPollingError(error
.c_str(), 0);
488 for (ScopedVector
<win::BluetoothLowEnergyDeviceInfo
>::iterator iter
=
489 btle_devices
.begin();
490 iter
!= btle_devices
.end();
492 win::BluetoothLowEnergyDeviceInfo
* device_info
= (*iter
);
493 DeviceState
* device_state
= new DeviceState();
494 device_state
->name
= device_info
->friendly_name
;
495 device_state
->address
=
496 BluetoothAddressToCanonicalString(device_info
->address
);
497 device_state
->visible
= device_info
->visible
;
498 device_state
->authenticated
= device_info
->authenticated
;
499 device_state
->connected
= device_info
->connected
;
500 device_state
->path
= device_info
->path
;
501 device_list
->push_back(device_state
);
506 bool BluetoothTaskManagerWin::DiscoverServices(
507 ScopedVector
<DeviceState
>* device_list
,
508 bool search_cached_services_only
) {
509 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
510 net::EnsureWinsockInit();
511 for (ScopedVector
<DeviceState
>::iterator iter
= device_list
->begin();
512 iter
!= device_list
->end();
514 DeviceState
* device
= (*iter
);
515 ScopedVector
<ServiceRecordState
>* service_record_states
=
516 &(*iter
)->service_record_states
;
518 if ((*iter
)->is_bluetooth_classic()) {
519 if (!DiscoverClassicDeviceServices(device
->address
,
521 search_cached_services_only
,
522 service_record_states
)) {
526 if (!DiscoverLowEnergyDeviceServices(device
->path
,
527 service_record_states
)) {
535 bool BluetoothTaskManagerWin::DiscoverClassicDeviceServices(
536 const std::string
& device_address
,
537 const GUID
& protocol_uuid
,
538 bool search_cached_services_only
,
539 ScopedVector
<ServiceRecordState
>* service_record_states
) {
541 DiscoverClassicDeviceServicesWorker(device_address
,
543 search_cached_services_only
,
544 service_record_states
);
545 // If the device is "offline", no services are returned when specifying
546 // "LUP_FLUSHCACHE". Try again without flushing the cache so that the list
547 // of previously known services is returned.
548 if (!search_cached_services_only
&&
549 (error_code
== WSASERVICE_NOT_FOUND
|| error_code
== WSANO_DATA
)) {
550 error_code
= DiscoverClassicDeviceServicesWorker(
551 device_address
, protocol_uuid
, true, service_record_states
);
554 return (error_code
== ERROR_SUCCESS
);
557 int BluetoothTaskManagerWin::DiscoverClassicDeviceServicesWorker(
558 const std::string
& device_address
,
559 const GUID
& protocol_uuid
,
560 bool search_cached_services_only
,
561 ScopedVector
<ServiceRecordState
>* service_record_states
) {
562 // Bluetooth and WSAQUERYSET for Service Inquiry. See http://goo.gl/2v9pyt.
563 WSAQUERYSET sdp_query
;
564 ZeroMemory(&sdp_query
, sizeof(sdp_query
));
565 sdp_query
.dwSize
= sizeof(sdp_query
);
566 GUID protocol
= protocol_uuid
;
567 sdp_query
.lpServiceClassId
= &protocol
;
568 sdp_query
.dwNameSpace
= NS_BTH
;
569 wchar_t device_address_context
[kMaxNumDeviceAddressChar
];
570 std::size_t length
= base::SysUTF8ToWide("(" + device_address
+ ")").copy(
571 device_address_context
, kMaxNumDeviceAddressChar
);
572 device_address_context
[length
] = NULL
;
573 sdp_query
.lpszContext
= device_address_context
;
574 DWORD control_flags
= LUP_RETURN_ALL
;
575 // See http://goo.gl/t1Hulo: "Applications should generally specify
576 // LUP_FLUSHCACHE. This flag instructs the system to ignore any cached
577 // information and establish an over-the-air SDP connection to the specified
578 // device to perform the SDP search. This non-cached operation may take
579 // several seconds (whereas a cached search returns quickly)."
580 // In summary, we need to specify LUP_FLUSHCACHE if we want to obtain the list
581 // of services for devices which have not been discovered before.
582 if (!search_cached_services_only
)
583 control_flags
|= LUP_FLUSHCACHE
;
586 WSALookupServiceBegin(&sdp_query
, control_flags
, &sdp_handle
)) {
587 int last_error
= WSAGetLastError();
588 // If the device is "offline", no services are returned when specifying
589 // "LUP_FLUSHCACHE". Don't log error in that case.
590 if (!search_cached_services_only
&&
591 (last_error
== WSASERVICE_NOT_FOUND
|| last_error
== WSANO_DATA
)) {
594 LogPollingError("Error calling WSALookupServiceBegin", last_error
);
597 char sdp_buffer
[kServiceDiscoveryResultBufferSize
];
598 LPWSAQUERYSET sdp_result_data
= reinterpret_cast<LPWSAQUERYSET
>(sdp_buffer
);
600 DWORD sdp_buffer_size
= sizeof(sdp_buffer
);
602 WSALookupServiceNext(
603 sdp_handle
, control_flags
, &sdp_buffer_size
, sdp_result_data
)) {
604 int last_error
= WSAGetLastError();
605 if (last_error
== WSA_E_NO_MORE
|| last_error
== WSAENOMORE
) {
608 LogPollingError("Error calling WSALookupServiceNext", last_error
);
609 WSALookupServiceEnd(sdp_handle
);
612 ServiceRecordState
* service_record_state
= new ServiceRecordState();
613 service_record_state
->name
=
614 base::SysWideToUTF8(sdp_result_data
->lpszServiceInstanceName
);
615 for (uint64 i
= 0; i
< sdp_result_data
->lpBlob
->cbSize
; i
++) {
616 service_record_state
->sdp_bytes
.push_back(
617 sdp_result_data
->lpBlob
->pBlobData
[i
]);
619 service_record_states
->push_back(service_record_state
);
621 if (ERROR_SUCCESS
!= WSALookupServiceEnd(sdp_handle
)) {
622 int last_error
= WSAGetLastError();
623 LogPollingError("Error calling WSALookupServiceEnd", last_error
);
627 return ERROR_SUCCESS
;
630 bool BluetoothTaskManagerWin::DiscoverLowEnergyDeviceServices(
631 const base::FilePath
& device_path
,
632 ScopedVector
<ServiceRecordState
>* service_record_states
) {
633 if (!win::IsBluetoothLowEnergySupported())
634 return true; // Bluetooth LE not supported is not an error.
637 ScopedVector
<win::BluetoothLowEnergyServiceInfo
> services
;
638 bool success
= win::EnumerateKnownBluetoothLowEnergyServices(
639 device_path
, &services
, &error
);
641 LogPollingError(error
.c_str(), 0);
645 for (ScopedVector
<win::BluetoothLowEnergyServiceInfo
>::iterator iter2
=
647 iter2
!= services
.end();
649 ServiceRecordState
* service_state
= new ServiceRecordState();
650 service_state
->gatt_uuid
=
651 BluetoothLowEnergyUuidToBluetoothUuid((*iter2
)->uuid
);
652 service_record_states
->push_back(service_state
);
657 } // namespace device