Home | History | Annotate | Download | only in bluetooth
      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
      3 // found in the LICENSE file.
      4 
      5 #include "device/bluetooth/bluetooth_low_energy_win.h"
      6 
      7 #include "base/files/file.h"
      8 #include "base/logging.h"
      9 #include "base/strings/sys_string_conversions.h"
     10 #include "base/win/scoped_handle.h"
     11 #include "base/win/windows_version.h"
     12 
     13 namespace {
     14 
     15 using device::win::DeviceRegistryPropertyValue;
     16 using device::win::DevicePropertyValue;
     17 using device::win::BluetoothLowEnergyDeviceInfo;
     18 using device::win::BluetoothLowEnergyServiceInfo;
     19 
     20 const char kPlatformNotSupported[] =
     21     "Bluetooth Low energy is only supported on Windows 8 and later.";
     22 const char kDeviceEnumError[] = "Error enumerating Bluetooth LE devices.";
     23 const char kDeviceInfoError[] =
     24     "Error retrieving Bluetooth LE device information.";
     25 const char kDeviceAddressError[] =
     26     "Device instance ID value does not seem to contain a Bluetooth Adapter "
     27     "address.";
     28 const char kDeviceFriendlyNameError[] = "Device name is not valid.";
     29 const char kInvalidBluetoothAddress[] = "Bluetooth address format is invalid.";
     30 
     31 // Like ScopedHandle but for HDEVINFO.  Only use this on HDEVINFO returned from
     32 // SetupDiGetClassDevs.
     33 class DeviceInfoSetTraits {
     34  public:
     35   typedef HDEVINFO Handle;
     36 
     37   static bool CloseHandle(HDEVINFO handle) {
     38     return ::SetupDiDestroyDeviceInfoList(handle) != FALSE;
     39   }
     40 
     41   static bool IsHandleValid(HDEVINFO handle) {
     42     return handle != INVALID_HANDLE_VALUE;
     43   }
     44 
     45   static HDEVINFO NullHandle() { return INVALID_HANDLE_VALUE; }
     46 
     47  private:
     48   DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceInfoSetTraits);
     49 };
     50 
     51 typedef base::win::GenericScopedHandle<DeviceInfoSetTraits,
     52                                        base::win::VerifierTraits>
     53     ScopedDeviceInfoSetHandle;
     54 
     55 bool StringToBluetoothAddress(const std::string& value,
     56                               BLUETOOTH_ADDRESS* btha,
     57                               std::string* error) {
     58   if (value.length() != 6 * 2) {
     59     *error = kInvalidBluetoothAddress;
     60     return false;
     61   }
     62 
     63   int buffer[6];
     64   int result = sscanf_s(value.c_str(),
     65                         "%02X%02X%02X%02X%02X%02X",
     66                         &buffer[5],
     67                         &buffer[4],
     68                         &buffer[3],
     69                         &buffer[2],
     70                         &buffer[1],
     71                         &buffer[0]);
     72   if (result != 6) {
     73     *error = kInvalidBluetoothAddress;
     74     return false;
     75   }
     76 
     77   ZeroMemory(btha, sizeof(*btha));
     78   btha->rgBytes[0] = buffer[0];
     79   btha->rgBytes[1] = buffer[1];
     80   btha->rgBytes[2] = buffer[2];
     81   btha->rgBytes[3] = buffer[3];
     82   btha->rgBytes[4] = buffer[4];
     83   btha->rgBytes[5] = buffer[5];
     84   return true;
     85 }
     86 
     87 std::string FormatBluetoothError(const char* message, HRESULT hr) {
     88   std::ostringstream string_stream;
     89   string_stream << message;
     90   if (FAILED(hr))
     91     string_stream << logging::SystemErrorCodeToString(hr);
     92   return string_stream.str();
     93 }
     94 
     95 bool CheckInsufficientBuffer(bool success,
     96                              const char* message,
     97                              std::string* error) {
     98   if (success) {
     99     *error = FormatBluetoothError(message, S_OK);
    100     return false;
    101   }
    102 
    103   HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
    104   if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
    105     *error = FormatBluetoothError(message, hr);
    106     return false;
    107   }
    108 
    109   return true;
    110 }
    111 
    112 bool CheckHResult(HRESULT hr, const char* message, std::string* error) {
    113   if (FAILED(hr)) {
    114     *error = FormatBluetoothError(message, hr);
    115     return false;
    116   }
    117 
    118   return true;
    119 }
    120 
    121 bool CheckSuccess(bool success, const char* message, std::string* error) {
    122   if (!success) {
    123     CheckHResult(HRESULT_FROM_WIN32(GetLastError()), message, error);
    124     return false;
    125   }
    126 
    127   return true;
    128 }
    129 
    130 bool CheckNoData(HRESULT hr, size_t length) {
    131   if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
    132     return true;
    133 
    134   if (SUCCEEDED(hr) && length == 0)
    135     return true;
    136 
    137   return false;
    138 }
    139 
    140 bool CheckMoreData(HRESULT hr, const char* message, std::string* error) {
    141   if (SUCCEEDED(hr)) {
    142     *error = FormatBluetoothError(message, hr);
    143     return false;
    144   }
    145 
    146   if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
    147     *error = FormatBluetoothError(message, hr);
    148     return false;
    149   }
    150 
    151   return true;
    152 }
    153 
    154 bool CheckExpectedLength(size_t actual_length,
    155                          size_t expected_length,
    156                          const char* message,
    157                          std::string* error) {
    158   if (actual_length != expected_length) {
    159     *error = FormatBluetoothError(message, E_FAIL);
    160     return false;
    161   }
    162 
    163   return true;
    164 }
    165 
    166 bool CollectBluetoothLowEnergyDeviceProperty(
    167     const ScopedDeviceInfoSetHandle& device_info_handle,
    168     PSP_DEVINFO_DATA device_info_data,
    169     const DEVPROPKEY& key,
    170     scoped_ptr<DevicePropertyValue>* value,
    171     std::string* error) {
    172   DWORD required_length;
    173   DEVPROPTYPE prop_type;
    174   BOOL success = SetupDiGetDeviceProperty(device_info_handle,
    175                                           device_info_data,
    176                                           &key,
    177                                           &prop_type,
    178                                           NULL,
    179                                           0,
    180                                           &required_length,
    181                                           0);
    182   if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
    183     return false;
    184 
    185   scoped_ptr<uint8_t[]> prop_value(new uint8_t[required_length]);
    186   DWORD actual_length = required_length;
    187   success = SetupDiGetDeviceProperty(device_info_handle,
    188                                      device_info_data,
    189                                      &key,
    190                                      &prop_type,
    191                                      prop_value.get(),
    192                                      actual_length,
    193                                      &required_length,
    194                                      0);
    195   if (!CheckSuccess(!!success, kDeviceInfoError, error))
    196     return false;
    197   if (!CheckExpectedLength(
    198           actual_length, required_length, kDeviceInfoError, error)) {
    199     return false;
    200   }
    201 
    202   (*value) = scoped_ptr<DevicePropertyValue>(
    203       new DevicePropertyValue(prop_type, prop_value.Pass(), actual_length));
    204   return true;
    205 }
    206 
    207 bool CollectBluetoothLowEnergyDeviceRegistryProperty(
    208     const ScopedDeviceInfoSetHandle& device_info_handle,
    209     PSP_DEVINFO_DATA device_info_data,
    210     DWORD property_id,
    211     scoped_ptr<DeviceRegistryPropertyValue>* value,
    212     std::string* error) {
    213   ULONG required_length = 0;
    214   BOOL success = SetupDiGetDeviceRegistryProperty(device_info_handle,
    215                                                   device_info_data,
    216                                                   property_id,
    217                                                   NULL,
    218                                                   NULL,
    219                                                   0,
    220                                                   &required_length);
    221   if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
    222     return false;
    223 
    224   scoped_ptr<uint8_t[]> property_value(new uint8_t[required_length]);
    225   ULONG actual_length = required_length;
    226   DWORD property_type;
    227   success = SetupDiGetDeviceRegistryProperty(device_info_handle,
    228                                              device_info_data,
    229                                              property_id,
    230                                              &property_type,
    231                                              property_value.get(),
    232                                              actual_length,
    233                                              &required_length);
    234   if (!CheckSuccess(!!success, kDeviceInfoError, error))
    235     return false;
    236   if (!CheckExpectedLength(
    237           actual_length, required_length, kDeviceInfoError, error)) {
    238     return false;
    239   }
    240 
    241   (*value) = DeviceRegistryPropertyValue::Create(
    242                  property_type, property_value.Pass(), actual_length).Pass();
    243   return true;
    244 }
    245 
    246 bool CollectBluetoothLowEnergyDeviceInstanceId(
    247     const ScopedDeviceInfoSetHandle& device_info_handle,
    248     PSP_DEVINFO_DATA device_info_data,
    249     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
    250     std::string* error) {
    251   ULONG required_length = 0;
    252   BOOL success = SetupDiGetDeviceInstanceId(
    253       device_info_handle, device_info_data, NULL, 0, &required_length);
    254   if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
    255     return false;
    256 
    257   scoped_ptr<WCHAR[]> instance_id(new WCHAR[required_length]);
    258   ULONG actual_length = required_length;
    259   success = SetupDiGetDeviceInstanceId(device_info_handle,
    260                                        device_info_data,
    261                                        instance_id.get(),
    262                                        actual_length,
    263                                        &required_length);
    264   if (!CheckSuccess(!!success, kDeviceInfoError, error))
    265     return false;
    266   if (!CheckExpectedLength(
    267           actual_length, required_length, kDeviceInfoError, error)) {
    268     return false;
    269   }
    270 
    271   if (actual_length >= 1) {
    272     // Ensure string is zero terminated.
    273     instance_id.get()[actual_length - 1] = 0;
    274     device_info->id = base::SysWideToUTF8(instance_id.get());
    275   }
    276   return true;
    277 }
    278 
    279 bool CollectBluetoothLowEnergyDeviceFriendlyName(
    280     const ScopedDeviceInfoSetHandle& device_info_handle,
    281     PSP_DEVINFO_DATA device_info_data,
    282     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
    283     std::string* error) {
    284   scoped_ptr<DeviceRegistryPropertyValue> property_value;
    285   if (!CollectBluetoothLowEnergyDeviceRegistryProperty(device_info_handle,
    286                                                        device_info_data,
    287                                                        SPDRP_FRIENDLYNAME,
    288                                                        &property_value,
    289                                                        error)) {
    290     return false;
    291   }
    292 
    293   if (property_value->property_type() != REG_SZ) {
    294     *error = kDeviceFriendlyNameError;
    295     return false;
    296   }
    297 
    298   device_info->friendly_name = property_value->AsString();
    299   return true;
    300 }
    301 
    302 bool ExtractBluetoothAddressFromDeviceInstanceId(const std::string& instance_id,
    303                                                  BLUETOOTH_ADDRESS* btha,
    304                                                  std::string* error) {
    305   size_t start = instance_id.find("_");
    306   if (start == std::string::npos) {
    307     *error = kDeviceAddressError;
    308     return false;
    309   }
    310   size_t end = instance_id.find("\\", start);
    311   if (end == std::string::npos) {
    312     *error = kDeviceAddressError;
    313     return false;
    314   }
    315 
    316   start++;
    317   std::string address = instance_id.substr(start, end - start);
    318   if (!StringToBluetoothAddress(address, btha, error))
    319     return false;
    320 
    321   return true;
    322 }
    323 
    324 bool CollectBluetoothLowEnergyDeviceAddress(
    325     const ScopedDeviceInfoSetHandle& device_info_handle,
    326     PSP_DEVINFO_DATA device_info_data,
    327     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
    328     std::string* error) {
    329   // TODO(rpaquay): We exctract the bluetooth device address from the device
    330   // instance ID string, as we did not find a more formal API for retrieving the
    331   // bluetooth address of a Bluetooth Low Energy device.
    332   // An Bluetooth device instance ID has the following format (under Win8+):
    333   // BTHLE\DEV_BC6A29AB5FB0\8&31038925&0&BC6A29AB5FB0
    334   return ExtractBluetoothAddressFromDeviceInstanceId(
    335       device_info->id, &device_info->address, error);
    336 }
    337 
    338 bool CollectBluetoothLowEnergyDeviceStatus(
    339     const ScopedDeviceInfoSetHandle& device_info_handle,
    340     PSP_DEVINFO_DATA device_info_data,
    341     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
    342     std::string* error) {
    343   scoped_ptr<DevicePropertyValue> value;
    344   if (!CollectBluetoothLowEnergyDeviceProperty(device_info_handle,
    345                                                device_info_data,
    346                                                DEVPKEY_Device_DevNodeStatus,
    347                                                &value,
    348                                                error)) {
    349     return false;
    350   }
    351 
    352   if (value->property_type() != DEVPROP_TYPE_UINT32) {
    353     *error = kDeviceInfoError;
    354     return false;
    355   }
    356 
    357   device_info->connected = !(value->AsUint32() & DN_DEVICE_DISCONNECTED);
    358   // Windows 8 exposes BLE devices only if they are visible and paired. This
    359   // might change in the future if Windows offers a public API for discovering
    360   // and pairing BLE devices.
    361   device_info->visible = true;
    362   device_info->authenticated = true;
    363   return true;
    364 }
    365 
    366 bool CollectBluetoothLowEnergyDeviceServices(
    367     const base::FilePath& device_path,
    368     ScopedVector<BluetoothLowEnergyServiceInfo>* services,
    369     std::string* error) {
    370   base::File file(device_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
    371   if (!file.IsValid()) {
    372     *error = file.ErrorToString(file.error_details());
    373     return false;
    374   }
    375 
    376   USHORT required_length;
    377   HRESULT hr = BluetoothGATTGetServices(file.GetPlatformFile(),
    378                                         0,
    379                                         NULL,
    380                                         &required_length,
    381                                         BLUETOOTH_GATT_FLAG_NONE);
    382   if (CheckNoData(hr, required_length))
    383     return true;
    384   if (!CheckMoreData(hr, kDeviceInfoError, error))
    385     return false;
    386 
    387   scoped_ptr<BTH_LE_GATT_SERVICE[]> gatt_services(
    388       new BTH_LE_GATT_SERVICE[required_length]);
    389   USHORT actual_length = required_length;
    390   hr = BluetoothGATTGetServices(file.GetPlatformFile(),
    391                                 actual_length,
    392                                 gatt_services.get(),
    393                                 &required_length,
    394                                 BLUETOOTH_GATT_FLAG_NONE);
    395   if (!CheckHResult(hr, kDeviceInfoError, error))
    396     return false;
    397   if (!CheckExpectedLength(
    398           actual_length, required_length, kDeviceInfoError, error)) {
    399     return false;
    400   }
    401 
    402   for (USHORT i = 0; i < actual_length; ++i) {
    403     BTH_LE_GATT_SERVICE& gatt_service(gatt_services.get()[i]);
    404     BluetoothLowEnergyServiceInfo* service_info =
    405         new BluetoothLowEnergyServiceInfo();
    406     service_info->uuid = gatt_service.ServiceUuid;
    407     services->push_back(service_info);
    408   }
    409 
    410   return true;
    411 }
    412 
    413 bool CollectBluetoothLowEnergyDeviceInfo(
    414     const ScopedDeviceInfoSetHandle& device_info_handle,
    415     PSP_DEVICE_INTERFACE_DATA device_interface_data,
    416     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info,
    417     std::string* error) {
    418   // Retrieve required # of bytes for interface details
    419   ULONG required_length = 0;
    420   BOOL success = SetupDiGetDeviceInterfaceDetail(device_info_handle,
    421                                                  device_interface_data,
    422                                                  NULL,
    423                                                  0,
    424                                                  &required_length,
    425                                                  NULL);
    426   if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
    427     return false;
    428 
    429   scoped_ptr<uint8_t[]> interface_data(new uint8_t[required_length]);
    430   ZeroMemory(interface_data.get(), required_length);
    431 
    432   PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data =
    433       reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(interface_data.get());
    434   device_interface_detail_data->cbSize =
    435       sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
    436 
    437   SP_DEVINFO_DATA device_info_data = {0};
    438   device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
    439 
    440   ULONG actual_length = required_length;
    441   success = SetupDiGetDeviceInterfaceDetail(device_info_handle,
    442                                             device_interface_data,
    443                                             device_interface_detail_data,
    444                                             actual_length,
    445                                             &required_length,
    446                                             &device_info_data);
    447   if (!CheckSuccess(!!success, kDeviceInfoError, error))
    448     return false;
    449   if (!CheckExpectedLength(
    450           actual_length, required_length, kDeviceInfoError, error)) {
    451     return false;
    452   }
    453 
    454   scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo> result(
    455       new device::win::BluetoothLowEnergyDeviceInfo());
    456   result->path =
    457       base::FilePath(std::wstring(device_interface_detail_data->DevicePath));
    458   if (!CollectBluetoothLowEnergyDeviceInstanceId(
    459           device_info_handle, &device_info_data, result, error)) {
    460     return false;
    461   }
    462   if (!CollectBluetoothLowEnergyDeviceFriendlyName(
    463           device_info_handle, &device_info_data, result, error)) {
    464     return false;
    465   }
    466   if (!CollectBluetoothLowEnergyDeviceAddress(
    467           device_info_handle, &device_info_data, result, error)) {
    468     return false;
    469   }
    470   if (!CollectBluetoothLowEnergyDeviceStatus(
    471           device_info_handle, &device_info_data, result, error)) {
    472     return false;
    473   }
    474   (*device_info) = result.Pass();
    475   return true;
    476 }
    477 
    478 enum DeviceInfoResult { kOk, kError, kNoMoreDevices };
    479 
    480 DeviceInfoResult EnumerateSingleBluetoothLowEnergyDevice(
    481     const ScopedDeviceInfoSetHandle& device_info_handle,
    482     DWORD device_index,
    483     scoped_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info,
    484     std::string* error) {
    485   // Enumerate device of BLUETOOTHLE_DEVICE interface class
    486   GUID BluetoothInterfaceGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE;
    487   SP_DEVICE_INTERFACE_DATA device_interface_data = {0};
    488   device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    489   BOOL success = ::SetupDiEnumDeviceInterfaces(device_info_handle,
    490                                                NULL,
    491                                                &BluetoothInterfaceGUID,
    492                                                device_index,
    493                                                &device_interface_data);
    494   if (!success) {
    495     HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
    496     if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
    497       return kNoMoreDevices;
    498     }
    499     *error = FormatBluetoothError(kDeviceInfoError, hr);
    500     return kError;
    501   }
    502 
    503   if (!CollectBluetoothLowEnergyDeviceInfo(
    504           device_info_handle, &device_interface_data, device_info, error)) {
    505     return kError;
    506   }
    507 
    508   return kOk;
    509 }
    510 
    511 // Opens a Device Info Set that can be used to enumerate Bluetooth LE devices
    512 // present on the machine.
    513 HRESULT OpenBluetoothLowEnergyDevices(ScopedDeviceInfoSetHandle* handle) {
    514   GUID BluetoothClassGUID = GUID_BLUETOOTHLE_DEVICE_INTERFACE;
    515   ScopedDeviceInfoSetHandle result(SetupDiGetClassDevs(
    516       &BluetoothClassGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
    517   if (!result.IsValid()) {
    518     return HRESULT_FROM_WIN32(::GetLastError());
    519   }
    520 
    521   (*handle) = result.Pass();
    522   return S_OK;
    523 }
    524 
    525 // Opens a Device Info Set that can be used to enumerate Bluetooth LE devices
    526 // exposing a service GUID.
    527 HRESULT OpenBluetoothLowEnergyService(const GUID& service_guid,
    528                                       ScopedDeviceInfoSetHandle* handle) {
    529   ScopedDeviceInfoSetHandle result(SetupDiGetClassDevs(
    530       &service_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
    531   if (!result.IsValid()) {
    532     return HRESULT_FROM_WIN32(::GetLastError());
    533   }
    534 
    535   (*handle) = result.Pass();
    536   return S_OK;
    537 }
    538 
    539 }  // namespace
    540 
    541 namespace device {
    542 namespace win {
    543 
    544 // static
    545 scoped_ptr<DeviceRegistryPropertyValue> DeviceRegistryPropertyValue::Create(
    546     DWORD property_type,
    547     scoped_ptr<uint8_t[]> value,
    548     size_t value_size) {
    549   switch (property_type) {
    550     case REG_SZ: {
    551       // Ensure string is zero terminated.
    552       size_t character_size = value_size / sizeof(WCHAR);
    553       CHECK_EQ(character_size * sizeof(WCHAR), value_size);
    554       CHECK_GE(character_size, 1u);
    555       WCHAR* value_string = reinterpret_cast<WCHAR*>(value.get());
    556       value_string[character_size - 1] = 0;
    557       break;
    558     }
    559     case REG_DWORD: {
    560       CHECK_EQ(value_size, sizeof(DWORD));
    561       break;
    562     }
    563   }
    564   return scoped_ptr<DeviceRegistryPropertyValue>(
    565       new DeviceRegistryPropertyValue(property_type, value.Pass(), value_size));
    566 }
    567 
    568 DeviceRegistryPropertyValue::DeviceRegistryPropertyValue(
    569     DWORD property_type,
    570     scoped_ptr<uint8_t[]> value,
    571     size_t value_size)
    572     : property_type_(property_type),
    573       value_(value.Pass()),
    574       value_size_(value_size) {
    575 }
    576 
    577 DeviceRegistryPropertyValue::~DeviceRegistryPropertyValue() {
    578 }
    579 
    580 std::string DeviceRegistryPropertyValue::AsString() const {
    581   CHECK_EQ(property_type_, static_cast<DWORD>(REG_SZ));
    582   WCHAR* value_string = reinterpret_cast<WCHAR*>(value_.get());
    583   return base::SysWideToUTF8(value_string);
    584 }
    585 
    586 DWORD DeviceRegistryPropertyValue::AsDWORD() const {
    587   CHECK_EQ(property_type_, static_cast<DWORD>(REG_DWORD));
    588   DWORD* value = reinterpret_cast<DWORD*>(value_.get());
    589   return *value;
    590 }
    591 
    592 DevicePropertyValue::DevicePropertyValue(DEVPROPTYPE property_type,
    593                                          scoped_ptr<uint8_t[]> value,
    594                                          size_t value_size)
    595     : property_type_(property_type),
    596       value_(value.Pass()),
    597       value_size_(value_size) {
    598 }
    599 
    600 uint32_t DevicePropertyValue::AsUint32() const {
    601   CHECK_EQ(property_type_, static_cast<DEVPROPTYPE>(DEVPROP_TYPE_UINT32));
    602   CHECK_EQ(value_size_, sizeof(uint32_t));
    603   return *reinterpret_cast<uint32_t*>(value_.get());
    604 }
    605 
    606 BluetoothLowEnergyServiceInfo::BluetoothLowEnergyServiceInfo() {
    607 }
    608 
    609 BluetoothLowEnergyServiceInfo::~BluetoothLowEnergyServiceInfo() {
    610 }
    611 
    612 BluetoothLowEnergyDeviceInfo::BluetoothLowEnergyDeviceInfo()
    613     : visible(false), authenticated(false), connected(false) {
    614   address.ullLong = BLUETOOTH_NULL_ADDRESS;
    615 }
    616 
    617 BluetoothLowEnergyDeviceInfo::~BluetoothLowEnergyDeviceInfo() {
    618 }
    619 
    620 bool IsBluetoothLowEnergySupported() {
    621   return base::win::GetVersion() >= base::win::VERSION_WIN8;
    622 }
    623 
    624 bool EnumerateKnownBluetoothLowEnergyDevices(
    625     ScopedVector<BluetoothLowEnergyDeviceInfo>* devices,
    626     std::string* error) {
    627   if (!IsBluetoothLowEnergySupported()) {
    628     *error = kPlatformNotSupported;
    629     return false;
    630   }
    631 
    632   ScopedDeviceInfoSetHandle info_set_handle;
    633   HRESULT hr = OpenBluetoothLowEnergyDevices(&info_set_handle);
    634   if (FAILED(hr)) {
    635     *error = FormatBluetoothError(kDeviceEnumError, hr);
    636     return false;
    637   }
    638 
    639   for (DWORD i = 0;; ++i) {
    640     scoped_ptr<BluetoothLowEnergyDeviceInfo> device_info;
    641     DeviceInfoResult result = EnumerateSingleBluetoothLowEnergyDevice(
    642         info_set_handle, i, &device_info, error);
    643     switch (result) {
    644       case kNoMoreDevices:
    645         return true;
    646       case kError:
    647         return false;
    648       case kOk:
    649         devices->push_back(device_info.release());
    650     }
    651   }
    652 }
    653 
    654 bool EnumerateKnownBluetoothLowEnergyServices(
    655     const base::FilePath& device_path,
    656     ScopedVector<BluetoothLowEnergyServiceInfo>* services,
    657     std::string* error) {
    658   if (!IsBluetoothLowEnergySupported()) {
    659     *error = kPlatformNotSupported;
    660     return false;
    661   }
    662 
    663   return CollectBluetoothLowEnergyDeviceServices(device_path, services, error);
    664 }
    665 
    666 bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting(
    667     const std::string& instance_id,
    668     BLUETOOTH_ADDRESS* btha,
    669     std::string* error) {
    670   return ExtractBluetoothAddressFromDeviceInstanceId(instance_id, btha, error);
    671 }
    672 
    673 }  // namespace win
    674 }  // namespace device
    675