OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "device/bluetooth/bluetooth_low_energy_win.h" | 5 #include "device/bluetooth/bluetooth_low_energy_win.h" |
6 | 6 |
7 #include <cfg.h> | |
8 #define INITGUID // For DEVPKEY_Xxxx guid/pid pairs | |
9 #include <devpkey.h> | |
10 | |
11 #include "base/logging.h" | 7 #include "base/logging.h" |
12 #include "base/strings/sys_string_conversions.h" | 8 #include "base/strings/sys_string_conversions.h" |
13 #include "base/win/windows_version.h" | 9 #include "base/win/windows_version.h" |
14 | 10 |
15 namespace { | 11 namespace { |
16 | 12 |
17 using device::win::DeviceRegistryPropertyValue; | 13 using device::win::DeviceRegistryPropertyValue; |
18 using device::win::DevicePropertyValue; | |
19 | 14 |
20 const char kPlatformNotSupported[] = | 15 const char kPlatformNotSupported[] = |
21 "Bluetooth Low energy is only supported on Windows 8 and later."; | 16 "Bluetooth Low energy is only supported on Windows 8 and later."; |
22 const char kDeviceEnumError[] = "Error enumerating Bluetooth LE devices."; | 17 const char kDeviceEnumError[] = "Error enumerating Bluetooth LE devices."; |
23 const char kDeviceInfoError[] = | 18 const char kDeviceInfoError[] = |
24 "Error retrieving Bluetooth LE device information."; | 19 "Error retrieving Bluetooth LE device information."; |
25 const char kDeviceAddressError[] = | 20 const char kDeviceAddressError[] = |
26 "Device instance ID value does not seem to contain a Bluetooth Adapter " | 21 "Device instance ID value does not seem to contain a Bluetooth Adapter " |
27 "address."; | 22 "address."; |
28 const char kDeviceFriendlyNameError[] = "Device name is not valid."; | 23 const char kDeviceFriendlyNameError[] = "Device name is not valid."; |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 const char* message, | 118 const char* message, |
124 std::string* error) { | 119 std::string* error) { |
125 if (actual_length != expected_length) { | 120 if (actual_length != expected_length) { |
126 *error = FormatBluetoothError(message, E_FAIL); | 121 *error = FormatBluetoothError(message, E_FAIL); |
127 return false; | 122 return false; |
128 } | 123 } |
129 | 124 |
130 return true; | 125 return true; |
131 } | 126 } |
132 | 127 |
133 bool CollectBluetoothLowEnergyDeviceProperty( | |
134 const ScopedDeviceInfoSetHandle& device_info_handle, | |
135 PSP_DEVINFO_DATA device_info_data, | |
136 const DEVPROPKEY& key, | |
137 scoped_ptr<DevicePropertyValue>* value, | |
138 std::string* error) { | |
139 DWORD required_length; | |
140 DEVPROPTYPE prop_type; | |
141 BOOL success = SetupDiGetDeviceProperty(device_info_handle, | |
142 device_info_data, | |
143 &key, | |
144 &prop_type, | |
145 NULL, | |
146 0, | |
147 &required_length, | |
148 0); | |
149 if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error)) | |
150 return false; | |
151 | |
152 scoped_ptr<uint8_t[]> prop_value(new uint8_t[required_length]); | |
153 DWORD actual_length = required_length; | |
154 success = SetupDiGetDeviceProperty(device_info_handle, | |
155 device_info_data, | |
156 &key, | |
157 &prop_type, | |
158 prop_value.get(), | |
159 actual_length, | |
160 &required_length, | |
161 0); | |
162 if (!CheckSuccess(!!success, kDeviceInfoError, error)) | |
163 return false; | |
164 if (!CheckExpectedLength( | |
165 actual_length, required_length, kDeviceInfoError, error)) { | |
166 return false; | |
167 } | |
168 | |
169 (*value) = scoped_ptr<DevicePropertyValue>( | |
170 new DevicePropertyValue(prop_type, prop_value.Pass(), actual_length)); | |
171 return true; | |
172 } | |
173 | |
174 bool CollectBluetoothLowEnergyDeviceRegistryProperty( | 128 bool CollectBluetoothLowEnergyDeviceRegistryProperty( |
175 const ScopedDeviceInfoSetHandle& device_info_handle, | 129 const ScopedDeviceInfoSetHandle& device_info_handle, |
176 PSP_DEVINFO_DATA device_info_data, | 130 PSP_DEVINFO_DATA device_info_data, |
177 DWORD property_id, | 131 DWORD property_id, |
178 scoped_ptr<DeviceRegistryPropertyValue>* value, | 132 scoped_ptr<DeviceRegistryPropertyValue>* value, |
179 std::string* error) { | 133 std::string* error) { |
180 ULONG required_length = 0; | 134 ULONG required_length = 0; |
181 BOOL success = SetupDiGetDeviceRegistryProperty(device_info_handle, | 135 BOOL success = SetupDiGetDeviceRegistryProperty(device_info_handle, |
182 device_info_data, | 136 device_info_data, |
183 property_id, | 137 property_id, |
184 NULL, | 138 NULL, |
185 NULL, | 139 NULL, |
186 0, | 140 0, |
187 &required_length); | 141 &required_length); |
188 if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error)) | 142 if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error)) |
189 return false; | 143 return false; |
190 | 144 |
191 scoped_ptr<uint8_t[]> property_value(new uint8_t[required_length]); | 145 scoped_ptr<UINT8[]> property_value(new UINT8[required_length]); |
192 ULONG actual_length = required_length; | 146 ULONG actual_length = required_length; |
193 DWORD property_type; | 147 DWORD property_type; |
194 success = SetupDiGetDeviceRegistryProperty(device_info_handle, | 148 success = SetupDiGetDeviceRegistryProperty(device_info_handle, |
195 device_info_data, | 149 device_info_data, |
196 property_id, | 150 property_id, |
197 &property_type, | 151 &property_type, |
198 property_value.get(), | 152 property_value.get(), |
199 actual_length, | 153 actual_length, |
200 &required_length); | 154 &required_length); |
201 if (!CheckSuccess(!!success, kDeviceInfoError, error)) | 155 if (!CheckSuccess(!!success, kDeviceInfoError, error)) |
202 return false; | 156 return false; |
203 if (!CheckExpectedLength( | 157 if (!CheckExpectedLength( |
204 actual_length, required_length, kDeviceInfoError, error)) { | 158 actual_length, required_length, kDeviceInfoError, error)) |
205 return false; | 159 return false; |
206 } | |
207 | 160 |
208 (*value) = DeviceRegistryPropertyValue::Create( | 161 (*value) = DeviceRegistryPropertyValue::Create( |
209 property_type, property_value.Pass(), actual_length).Pass(); | 162 property_type, property_value.Pass(), actual_length).Pass(); |
210 return true; | 163 return true; |
211 } | 164 } |
212 | 165 |
213 bool CollectBluetoothLowEnergyDeviceInstanceId( | 166 bool CollectBluetoothLowEnergyDeviceInstanceId( |
214 const ScopedDeviceInfoSetHandle& device_info_handle, | 167 const ScopedDeviceInfoSetHandle& device_info_handle, |
215 PSP_DEVINFO_DATA device_info_data, | 168 PSP_DEVINFO_DATA device_info_data, |
216 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info, | 169 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info, |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 std::string* error) { | 248 std::string* error) { |
296 // TODO(rpaquay): We exctract the bluetooth device address from the device | 249 // TODO(rpaquay): We exctract the bluetooth device address from the device |
297 // instance ID string, as we did not find a more formal API for retrieving the | 250 // instance ID string, as we did not find a more formal API for retrieving the |
298 // bluetooth address of a Bluetooth Low Energy device. | 251 // bluetooth address of a Bluetooth Low Energy device. |
299 // An Bluetooth device instance ID has the following format (under Win8+): | 252 // An Bluetooth device instance ID has the following format (under Win8+): |
300 // BTHLE\DEV_BC6A29AB5FB0\8&31038925&0&BC6A29AB5FB0 | 253 // BTHLE\DEV_BC6A29AB5FB0\8&31038925&0&BC6A29AB5FB0 |
301 return ExtractBluetoothAddressFromDeviceInstanceId( | 254 return ExtractBluetoothAddressFromDeviceInstanceId( |
302 device_info->id, &device_info->address, error); | 255 device_info->id, &device_info->address, error); |
303 } | 256 } |
304 | 257 |
305 bool CollectBluetoothLowEnergyDeviceStatus( | |
306 const ScopedDeviceInfoSetHandle& device_info_handle, | |
307 PSP_DEVINFO_DATA device_info_data, | |
308 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info, | |
309 std::string* error) { | |
310 scoped_ptr<DevicePropertyValue> value; | |
311 if (!CollectBluetoothLowEnergyDeviceProperty(device_info_handle, | |
312 device_info_data, | |
313 DEVPKEY_Device_DevNodeStatus, | |
314 &value, | |
315 error)) { | |
316 return false; | |
317 } | |
318 | |
319 if (value->property_type() != DEVPROP_TYPE_UINT32) { | |
320 *error = kDeviceInfoError; | |
321 return false; | |
322 } | |
323 | |
324 device_info->connected = !(value->AsUint32() & DN_DEVICE_DISCONNECTED); | |
325 return true; | |
326 } | |
327 | |
328 bool CollectBluetoothLowEnergyDeviceInfo( | 258 bool CollectBluetoothLowEnergyDeviceInfo( |
329 const ScopedDeviceInfoSetHandle& device_info_handle, | 259 const ScopedDeviceInfoSetHandle& device_info_handle, |
330 PSP_DEVICE_INTERFACE_DATA device_interface_data, | 260 PSP_DEVICE_INTERFACE_DATA device_interface_data, |
331 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info, | 261 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info, |
332 std::string* error) { | 262 std::string* error) { |
333 // Retrieve required # of bytes for interface details | 263 // Retrieve required # of bytes for interface details |
334 ULONG required_length = 0; | 264 ULONG required_length = 0; |
335 BOOL success = SetupDiGetDeviceInterfaceDetail(device_info_handle, | 265 BOOL success = SetupDiGetDeviceInterfaceDetail(device_info_handle, |
336 device_interface_data, | 266 device_interface_data, |
337 NULL, | 267 NULL, |
338 0, | 268 0, |
339 &required_length, | 269 &required_length, |
340 NULL); | 270 NULL); |
341 if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error)) | 271 if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error)) |
342 return false; | 272 return false; |
343 | 273 |
344 scoped_ptr<uint8_t[]> interface_data(new uint8_t[required_length]); | 274 scoped_ptr<UINT8[]> interface_data(new UINT8[required_length]); |
345 ZeroMemory(interface_data.get(), required_length); | 275 ZeroMemory(interface_data.get(), required_length); |
346 | 276 |
347 PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data = | 277 PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data = |
348 reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(interface_data.get()); | 278 reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(interface_data.get()); |
349 device_interface_detail_data->cbSize = | 279 device_interface_detail_data->cbSize = |
350 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | 280 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); |
351 | 281 |
352 SP_DEVINFO_DATA device_info_data = {0}; | 282 SP_DEVINFO_DATA device_info_data = {0}; |
353 device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); | 283 device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); |
354 | 284 |
355 ULONG actual_length = required_length; | 285 ULONG actual_length = required_length; |
356 success = SetupDiGetDeviceInterfaceDetail(device_info_handle, | 286 success = SetupDiGetDeviceInterfaceDetail(device_info_handle, |
357 device_interface_data, | 287 device_interface_data, |
358 device_interface_detail_data, | 288 device_interface_detail_data, |
359 actual_length, | 289 actual_length, |
360 &required_length, | 290 &required_length, |
361 &device_info_data); | 291 &device_info_data); |
362 if (!CheckSuccess(!!success, kDeviceInfoError, error)) | 292 if (!CheckSuccess(!!success, kDeviceInfoError, error)) |
363 return false; | 293 return false; |
364 if (!CheckExpectedLength( | 294 if (!CheckExpectedLength( |
365 actual_length, required_length, kDeviceInfoError, error)) { | 295 actual_length, required_length, kDeviceInfoError, error)) |
366 return false; | 296 return false; |
367 } | |
368 | 297 |
369 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo> result( | 298 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo> result( |
370 new device::win::BluetoothLowEnergyDeviceInfo()); | 299 new device::win::BluetoothLowEnergyDeviceInfo()); |
371 result->path = | 300 result->path = |
372 base::FilePath(std::wstring(device_interface_detail_data->DevicePath)); | 301 base::FilePath(std::wstring(device_interface_detail_data->DevicePath)); |
373 if (!CollectBluetoothLowEnergyDeviceInstanceId( | 302 if (!CollectBluetoothLowEnergyDeviceInstanceId( |
374 device_info_handle, &device_info_data, result, error)) { | 303 device_info_handle, &device_info_data, result, error)) { |
375 return false; | 304 return false; |
376 } | 305 } |
377 if (!CollectDeviceFriendlyName( | 306 if (!CollectDeviceFriendlyName( |
378 device_info_handle, &device_info_data, result, error)) { | 307 device_info_handle, &device_info_data, result, error)) { |
379 return false; | 308 return false; |
380 } | 309 } |
381 if (!CollectBluetoothLowEnergyDeviceAddress( | 310 if (!CollectBluetoothLowEnergyDeviceAddress( |
382 device_info_handle, &device_info_data, result, error)) { | 311 device_info_handle, &device_info_data, result, error)) { |
383 return false; | 312 return false; |
384 } | 313 } |
385 if (!CollectBluetoothLowEnergyDeviceStatus( | |
386 device_info_handle, &device_info_data, result, error)) { | |
387 return false; | |
388 } | |
389 (*device_info) = result.Pass(); | 314 (*device_info) = result.Pass(); |
390 return true; | 315 return true; |
391 } | 316 } |
392 | 317 |
393 enum DeviceInfoResult { kOk, kError, kNoMoreDevices }; | 318 enum DeviceInfoResult { kOk, kError, kNoMoreDevices }; |
394 | 319 |
395 DeviceInfoResult EnumerateSingleBluetoothLowEnergyDevice( | 320 DeviceInfoResult EnumerateSingleBluetoothLowEnergyDevice( |
396 const ScopedDeviceInfoSetHandle& device_info_handle, | 321 const ScopedDeviceInfoSetHandle& device_info_handle, |
397 DWORD device_index, | 322 DWORD device_index, |
398 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info, | 323 scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info, |
399 std::string* error) { | 324 std::string* error) { |
400 // Enumerate device of BLUETOOTHLE_DEVICE interface class | 325 // Enumerate device of LE_DEVICE interface class |
401 GUID BluetoothInterfaceGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE; | 326 GUID BluetoothInterfaceGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE; |
402 SP_DEVICE_INTERFACE_DATA device_interface_data = {0}; | 327 SP_DEVICE_INTERFACE_DATA device_interface_data = {0}; |
403 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | 328 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
404 BOOL success = ::SetupDiEnumDeviceInterfaces(device_info_handle, | 329 BOOL success = ::SetupDiEnumDeviceInterfaces(device_info_handle, |
405 NULL, | 330 NULL, |
406 &BluetoothInterfaceGUID, | 331 &BluetoothInterfaceGUID, |
407 device_index, | 332 device_index, |
408 &device_interface_data); | 333 &device_interface_data); |
409 if (!success) { | 334 if (!success) { |
410 HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); | 335 HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); |
411 if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { | 336 if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { |
412 return kNoMoreDevices; | 337 return kNoMoreDevices; |
413 } | 338 } |
414 *error = FormatBluetoothError(kDeviceInfoError, hr); | 339 *error = FormatBluetoothError(kDeviceInfoError, hr); |
415 return kError; | 340 return kError; |
416 } | 341 } |
417 | 342 |
418 if (!CollectBluetoothLowEnergyDeviceInfo( | 343 if (!CollectBluetoothLowEnergyDeviceInfo( |
419 device_info_handle, &device_interface_data, device_info, error)) { | 344 device_info_handle, &device_interface_data, device_info, error)) |
420 return kError; | 345 return kError; |
421 } | |
422 | 346 |
423 return kOk; | 347 return kOk; |
424 } | 348 } |
425 | 349 |
426 // Opens a Device Info Set that can be used to enumerate Bluetooth LE devices | 350 // Opens a Device Info Set that can be used to enumerate Bluetooth LE devices |
427 // present on the machine. | 351 // present on the machine. |
428 HRESULT OpenBluetoothLowEnergyDevices(ScopedDeviceInfoSetHandle* handle) { | 352 HRESULT OpenBluetoothLowEnergyDevices(ScopedDeviceInfoSetHandle* handle) { |
429 GUID BluetoothClassGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE; | 353 GUID BluetoothClassGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE; |
430 ScopedDeviceInfoSetHandle result(SetupDiGetClassDevs( | 354 ScopedDeviceInfoSetHandle result(SetupDiGetClassDevs( |
431 &BluetoothClassGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); | 355 &BluetoothClassGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 WCHAR* value_string = reinterpret_cast<WCHAR*>(value_.get()); | 421 WCHAR* value_string = reinterpret_cast<WCHAR*>(value_.get()); |
498 return base::SysWideToUTF8(value_string); | 422 return base::SysWideToUTF8(value_string); |
499 } | 423 } |
500 | 424 |
501 DWORD DeviceRegistryPropertyValue::AsDWORD() const { | 425 DWORD DeviceRegistryPropertyValue::AsDWORD() const { |
502 CHECK_EQ(property_type_, static_cast<DWORD>(REG_DWORD)); | 426 CHECK_EQ(property_type_, static_cast<DWORD>(REG_DWORD)); |
503 DWORD* value = reinterpret_cast<DWORD*>(value_.get()); | 427 DWORD* value = reinterpret_cast<DWORD*>(value_.get()); |
504 return *value; | 428 return *value; |
505 } | 429 } |
506 | 430 |
507 DevicePropertyValue::DevicePropertyValue(DEVPROPTYPE property_type, | |
508 scoped_ptr<uint8_t[]> value, | |
509 size_t value_size) | |
510 : property_type_(property_type), | |
511 value_(value.Pass()), | |
512 value_size_(value_size) { | |
513 } | |
514 | |
515 uint32_t DevicePropertyValue::AsUint32() const { | |
516 CHECK_EQ(property_type_, static_cast<DEVPROPTYPE>(DEVPROP_TYPE_UINT32)); | |
517 CHECK_EQ(value_size_, sizeof(uint32_t)); | |
518 return *reinterpret_cast<uint32_t*>(value_.get()); | |
519 } | |
520 | |
521 BluetoothLowEnergyDeviceInfo::BluetoothLowEnergyDeviceInfo() | |
522 : connected(false) { | |
523 address.ullLong = BLUETOOTH_NULL_ADDRESS; | |
524 } | |
525 | |
526 BluetoothLowEnergyDeviceInfo::~BluetoothLowEnergyDeviceInfo() { | |
527 } | |
528 | |
529 bool IsBluetoothLowEnergySupported() { | 431 bool IsBluetoothLowEnergySupported() { |
530 return base::win::GetVersion() >= base::win::VERSION_WIN8; | 432 return base::win::GetVersion() >= base::win::VERSION_WIN8; |
531 } | 433 } |
532 | 434 |
533 bool EnumerateKnownBluetoothLowEnergyDevices( | 435 bool EnumerateKnownBluetoothLowEnergyDevices( |
534 ScopedVector<BluetoothLowEnergyDeviceInfo>* devices, | 436 ScopedVector<BluetoothLowEnergyDeviceInfo>* devices, |
535 std::string* error) { | 437 std::string* error) { |
536 if (!IsBluetoothLowEnergySupported()) { | 438 if (!IsBluetoothLowEnergySupported()) { |
537 *error = kPlatformNotSupported; | 439 *error = kPlatformNotSupported; |
538 return false; | 440 return false; |
(...skipping 23 matching lines...) Expand all Loading... |
562 | 464 |
563 bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting( | 465 bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting( |
564 const std::string& instance_id, | 466 const std::string& instance_id, |
565 BLUETOOTH_ADDRESS* btha, | 467 BLUETOOTH_ADDRESS* btha, |
566 std::string* error) { | 468 std::string* error) { |
567 return ExtractBluetoothAddressFromDeviceInstanceId(instance_id, btha, error); | 469 return ExtractBluetoothAddressFromDeviceInstanceId(instance_id, btha, error); |
568 } | 470 } |
569 | 471 |
570 } // namespace win | 472 } // namespace win |
571 } // namespace device | 473 } // namespace device |
OLD | NEW |