// Copyright 2020-2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "internal/platform/implementation/windows/bluetooth_adapter.h" // clang-format off #include // These two headers must be defined #include // first and in this order, otherwise // the MSVC compiler emits a warning // 'CTL_CODE undefined. Include winioctl.h or wdm.h' // See https://screenshot.googleplex.com/BYB9QnoHT6eP6oc // clang-format on #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "third_party/json/src/json.hpp" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" typedef std::basic_string tstring; // IOCTL to get local radio information #define BTH_GET_DEVICE_INFO_IOCTL 0x411008 #define REGISTRY_QUERY_FORMAT \ "SYSTEM\\ControlSet001\\Enum\\%s\\Device Parameters" #define BLUETOOTH_RADIO_REGISTRY_NAME_KEY "Local Name" namespace nearby { namespace windows { namespace { struct LocalSettings { std::string original_radio_name; std::string nearby_radio_name; }; using json = ::nlohmann::json; constexpr absl::string_view kLocalSettingsFileName = "settings_file.json"; constexpr char kOriginalRadioName[] = "OriginalRadioName"; constexpr char kNearbyRadioName[] = "NearbyRadioName"; void to_json(json &json_output, const LocalSettings &local_settings) { json_output = json{{kOriginalRadioName, local_settings.original_radio_name}, {kNearbyRadioName, local_settings.nearby_radio_name}}; } void from_json(const json &json_input, LocalSettings &local_settings) { json_input.at(kOriginalRadioName).get_to(local_settings.original_radio_name); json_input.at(kNearbyRadioName).get_to(local_settings.nearby_radio_name); } } // namespace constexpr uint8_t kAndroidDiscoverableBluetoothNameMaxLength = 37; // bytes BluetoothAdapter::BluetoothAdapter() : windows_bluetooth_adapter_(nullptr) { try { windows_bluetooth_adapter_ = winrt::Windows::Devices::Bluetooth::BluetoothAdapter::GetDefaultAsync() .get(); if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; } else { // Gets the radio represented by this Bluetooth adapter. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.getradioasync?view=winrt-20348 windows_bluetooth_radio_ = windows_bluetooth_adapter_.GetRadioAsync().get(); } } catch (const winrt::hresult_error &error) { LOG(ERROR) << __func__ << ": WinRT exception: " << error.code() << ": " << winrt::to_string(error.message()); } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; } } // Synchronously sets the status of the BluetoothAdapter to 'status', and // returns true if the operation was a success. bool BluetoothAdapter::SetStatus(Status status) { LOG(INFO) << __func__ << ": Set Bluetooth radio status to " << (status == Status::kEnabled ? "On" : "Off"); if (windows_bluetooth_radio_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth radio on this device."; return false; } auto radio_state = windows_bluetooth_radio_.State(); if (status == Status::kDisabled && (radio_state == RadioState::Unknown || radio_state == RadioState::Off || radio_state == RadioState::Disabled)) { VLOG(1) << __func__ << ": Skip set radio status kDisabled due to requested state is " "already kDisabled."; return true; } if (status == Status::kEnabled && radio_state == RadioState::On) { VLOG(1) << __func__ << ": Skip set radio status kEnabled due to requested state is " "already kEnabled."; return true; } if (!FeatureFlags::GetInstance().GetFlags().enable_set_radio_state) { VLOG(1) << __func__ << ": Attempt to set the radio state while " "FeatureFlags::enable_set_radio_state is false."; return false; } try { // An asynchronous operation that attempts to set the state of the radio // represented by this object. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio.setstateasync?view=winrt-20348 if (status == Status::kDisabled) { windows_bluetooth_radio_.SetStateAsync(RadioState::Off).get(); } else { windows_bluetooth_radio_.SetStateAsync(RadioState::On).get(); } } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": Failed to set Bluetooth radio state to " << (status == Status::kDisabled ? "kDisabled." : "kEnabled.") << "Exception: " << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } LOG(INFO) << __func__ << ": Successfully set the radio state to " << (status == Status::kDisabled ? "kDisabled." : "kEnabled."); return true; } // Returns true if the BluetoothAdapter's current status is // Status::Value::kEnabled. bool BluetoothAdapter::IsEnabled() const { if (windows_bluetooth_radio_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth radio on this device."; return false; } try { // Gets the current state of the radio represented by this object. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio.state?view=winrt-20348 return windows_bluetooth_radio_.State() == RadioState::On; } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); return false; } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } } // Returns true if the Bluetooth hardware supports Bluetooth 5.0 Extended // Advertising bool BluetoothAdapter::IsExtendedAdvertisingSupported() const { if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; return false; } try { // Indicates whether the adapter supports the 5.0 Extended Advertising // format. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.isextendedadvertisingsupported?view=winrt-22621 return windows_bluetooth_adapter_.IsExtendedAdvertisingSupported(); } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); return false; } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } } // Returns true if the Bluetooth hardware supports BLE Central Role bool BluetoothAdapter::IsCentralRoleSupported() const { if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; return false; } try { // Indicates whether the adapter supports the BLE Central Role // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.iscentralrolesupported?view=winrt-22621 return windows_bluetooth_adapter_.IsCentralRoleSupported(); } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); return false; } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } } // Returns true if the Bluetooth hardware supports BLE Peripheral Role bool BluetoothAdapter::IsPeripheralRoleSupported() const { if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; return false; } try { // Indicates whether the adapter supports the BLE Peripheral Role // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.isperipheralrolesupported?view=winrt-22621 return windows_bluetooth_adapter_.IsPeripheralRoleSupported(); } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); return false; } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } } // Returns true if the Bluetooth hardware supports BLE bool BluetoothAdapter::IsLowEnergySupported() const { if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; return false; } try { // Indicates whether the adapter supports BLE // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter.islowenergysupported?view=winrt-22621 return windows_bluetooth_adapter_.IsLowEnergySupported(); } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); return false; } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); return false; } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; return false; } } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode() // // Returns ScanMode::kUnknown on error. BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const { return scan_mode_; } // Synchronously sets the scan mode of the adapter, and returns true if the // operation was a success. bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) { scan_mode_ = scan_mode; if (scan_mode_changed_ != nullptr) { scan_mode_changed_(scan_mode); } return true; } void BluetoothAdapter::RestoreRadioNameIfNecessary() { try { std::string nearby_radio_name; std::string current_radio_name = GetName(); std::string settings_path(kLocalSettingsFileName); auto full_path = nearby::api::ImplementationPlatform::GetAppDataPath(settings_path); auto settings_file = nearby::api::ImplementationPlatform::CreateInputFile(full_path); if (settings_file == nullptr) { LOG(ERROR) << __func__ << ": Failed to create input file."; return; } auto total_size = settings_file->GetTotalSize(); if (total_size == 0) { LOG(WARNING) << __func__ << ": No data for local settings."; return; } nearby::ExceptionOr raw_local_settings; raw_local_settings = settings_file->Read(total_size); settings_file->Close(); if (!raw_local_settings.ok()) { LOG(ERROR) << __func__ << ": Failed to read data file."; return; } auto local_settings = json::parse(raw_local_settings.GetResult().data(), nullptr, false); if (local_settings.is_discarded()) { LOG(ERROR) << __func__ << ": Invalid local settings data."; return; } VLOG(1) << __func__ << ": loaded settings: " << local_settings.dump(); LocalSettings settings = local_settings.get(); if (current_radio_name == settings.nearby_radio_name) { SetName(settings.original_radio_name, /* persist= */ true); } } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; } } void BluetoothAdapter::StoreRadioNames(absl::string_view original_radio_name, absl::string_view nearby_radio_name) { try { if (original_radio_name.empty() || nearby_radio_name.empty()) { LOG(ERROR) << __func__ << ":Failed to save radio names due to invalid parameters."; return; } std::string settings_path(kLocalSettingsFileName); auto full_path = nearby::api::ImplementationPlatform::GetAppDataPath(settings_path); auto settings_file = nearby::api::ImplementationPlatform::CreateOutputFile(full_path); if (settings_file == nullptr) { LOG(ERROR) << __func__ << ": Failed to create output file."; return; } LocalSettings local_settings = {std::string(original_radio_name), std::string(nearby_radio_name)}; json encoded_local_settings; to_json(encoded_local_settings, local_settings); VLOG(1) << __func__ << ": saved settings: " << encoded_local_settings.dump(); settings_file->Write(encoded_local_settings.dump()); settings_file->Close(); } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; } } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error std::string BluetoothAdapter::GetName() const { if (device_name_.has_value()) { return *device_name_; } std::optional adapter_instance_id = GetGenericBluetoothAdapterInstanceID(); if (!adapter_instance_id.has_value()) { LOG(ERROR) << __func__ << ": Failed to get Generic Bluetooth Adapter InstanceID"; return std::string(); } std::string instance_id = *adapter_instance_id; // Change radio module local name in registry HKEY hKey; // Retrieve the size required char empty[0]; size_t registry_query_size = absl::SNPrintF(empty, // output 0, // size REGISTRY_QUERY_FORMAT, // format instance_id.c_str()); // args std::string local_name_key; local_name_key.reserve(registry_query_size + 1); absl::SNPrintF(local_name_key.data(), // output registry_query_size + 1, // size REGISTRY_QUERY_FORMAT, // format instance_id.c_str()); // args LSTATUS status; // Opens the specified registry key. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa status = RegOpenKeyExA( HKEY_LOCAL_MACHINE, // A handle to an open registry key, using // predefined key for local machine local_name_key.c_str(), // The name of the registry subkey to be opened. 0L, // Specifies the option to apply when opening the key. KEY_QUERY_VALUE, // A mask that specifies the desired access rights to // the key to be opened. &hKey); // A pointer to a variable that receives a handle to the opened // key std::string adapter_name; if (status == ERROR_SUCCESS) { adapter_name = GetNameFromRegistry(&hKey); } // Closes a handle to the specified registry key. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey RegCloseKey(hKey); // The local name is not in the registry, return the machine name if (adapter_name.empty()) { return GetNameFromComputerName(); } return adapter_name; } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) bool BluetoothAdapter::SetName(absl::string_view name) { return SetName(name, /* persist= */ true); } bool BluetoothAdapter::SetName(absl::string_view name, bool persist) { if (!persist) { StoreRadioNames(GetName(), name); } if (name.size() > 248 * sizeof(char)) { LOG(ERROR) << __func__ << ": Failed to set name for bluetooth adapter because " "the name exceeded the 248 bytes limit for Windows."; return false; } if (name.size() > kAndroidDiscoverableBluetoothNameMaxLength * sizeof(char)) { LOG(ERROR) << __func__ << ": Failed to set name for bluetooth adapter because " "Android cannot discover Windows bluetooth device " "name that exceeded the 37 bytes limit (11 " "characters in EndpointInfo)."; device_name_ = std::string(name); return true; } device_name_ = std::nullopt; if (registry_bluetooth_adapter_name_ == name) { VLOG(1) << __func__ << ": Tried to set name for bluetooth adapter to the " "same name again."; return true; } std::optional adapter_instance_id = GetGenericBluetoothAdapterInstanceID(); if (!adapter_instance_id.has_value()) { LOG(ERROR) << __func__ << ": Failed to get Generic Bluetooth Adapter InstanceID"; return false; } std::string instance_id = *adapter_instance_id; // defined in usbiodef.h const GUID guid = GUID_DEVINTERFACE_USB_DEVICE; // Conversion of a 16 byte guid to a format of // "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" results in // a required buffer of // (32 digits + 4 '-'s + 2 braces + 1 '\0') * sizeof(OLECHAR) for // a total of 39 OLECHARs. #define OLE_GUID_STRING_BUFFER_SIZE 39 OLECHAR guid_ole_str[OLE_GUID_STRING_BUFFER_SIZE]; size_t guid_ole_str_size = OLE_GUID_STRING_BUFFER_SIZE; // Converts a globally unique identifier (GUID) into a string of printable // characters. // https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-stringfromguid2 auto conversionResult = StringFromGUID2(guid, guid_ole_str, guid_ole_str_size); if (conversionResult == 0) { LOG(ERROR) << __func__ << ": Failed to convert guid to string"; return false; } std::string guid_str; int guid_str_size = 0; BOOL defaultCharUsed; guid_str_size = WideCharToMultiByte( CP_UTF8, // Code page to use in performing the conversion. 0, // Flags indicating the conversion type. guid_ole_str, // Pointer to the Unicode string to convert. guid_ole_str_size, // Size, in characters, of the string indicated by // lpWideCharStr. nullptr, // Pointer to a buffer that receives the converted string. 0, // Size, in bytes, of the buffer indicated by lpMultiByteStr. NULL, // Pointer to the character to use if a character cannot be // represented in the specified code page. &defaultCharUsed); // Pointer to a flag that indicates if the function // has used a default character in the conversion. if (guid_str_size == 0) { process_error(); return false; } guid_str.reserve(guid_str_size); // Maps a UTF-16 (wide character) string to a new character string. The new // character string is not necessarily from a multibyte character set. // https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte conversionResult = WideCharToMultiByte( CP_UTF8, // Code page to use in performing the conversion. 0, // Flags indicating the conversion type. guid_ole_str, // Pointer to the Unicode string to convert. guid_ole_str_size, // Size, in characters, of the string indicated by // lpWideCharStr. guid_str .data(), // Pointer to a buffer that receives the converted string. guid_str_size, // Size, in bytes, of the buffer indicated by // lpMultiByteStr. NULL, // // Pointer to the character to use if a character cannot be // represented in the specified code page. &defaultCharUsed); // // Pointer to a flag that indicates if the // function has used a default character in the // conversion. if (conversionResult == 0) { process_error(); return false; } // Add 1 to length to get size (including null) std::string instance_id_modified; instance_id_modified.reserve((instance_id.size() + 1) * sizeof(char)); absl::SNPrintF(instance_id_modified.data(), size_t((instance_id.size() + 1) * sizeof(char)), "%s", instance_id.c_str()); // Convert the P&P instance id, to one that CreateFileA expects find_and_replace(instance_id_modified.data(), "\\", "#"); char empty[0]; size_t file_name_size = absl::SNPrintF( empty, 0, "\\\\.\\%s#%s", instance_id_modified.c_str(), guid_str.c_str()); std::string file_name; file_name.reserve(file_name_size + 1); // Add the guid to the P&P instance id forming the complete symbolic // path to the device. absl::SNPrintF(file_name.data(), file_name_size + 1, "\\\\.\\%s#%s", instance_id_modified.c_str(), guid_str.c_str()); HANDLE hDevice; // Creates or opens a file or I/O device. // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea hDevice = CreateFileA( file_name.c_str(), // The name of the file or device to be created or // opened. GENERIC_WRITE, // The requested access to the file or device. 0, // The requested sharing mode of the file or device. NULL, // A pointer to a SECURITY_ATTRIBUTES structure. OPEN_EXISTING, // An action to take on a file or device that exists or // does not exist. 0, // The file or device attributes and flags. NULL); // A valid handle to a template file with the GENERIC_READ // access right. This parameter can be NULL. if (hDevice == INVALID_HANDLE_VALUE) { LOG(ERROR) << __func__ << ": Failed to open device. Error code: " << GetLastError(); return false; } // Change radio module local name in registry HKEY hKey; size_t buffer_size = absl::SNPrintF(empty, 0, REGISTRY_QUERY_FORMAT, instance_id); std::string local_name_key; local_name_key.reserve(buffer_size + 1); absl::SNPrintF(local_name_key.data(), buffer_size + 1, REGISTRY_QUERY_FORMAT, instance_id.c_str()); LSTATUS status; // Opens the specified registry key. Note that key names are not case // sensitive. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa status = RegOpenKeyExA( HKEY_LOCAL_MACHINE, // A handle to an open registry key. local_name_key.c_str(), // The name of the registry subkey to be opened. 0L, // Specifies the option to apply when opening the key. KEY_SET_VALUE, // A mask that specifies the desired access rights to // the key to be opened. &hKey); // A pointer to a variable that receives a handle to the opened // key. if (status != ERROR_SUCCESS) { LOG(ERROR) << __func__ << ": Failed to open registry key. Error code: " << status; return false; } if (name != "") { // Sets the data and type of a specified value under a registry key. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexa status = RegSetValueExA( hKey, // A handle to an open registry key BLUETOOTH_RADIO_REGISTRY_NAME_KEY, // The name of the value to be // set. 0, // This parameter is reserved and must be zero. REG_BINARY, // The type of data pointed to by the lpData parameter. (LPBYTE)std::string(name).c_str(), // The data to be stored. strlen(std::string(name) .c_str())); // The size of the information pointed // to by the lpData parameter, in bytes. } else { // If we are told to set the key to "", we treat this as a reset // If we delete the key value the OS will default to the system // name. If we just set it to blank, it will show as blank in all // system dialogs, this is likely undesireable status = RegDeleteValueA(hKey, BLUETOOTH_RADIO_REGISTRY_NAME_KEY); } if (status != ERROR_SUCCESS) { LOG(ERROR) << __func__ << ": Failed to set/delete registry key. Error code: " << status; return false; } // Closes a handle to the specified registry key. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey RegCloseKey(hKey); // tells the control function to reset or reload or similar... int32_t reload = 4; // merely a placeholder DWORD bytes = 0; // Send radio module driver command to update device information // Sends a control code directly to a specified device driver, causing the // corresponding device to perform the corresponding operation. // https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol if (!DeviceIoControl( hDevice, // A handle to the device on which the operation is to be // performed. BTH_GET_DEVICE_INFO_IOCTL, // The control code for the operation. &reload, // A pointer to the input buffer that contains the data // required to perform the operation. sizeof(reload), // The size of the input buffer, in bytes. NULL, // A pointer to the output buffer that is to receive the data // returned by the operation. 0, // The size of the output buffer, in bytes. &bytes, // A pointer to a variable that receives the size of the // data stored in the output buffer, in bytes. NULL)) { // A pointer to an OVERLAPPED structure. LOG(ERROR) << __func__ << ": Failed to update radio module local name. Error code: " << GetLastError(); return false; } registry_bluetooth_adapter_name_ = std::string(name); return true; } void BluetoothAdapter::process_error() { const char *errorResult = {}; int errorMessageID = GetLastError(); switch (errorMessageID) { case ERROR_INSUFFICIENT_BUFFER: errorResult = "A supplied buffer size was not large enough, or it was " "incorrectly set to NULL."; break; case ERROR_INVALID_FLAGS: errorResult = "The values supplied for flags were not valid."; break; case ERROR_INVALID_PARAMETER: errorResult = "Any of the parameter values was invalid."; break; case ERROR_NO_UNICODE_TRANSLATION: errorResult = "Invalid Unicode was found in a string."; break; default: errorResult = "Unknown error."; break; } LOG(ERROR) << __func__ << ": Failed to convert guid to string " << errorResult << " Error code:" << errorMessageID; } void BluetoothAdapter::find_and_replace(char *source, const char *strFind, const char *strReplace) const { std::string s = std::string(source); std::string f = std::string(strFind); std::string r = std::string(strReplace); size_t j; for (; (j = s.find(f)) != std::string::npos;) { s.replace(j, f.length(), r); } memcpy(source, s.c_str(), s.size()); } std::optional BluetoothAdapter::GetGenericBluetoothAdapterInstanceID() const { unsigned i; CONFIGRET r; HDEVINFO hDevInfo; SP_DEVINFO_DATA DeviceInfoData; char *deviceInstanceID = new char[MAX_DEVICE_ID_LEN]; // Find all bluetooth radio modules // The SetupDiGetClassDevs function returns a handle to a device information // set that contains requested device information elements for a local // computer. // https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa hDevInfo = SetupDiGetClassDevsA(&GUID_DEVCLASS_BLUETOOTH, NULL, NULL, DIGCF_PRESENT); if (hDevInfo == INVALID_HANDLE_VALUE) { LOG(ERROR) << __func__ << ": Could not find BluetoothDevice on this machine"; return std::nullopt; } // Get first Generic Bluetooth Adapter InstanceID for (i = 0;; i++) { DeviceInfoData.cbSize = sizeof(DeviceInfoData); // The SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA structure // that specifies a device information element in a device information // set. // https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinfo if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData)) break; // The CM_Get_Device_ID function retrieves the device instance ID for a // specified device instance on the local machine. // https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_get_device_ida r = CM_Get_Device_IDA(DeviceInfoData.DevInst, deviceInstanceID, MAX_DEVICE_ID_LEN, 0); if (r != CR_SUCCESS) continue; // With Windows, a Bluetooth radio can be packaged as an external dongle // or embedded inside a computer but it must be connected to one of the // computer's USB ports. // https://docs.microsoft.com/en-us/windows-hardware/drivers/bluetooth/bluetooth-host-radio-support if (strncmp("USB", deviceInstanceID, 3) == 0) { SetupDiDestroyDeviceInfoList(hDevInfo); return std::string(deviceInstanceID); } } LOG(ERROR) << __func__ << ": Failed to get the generic bluetooth adapter id"; SetupDiDestroyDeviceInfoList(hDevInfo); return std::nullopt; } // Returns BT MAC address assigned to this adapter. MacAddress BluetoothAdapter::GetMacAddress() const { if (windows_bluetooth_adapter_ == nullptr) { LOG(ERROR) << __func__ << ": No Bluetooth adapter on this device."; return MacAddress(); } MacAddress mac_address; try { MacAddress::FromUint64( windows_bluetooth_adapter_.BluetoothAddress(), mac_address); } catch (std::exception exception) { LOG(ERROR) << __func__ << ": exception:" << exception.what(); } catch (const winrt::hresult_error &ex) { LOG(ERROR) << __func__ << ": exception:" << ex.code() << ": " << winrt::to_string(ex.message()); } catch (...) { LOG(ERROR) << __func__ << ": unknown error."; } return mac_address; } std::string BluetoothAdapter::GetNameFromRegistry(PHKEY hKey) const { DWORD local_name_size = 0; DWORD value_type; // Retrieves the size of the data for the specified value name associated // with an open registry key. // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexa LSTATUS status = RegQueryValueExA( *hKey, // A handle to an open registry key. BLUETOOTH_RADIO_REGISTRY_NAME_KEY, // The name of the registry // value. nullptr, // This parameter is reserved and must be NULL. &value_type, // A pointer to a variable that receives a code // indicating the type of data stored in the // specified value. nullptr, // null tells the function to just get the buffer size. // value's data. &local_name_size); // A pointer to a variable that specifies the // size of the buffer pointed to by the lpData // parameter, in bytes. if (status != ERROR_SUCCESS) { LOG(ERROR) << __func__ << ": Failed to get the required size of the local name buffer: " << status; return ""; } unsigned char *local_name = new unsigned char[local_name_size]; memset(local_name, '\0', local_name_size); status = RegQueryValueExA( *hKey, // A handle to an open registry key. BLUETOOTH_RADIO_REGISTRY_NAME_KEY, // The name of the registry // value. nullptr, // This parameter is reserved and must be NULL. &value_type, // A pointer to a variable that receives a code // indicating the type of data stored in the // specified value. local_name, // A pointer to a buffer that // receives the value's data. &local_name_size); // A pointer to a variable that specifies the // size of the buffer pointed to by the lpData // parameter, in bytes. if (status == ERROR_SUCCESS) { std::string local_name_return = std::string( local_name, local_name + local_name_size / sizeof local_name[0]); delete[] local_name; return local_name_return; } delete[] local_name; return ""; } std::string BluetoothAdapter::GetNameFromComputerName() const { DWORD size = MAX_COMPUTERNAME_LENGTH + 1; CHAR computer_name[MAX_COMPUTERNAME_LENGTH + 1]; if (GetComputerNameA(computer_name, &size)) { return std::string(computer_name); } LOG(ERROR) << __func__ << ": Failed to get any computer name"; return ""; } } // namespace windows } // namespace nearby