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());
198 if (worker_pool_
.get())
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
) };
294 if (adapter_handle_
.IsValid())
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_
.Get(), 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_
.IsValid()) {
336 BluetoothEnableDiscovery(adapter_handle_
.Get(), false);
338 !!BluetoothEnableIncomingConnections(adapter_handle_
.Get(), powered
);
342 PostAdapterStateToUi();
343 ui_task_runner_
->PostTask(FROM_HERE
, callback
);
345 ui_task_runner_
->PostTask(FROM_HERE
, error_callback
);
349 void BluetoothTaskManagerWin::StartDiscovery() {
350 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
351 ui_task_runner_
->PostTask(
353 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStarted
,
355 adapter_handle_
.IsValid()));
356 if (!adapter_handle_
.IsValid())
363 void BluetoothTaskManagerWin::StopDiscovery() {
364 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
365 discovering_
= false;
366 ui_task_runner_
->PostTask(
368 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped
, this));
371 void BluetoothTaskManagerWin::DiscoverDevices(int timeout_multiplier
) {
372 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
373 if (!discovering_
|| !adapter_handle_
.IsValid()) {
374 ui_task_runner_
->PostTask(
376 base::Bind(&BluetoothTaskManagerWin::OnDiscoveryStopped
, this));
380 scoped_ptr
<ScopedVector
<DeviceState
> > device_list(
381 new ScopedVector
<DeviceState
>());
382 if (SearchDevices(timeout_multiplier
, false, device_list
.get())) {
383 ui_task_runner_
->PostTask(
385 base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled
,
387 base::Owned(device_list
.release())));
390 if (timeout_multiplier
< kMaxDeviceDiscoveryTimeoutMultiplier
)
391 ++timeout_multiplier
;
392 bluetooth_task_runner_
->PostTask(
395 &BluetoothTaskManagerWin::DiscoverDevices
, this, timeout_multiplier
));
398 void BluetoothTaskManagerWin::GetKnownDevices() {
399 scoped_ptr
<ScopedVector
<DeviceState
> > device_list(
400 new ScopedVector
<DeviceState
>());
401 if (SearchDevices(1, true, device_list
.get())) {
402 ui_task_runner_
->PostTask(
404 base::Bind(&BluetoothTaskManagerWin::OnDevicesPolled
,
406 base::Owned(device_list
.release())));
410 bool BluetoothTaskManagerWin::SearchDevices(
411 int timeout_multiplier
,
412 bool search_cached_devices_only
,
413 ScopedVector
<DeviceState
>* device_list
) {
414 return SearchClassicDevices(
415 timeout_multiplier
, search_cached_devices_only
, device_list
) &&
416 SearchLowEnergyDevices(device_list
) &&
417 DiscoverServices(device_list
, search_cached_devices_only
);
420 bool BluetoothTaskManagerWin::SearchClassicDevices(
421 int timeout_multiplier
,
422 bool search_cached_devices_only
,
423 ScopedVector
<DeviceState
>* device_list
) {
424 // Issues a device inquiry and waits for |timeout_multiplier| * 1.28 seconds.
425 BLUETOOTH_DEVICE_SEARCH_PARAMS device_search_params
;
426 ZeroMemory(&device_search_params
, sizeof(device_search_params
));
427 device_search_params
.dwSize
= sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS
);
428 device_search_params
.fReturnAuthenticated
= 1;
429 device_search_params
.fReturnRemembered
= 1;
430 device_search_params
.fReturnUnknown
= (search_cached_devices_only
? 0 : 1);
431 device_search_params
.fReturnConnected
= 1;
432 device_search_params
.fIssueInquiry
= (search_cached_devices_only
? 0 : 1);
433 device_search_params
.cTimeoutMultiplier
= timeout_multiplier
;
435 BLUETOOTH_DEVICE_INFO device_info
;
436 ZeroMemory(&device_info
, sizeof(device_info
));
437 device_info
.dwSize
= sizeof(BLUETOOTH_DEVICE_INFO
);
438 HBLUETOOTH_DEVICE_FIND handle
=
439 BluetoothFindFirstDevice(&device_search_params
, &device_info
);
441 int last_error
= GetLastError();
442 if (last_error
== ERROR_NO_MORE_ITEMS
) {
443 return true; // No devices is not an error.
445 LogPollingError("Error calling BluetoothFindFirstDevice", last_error
);
450 DeviceState
* device_state
= new DeviceState();
451 GetDeviceState(device_info
, device_state
);
452 device_list
->push_back(device_state
);
454 // Reset device info before next call (as a safety precaution).
455 ZeroMemory(&device_info
, sizeof(device_info
));
456 device_info
.dwSize
= sizeof(BLUETOOTH_DEVICE_INFO
);
457 if (!BluetoothFindNextDevice(handle
, &device_info
)) {
458 int last_error
= GetLastError();
459 if (last_error
== ERROR_NO_MORE_ITEMS
) {
460 break; // No more items is expected error when done enumerating.
462 LogPollingError("Error calling BluetoothFindNextDevice", last_error
);
463 BluetoothFindDeviceClose(handle
);
468 if (!BluetoothFindDeviceClose(handle
)) {
469 LogPollingError("Error calling BluetoothFindDeviceClose", GetLastError());
475 bool BluetoothTaskManagerWin::SearchLowEnergyDevices(
476 ScopedVector
<DeviceState
>* device_list
) {
477 if (!win::IsBluetoothLowEnergySupported())
478 return true; // Bluetooth LE not supported is not an error.
480 ScopedVector
<win::BluetoothLowEnergyDeviceInfo
> btle_devices
;
483 win::EnumerateKnownBluetoothLowEnergyDevices(&btle_devices
, &error
);
485 LogPollingError(error
.c_str(), 0);
489 for (ScopedVector
<win::BluetoothLowEnergyDeviceInfo
>::iterator iter
=
490 btle_devices
.begin();
491 iter
!= btle_devices
.end();
493 win::BluetoothLowEnergyDeviceInfo
* device_info
= (*iter
);
494 DeviceState
* device_state
= new DeviceState();
495 device_state
->name
= device_info
->friendly_name
;
496 device_state
->address
=
497 BluetoothAddressToCanonicalString(device_info
->address
);
498 device_state
->visible
= device_info
->visible
;
499 device_state
->authenticated
= device_info
->authenticated
;
500 device_state
->connected
= device_info
->connected
;
501 device_state
->path
= device_info
->path
;
502 device_list
->push_back(device_state
);
507 bool BluetoothTaskManagerWin::DiscoverServices(
508 ScopedVector
<DeviceState
>* device_list
,
509 bool search_cached_services_only
) {
510 DCHECK(bluetooth_task_runner_
->RunsTasksOnCurrentThread());
511 net::EnsureWinsockInit();
512 for (ScopedVector
<DeviceState
>::iterator iter
= device_list
->begin();
513 iter
!= device_list
->end();
515 DeviceState
* device
= (*iter
);
516 ScopedVector
<ServiceRecordState
>* service_record_states
=
517 &(*iter
)->service_record_states
;
519 if ((*iter
)->is_bluetooth_classic()) {
520 if (!DiscoverClassicDeviceServices(device
->address
,
522 search_cached_services_only
,
523 service_record_states
)) {
527 if (!DiscoverLowEnergyDeviceServices(device
->path
,
528 service_record_states
)) {
536 bool BluetoothTaskManagerWin::DiscoverClassicDeviceServices(
537 const std::string
& device_address
,
538 const GUID
& protocol_uuid
,
539 bool search_cached_services_only
,
540 ScopedVector
<ServiceRecordState
>* service_record_states
) {
542 DiscoverClassicDeviceServicesWorker(device_address
,
544 search_cached_services_only
,
545 service_record_states
);
546 // If the device is "offline", no services are returned when specifying
547 // "LUP_FLUSHCACHE". Try again without flushing the cache so that the list
548 // of previously known services is returned.
549 if (!search_cached_services_only
&&
550 (error_code
== WSASERVICE_NOT_FOUND
|| error_code
== WSANO_DATA
)) {
551 error_code
= DiscoverClassicDeviceServicesWorker(
552 device_address
, protocol_uuid
, true, service_record_states
);
555 return (error_code
== ERROR_SUCCESS
);
558 int BluetoothTaskManagerWin::DiscoverClassicDeviceServicesWorker(
559 const std::string
& device_address
,
560 const GUID
& protocol_uuid
,
561 bool search_cached_services_only
,
562 ScopedVector
<ServiceRecordState
>* service_record_states
) {
563 // Bluetooth and WSAQUERYSET for Service Inquiry. See http://goo.gl/2v9pyt.
564 WSAQUERYSET sdp_query
;
565 ZeroMemory(&sdp_query
, sizeof(sdp_query
));
566 sdp_query
.dwSize
= sizeof(sdp_query
);
567 GUID protocol
= protocol_uuid
;
568 sdp_query
.lpServiceClassId
= &protocol
;
569 sdp_query
.dwNameSpace
= NS_BTH
;
570 wchar_t device_address_context
[kMaxNumDeviceAddressChar
];
571 std::size_t length
= base::SysUTF8ToWide("(" + device_address
+ ")").copy(
572 device_address_context
, kMaxNumDeviceAddressChar
);
573 device_address_context
[length
] = NULL
;
574 sdp_query
.lpszContext
= device_address_context
;
575 DWORD control_flags
= LUP_RETURN_ALL
;
576 // See http://goo.gl/t1Hulo: "Applications should generally specify
577 // LUP_FLUSHCACHE. This flag instructs the system to ignore any cached
578 // information and establish an over-the-air SDP connection to the specified
579 // device to perform the SDP search. This non-cached operation may take
580 // several seconds (whereas a cached search returns quickly)."
581 // In summary, we need to specify LUP_FLUSHCACHE if we want to obtain the list
582 // of services for devices which have not been discovered before.
583 if (!search_cached_services_only
)
584 control_flags
|= LUP_FLUSHCACHE
;
587 WSALookupServiceBegin(&sdp_query
, control_flags
, &sdp_handle
)) {
588 int last_error
= WSAGetLastError();
589 // If the device is "offline", no services are returned when specifying
590 // "LUP_FLUSHCACHE". Don't log error in that case.
591 if (!search_cached_services_only
&&
592 (last_error
== WSASERVICE_NOT_FOUND
|| last_error
== WSANO_DATA
)) {
595 LogPollingError("Error calling WSALookupServiceBegin", last_error
);
598 char sdp_buffer
[kServiceDiscoveryResultBufferSize
];
599 LPWSAQUERYSET sdp_result_data
= reinterpret_cast<LPWSAQUERYSET
>(sdp_buffer
);
601 DWORD sdp_buffer_size
= sizeof(sdp_buffer
);
603 WSALookupServiceNext(
604 sdp_handle
, control_flags
, &sdp_buffer_size
, sdp_result_data
)) {
605 int last_error
= WSAGetLastError();
606 if (last_error
== WSA_E_NO_MORE
|| last_error
== WSAENOMORE
) {
609 LogPollingError("Error calling WSALookupServiceNext", last_error
);
610 WSALookupServiceEnd(sdp_handle
);
613 ServiceRecordState
* service_record_state
= new ServiceRecordState();
614 service_record_state
->name
=
615 base::SysWideToUTF8(sdp_result_data
->lpszServiceInstanceName
);
616 for (uint64 i
= 0; i
< sdp_result_data
->lpBlob
->cbSize
; i
++) {
617 service_record_state
->sdp_bytes
.push_back(
618 sdp_result_data
->lpBlob
->pBlobData
[i
]);
620 service_record_states
->push_back(service_record_state
);
622 if (ERROR_SUCCESS
!= WSALookupServiceEnd(sdp_handle
)) {
623 int last_error
= WSAGetLastError();
624 LogPollingError("Error calling WSALookupServiceEnd", last_error
);
628 return ERROR_SUCCESS
;
631 bool BluetoothTaskManagerWin::DiscoverLowEnergyDeviceServices(
632 const base::FilePath
& device_path
,
633 ScopedVector
<ServiceRecordState
>* service_record_states
) {
634 if (!win::IsBluetoothLowEnergySupported())
635 return true; // Bluetooth LE not supported is not an error.
638 ScopedVector
<win::BluetoothLowEnergyServiceInfo
> services
;
639 bool success
= win::EnumerateKnownBluetoothLowEnergyServices(
640 device_path
, &services
, &error
);
642 LogPollingError(error
.c_str(), 0);
646 for (ScopedVector
<win::BluetoothLowEnergyServiceInfo
>::iterator iter2
=
648 iter2
!= services
.end();
650 ServiceRecordState
* service_state
= new ServiceRecordState();
651 service_state
->gatt_uuid
=
652 BluetoothLowEnergyUuidToBluetoothUuid((*iter2
)->uuid
);
653 service_record_states
->push_back(service_state
);
658 } // namespace device