Files
nearby/internal/platform/implementation/windows/bluetooth_classic_medium.cc
T
Janusz Sobczak 974d4b357c Skip SDP check for FP rfcomm connection on Windows
The SDP checks fails but rfcomm connection works.
Skipping the SDP check does not seem to have any ill side effects.

PiperOrigin-RevId: 551370531
2023-07-26 18:15:44 -07:00

899 lines
32 KiB
C++

// Copyright 2020 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_classic_medium.h"
#include <stdio.h>
#include <windows.h>
#include <codecvt>
#include <fstream>
#include <functional>
#include <ios>
#include <locale>
#include <map>
#include <memory>
#include <regex> // NOLINT
#include <string>
#include <utility>
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
#include "internal/platform/implementation/windows/bluetooth_classic_device.h"
#include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h"
#include "internal/platform/implementation/windows/bluetooth_classic_socket.h"
#include "internal/platform/implementation/windows/bluetooth_pairing.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "internal/platform/implementation/windows/generated/winrt/base.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi_lan.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace windows {
namespace {
using winrt::Windows::Foundation::IInspectable;
using winrt::Windows::Foundation::Collections::IMapView;
// Used to cntrol the dump output for device information. It is only for debug
// purpose.
constexpr bool kEnableDumpDeviceInfomation = false;
void DumpDeviceInformation(
const IMapView<winrt::hstring, IInspectable>& properties) {
if (!kEnableDumpDeviceInfomation) {
return;
}
if (properties == nullptr) {
return;
}
for (const auto& property : properties) {
if (property.Key() == L"System.ItemNameDisplay") {
NEARBY_LOGS(INFO) << "System.ItemNameDisplay: "
<< InspectableReader::ReadString(property.Value());
} else if (property.Key() == L"System.Devices.Aep.CanPair") {
NEARBY_LOGS(INFO) << "System.Devices.Aep.CanPair: "
<< InspectableReader::ReadBoolean(property.Value());
} else if (property.Key() == L"System.Devices.Aep.IsPaired") {
NEARBY_LOGS(INFO) << "System.Devices.Aep.IsPaired: "
<< InspectableReader::ReadBoolean(property.Value());
} else if (property.Key() == L"System.Devices.Aep.IsPresent") {
NEARBY_LOGS(INFO) << "System.Devices.Aep.IsPresent: "
<< InspectableReader::ReadBoolean(property.Value());
} else if (property.Key() == L"System.Devices.Aep.DeviceAddress") {
NEARBY_LOGS(INFO) << "System.Devices.Aep.DeviceAddress: "
<< InspectableReader::ReadString(property.Value());
}
}
}
} // namespace
constexpr uint8_t kAndroidDiscoverableBluetoothNameMaxLength = 37; // bytes
BluetoothClassicMedium::BluetoothClassicMedium(
api::BluetoothAdapter& bluetoothAdapter)
: bluetooth_adapter_(dynamic_cast<BluetoothAdapter&>(bluetoothAdapter)) {
InitializeDeviceWatcher();
bluetooth_adapter_.RestoreRadioNameIfNecessary();
bluetooth_adapter_.SetOnScanModeChanged(std::bind(
&BluetoothClassicMedium::OnScanModeChanged, this, std::placeholders::_1));
}
BluetoothClassicMedium::~BluetoothClassicMedium() {}
void BluetoothClassicMedium::OnScanModeChanged(
BluetoothAdapter::ScanMode scanMode) {
NEARBY_LOGS(INFO) << __func__
<< ": OnScanModeChanged is called with scanMode: "
<< static_cast<int>(scanMode);
if (scanMode == scan_mode_) {
NEARBY_LOGS(INFO) << __func__ << ": No change of scan mode.";
return;
}
scan_mode_ = scanMode;
bool radio_discoverable =
scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable;
if (bluetooth_adapter_.GetName().size() >
kAndroidDiscoverableBluetoothNameMaxLength) {
// If the name longer than the android limitation, always set the value to
// false.
radio_discoverable = false;
}
if (is_radio_discoverable_ == radio_discoverable) {
NEARBY_LOGS(INFO) << __func__ << ": No change of radio discovery.";
return;
}
if (rfcomm_provider_ == nullptr) {
NEARBY_LOGS(INFO) << __func__ << ": No advertising.";
return;
}
try {
rfcomm_provider_.StopAdvertising();
rfcomm_provider_.StartAdvertising(
raw_server_socket_->stream_socket_listener(), radio_discoverable);
is_radio_discoverable_ = radio_discoverable;
return;
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__
<< ": OnScanModeChanged exception: " << exception.what();
return;
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": OnScanModeChanged exception: " << ex.code() << ": "
<< winrt::to_string(ex.message());
return;
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
return;
}
}
bool BluetoothClassicMedium::StartDiscovery(
BluetoothClassicMedium::DiscoveryCallback discovery_callback) {
NEARBY_LOGS(INFO) << "StartDiscovery is called.";
bool result = false;
discovery_callback_ = std::move(discovery_callback);
if (!IsWatcherStarted()) {
result = StartScanning();
}
return result;
}
bool BluetoothClassicMedium::StopDiscovery() {
NEARBY_LOGS(INFO) << "StopDiscovery is called.";
bool result = false;
if (IsWatcherStarted()) {
result = StopScanning();
}
return result;
}
void BluetoothClassicMedium::InitializeDeviceWatcher() {
try {
// create watcher
const winrt::param::iterable<winrt::hstring> RequestedProperties =
winrt::single_threaded_vector<winrt::hstring>(
{winrt::to_hstring("System.Devices.Aep.IsPresent"),
winrt::to_hstring("System.Devices.Aep.DeviceAddress")});
device_watcher_ = DeviceInformation::CreateWatcher(
BLUETOOTH_SELECTOR, // aqsFilter
RequestedProperties, // additionalProperties
DeviceInformationKind::AssociationEndpoint); // kind
// An app must subscribe to all of the added, removed, and updated events
// to be notified when there are device additions, removals or updates. If
// an app handles only the added event, it will not receive an update if a
// device is added to the system after the initial device enumeration
// completes. register event handlers before starting the watcher
// Event that is raised when a device is added to the collection enumerated
// by the DeviceWatcher.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.added?view=winrt-20348
device_watcher_.Added({this, &BluetoothClassicMedium::DeviceWatcher_Added});
// Event that is raised when a device is updated in the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.updated?view=winrt-20348
device_watcher_.Updated(
{this, &BluetoothClassicMedium::DeviceWatcher_Updated});
// Event that is raised when a device is removed from the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.removed?view=winrt-20348
device_watcher_.Removed(
{this, &BluetoothClassicMedium::DeviceWatcher_Removed});
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__ << ": InitializeDeviceWatcher exception: "
<< exception.what();
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": InitializeDeviceWatcher exception: " << ex.code()
<< ": " << winrt::to_string(ex.message());
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
}
}
std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
api::BluetoothDevice& remote_device, const std::string& service_uuid,
CancellationFlag* cancellation_flag) {
try {
NEARBY_LOGS(INFO) << "ConnectToService is called.";
if (service_uuid.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service_uuid not specified.";
return nullptr;
}
const std::regex pattern(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-"
"F]"
"{12}$");
// Must check for valid pattern as the guid constructor will throw on an
// invalid format
if (!std::regex_match(service_uuid, pattern)) {
NEARBY_LOGS(ERROR) << __func__
<< ": invalid service_uuid: " << service_uuid;
return nullptr;
}
winrt::guid service(service_uuid);
if (cancellation_flag == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": cancellation_flag not specified.";
return nullptr;
}
remote_device_to_connect_ =
std::make_unique<BluetoothDevice>(remote_device.GetMacAddress());
// First try, check if the remote device that we want to request connection
// to has already been discovered by the Bluetooth Classic Device Watcher
// beforehand inside the discovered_devices_by_id_ map
std::map<winrt::hstring, std::unique_ptr<BluetoothDevice>>::const_iterator
it = discovered_devices_by_id_.find(
winrt::to_hstring(remote_device_to_connect_->GetId()));
std::unique_ptr<BluetoothDevice> device = nullptr;
BluetoothDevice* current_device = nullptr;
if (it != discovered_devices_by_id_.end()) {
current_device = it->second.get();
} else {
// The remote device was not discovered by the Bluetooth Classic Device
// Watcher beforehand.
// Second try, request Windows to scan for nearby
// bluetooth devices that has this static mac address again in this
// instance
auto remote_bluetooth_device_from_mac_address =
winrt::Windows::Devices::Bluetooth::BluetoothDevice::
FromBluetoothAddressAsync(
mac_address_string_to_uint64(remote_device.GetMacAddress()))
.get();
if (remote_bluetooth_device_from_mac_address == nullptr) {
NEARBY_LOGS(ERROR) << __func__
<< ": Windows failed to get remote bluetooth device "
"from static mac address.";
return nullptr;
}
device = std::make_unique<BluetoothDevice>(
remote_bluetooth_device_from_mac_address);
current_device = device.get();
}
if (current_device == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get current device.";
return nullptr;
}
winrt::hstring device_id = winrt::to_hstring(current_device->GetId());
if (!HaveAccess(device_id)) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to gain access to device: "
<< winrt::to_string(device_id);
return nullptr;
}
RfcommDeviceService requested_service(
GetRequestedService(current_device, service));
if (!FeatureFlags::GetInstance()
.GetFlags()
.skip_service_discovery_before_connecting_to_rfcomm &&
!CheckSdp(requested_service)) {
NEARBY_LOGS(ERROR) << __func__ << ": Invalid SDP.";
return nullptr;
}
auto rfcomm_socket = std::make_unique<BluetoothSocket>();
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(INFO)
<< __func__
<< ": Bluetooth Classic socket connection cancelled for device: "
<< winrt::to_string(device_id) << ", service: " << service_uuid;
return nullptr;
}
nearby::CancellationFlagListener cancellation_flag_listener(
cancellation_flag, [&rfcomm_socket]() { rfcomm_socket->Close(); });
bool success =
rfcomm_socket->Connect(requested_service.ConnectionHostName(),
requested_service.ConnectionServiceName());
if (!success) {
return nullptr;
}
return std::move(rfcomm_socket);
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
NEARBY_LOGS(ERROR) << __func__ << ": Exception connecting bluetooth async: "
<< exception.what();
return nullptr;
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": Exception connecting bluetooth async, error code: "
<< ex.code()
<< ", error message: " << winrt::to_string(ex.message());
return nullptr;
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
return nullptr;
}
}
std::unique_ptr<api::BluetoothPairing> BluetoothClassicMedium::CreatePairing(
api::BluetoothDevice& remote_device) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Start to createPairing with device: "
<< remote_device.GetMacAddress();
try {
winrt::Windows::Devices::Bluetooth::BluetoothDevice bluetooth_device =
winrt::Windows::Devices::Bluetooth::BluetoothDevice::
FromBluetoothAddressAsync(
mac_address_string_to_uint64(remote_device.GetMacAddress()))
.get();
winrt::Windows::Devices::Enumeration::DeviceInformationCustomPairing
custom_pairing =
bluetooth_device.DeviceInformation().Pairing().Custom();
if (custom_pairing) {
return std::make_unique<BluetoothPairing>(bluetooth_device,
custom_pairing);
}
NEARBY_LOGS(VERBOSE) << __func__
<< ": Failed to get DeviceInformationCustomPairing.";
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__ << " : Failed to create pairing. exception: "
<< exception.what();
} catch (const winrt::hresult_error& error) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to create pairing. WinRT exception: "
<< error.code() << ": "
<< winrt::to_string(error.message());
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
}
return nullptr;
}
bool BluetoothClassicMedium::HaveAccess(winrt::hstring device_id) {
if (device_id.empty()) {
return false;
}
DeviceAccessInformation access_information =
DeviceAccessInformation::CreateFromId(device_id);
if (access_information == nullptr) {
return false;
}
DeviceAccessStatus access_status = access_information.CurrentStatus();
if (access_status == DeviceAccessStatus::DeniedByUser ||
// This status is most likely caused by app permissions (did not declare
// the device in the app's package.appxmanifest)
// This status does not cover the case where the device is already
// opened by another app.
access_status == DeviceAccessStatus::DeniedBySystem ||
// Most likely the device is opened by another app, but cannot be sure
access_status == DeviceAccessStatus::Unspecified) {
return false;
}
return true;
}
RfcommDeviceService BluetoothClassicMedium::GetRequestedService(
BluetoothDevice* device, winrt::guid service) {
RfcommServiceId rfcommServiceId = RfcommServiceId::FromUuid(service);
return device->GetRfcommServiceForIdAsync(rfcommServiceId);
}
bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) {
// Do various checks of the SDP record to make sure you are talking to a
// device that actually supports the Bluetooth Rfcomm Service
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice.getsdprawattributesasync?view=winrt-20348
try {
if (requestedService == nullptr) {
return false;
}
auto attributes = requestedService.GetSdpRawAttributesAsync().get();
if (!attributes.HasKey(Constants::SdpServiceNameAttributeId)) {
NEARBY_LOGS(ERROR) << __func__ << ": Missing SdpServiceNameAttributeId.";
return false;
}
auto attributeReader = DataReader::FromBuffer(
attributes.Lookup(Constants::SdpServiceNameAttributeId));
auto attributeType = attributeReader.ReadByte();
if (attributeType != Constants::SdpServiceNameAttributeType) {
NEARBY_LOGS(ERROR) << __func__
<< ": Missing SdpServiceNameAttributeType.";
return false;
}
return true;
} catch (...) {
NEARBY_LOGS(ERROR) << "Failed to get SDP information.";
return false;
}
}
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord
//
// service_uuid is the canonical textual representation
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
// type 3 name-based
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
// UUID.
//
// Returns nullptr error.
std::unique_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
NEARBY_LOGS(INFO) << "ListenForService is called with service name: "
<< service_name << ".";
if (service_uuid.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service_uuid was empty.";
return nullptr;
}
if (service_name.empty()) {
NEARBY_LOGS(ERROR) << __func__ << ": service_name was empty.";
return nullptr;
}
service_name_ = service_name;
service_uuid_ = service_uuid;
scan_mode_ = bluetooth_adapter_.GetScanMode();
NEARBY_LOGS(INFO) << __func__
<< ": scan_mode: " << static_cast<int>(scan_mode_);
bool radio_discoverable =
scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable;
bool result = StartAdvertising(radio_discoverable);
if (!result) {
NEARBY_LOGS(ERROR) << __func__ << ": Failed to start listening.";
return nullptr;
}
return std::move(server_socket_);
}
api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
const std::string& mac_address) {
return new BluetoothDevice(mac_address);
}
bool BluetoothClassicMedium::StartScanning() {
if (!IsWatcherStarted()) {
if (device_watcher_ == nullptr) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Failed to start scanning due to no available watcher.";
return false;
}
discovered_devices_by_id_.clear();
// The Start method can only be called when the DeviceWatcher is in the
// Created, Stopped or Aborted state.
auto status = device_watcher_.Status();
if (status == DeviceWatcherStatus::Created ||
status == DeviceWatcherStatus::Stopped ||
status == DeviceWatcherStatus::Aborted) {
device_watcher_.Start();
return true;
}
}
NEARBY_LOGS(ERROR)
<< __func__
<< ": Attempted to start scanning when watcher already started.";
return false;
}
bool BluetoothClassicMedium::StopScanning() {
if (IsWatcherRunning()) {
device_watcher_.Stop();
return true;
}
NEARBY_LOGS(ERROR)
<< __func__
<< ": Attempted to stop scanning when watcher already stopped.";
return false;
}
winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added(
DeviceWatcher sender, DeviceInformation deviceInfo) {
NEARBY_LOGS(INFO) << "Device added " << winrt::to_string(deviceInfo.Id());
IMapView<winrt::hstring, IInspectable> properties = deviceInfo.Properties();
DumpDeviceInformation(properties);
if (!IsWatcherStarted()) {
return winrt::fire_and_forget();
}
// If device no item name, ignore it.
if (!properties.HasKey(L"System.ItemNameDisplay")) {
NEARBY_LOGS(WARNING) << __func__ << ": Ignore the Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " due to no name.";
return winrt::fire_and_forget();
}
if (properties.Lookup(L"System.ItemNameDisplay") == nullptr) {
NEARBY_LOGS(WARNING) << __func__ << ": Ignore the Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " due to empty name.";
return winrt::fire_and_forget();
}
// If device doesn't support pair, ignore it.
if (!properties.HasKey(L"System.Devices.Aep.CanPair")) {
NEARBY_LOGS(WARNING) << __func__ << ": Ignore the Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " due to no pair property.";
return winrt::fire_and_forget();
}
if (!InspectableReader::ReadBoolean(
properties.Lookup(L"System.Devices.Aep.CanPair"))) {
NEARBY_LOGS(WARNING) << __func__ << ": Ignore the Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " due to not support pair.";
return winrt::fire_and_forget();
}
// Create an iterator for the internal list
std::map<winrt::hstring, std::unique_ptr<BluetoothDevice>>::const_iterator
it = discovered_devices_by_id_.find(deviceInfo.Id());
// Add to our internal list if necessary
if (it != discovered_devices_by_id_.end()) {
// We're already tracking this one
NEARBY_LOGS(WARNING) << __func__ << ": Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " is alreay added.";
return winrt::fire_and_forget();
}
// Create a bluetooth device out of this id
auto bluetoothDevice =
winrt::Windows::Devices::Bluetooth::BluetoothDevice::FromIdAsync(
deviceInfo.Id())
.get();
auto bluetoothDeviceP = std::make_unique<BluetoothDevice>(bluetoothDevice);
discovered_devices_by_id_[deviceInfo.Id()] = std::move(bluetoothDeviceP);
NEARBY_LOGS(INFO) << __func__ << ": Notifying bluetooth device added";
if (discovery_callback_.device_discovered_cb != nullptr) {
discovery_callback_.device_discovered_cb(
*discovered_devices_by_id_[deviceInfo.Id()]);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceAdded(*discovered_devices_by_id_[deviceInfo.Id()]);
}
return winrt::fire_and_forget();
}
winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated(
DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
auto it = discovered_devices_by_id_.find(deviceInfoUpdate.Id());
if (it == discovered_devices_by_id_.end()) {
NEARBY_LOGS(WARNING) << __func__ << ": Bluetooth device "
<< winrt::to_string(deviceInfoUpdate.Id())
<< " is not in list.";
return winrt::fire_and_forget();
}
NEARBY_LOGS(INFO)
<< "Device updated name: "
<< discovered_devices_by_id_[deviceInfoUpdate.Id()]->GetName() << " ("
<< winrt::to_string(deviceInfoUpdate.Id()) << ")";
IMapView<winrt::hstring, IInspectable> properties =
deviceInfoUpdate.Properties();
DumpDeviceInformation(properties);
if (!IsWatcherStarted()) {
// Spurious call, watcher has stopped or wasn't started
return winrt::fire_and_forget();
}
// https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/device-information-properties#associationendpoint-properties
if (properties.HasKey(L"System.ItemNameDisplay")) {
// we need to really change the name of the bluetooth device
std::string new_device_name = InspectableReader::ReadString(
properties.Lookup(L"System.ItemNameDisplay"));
if (it->second->GetName() == new_device_name) {
NEARBY_LOGS(INFO)
<< "Device name is same as old name, ignore the update.";
} else {
it->second->SetName(new_device_name);
NEARBY_LOGS(INFO)
<< "Updated device name:"
<< discovered_devices_by_id_[deviceInfoUpdate.Id()]->GetName();
discovery_callback_.device_name_changed_cb(
*discovered_devices_by_id_[deviceInfoUpdate.Id()]);
}
}
// Indicates if the device is currently paired.
if (properties.HasKey(L"System.Devices.Aep.IsPaired")) {
bool new_paired_status = InspectableReader::ReadBoolean(
properties.Lookup(L"System.Devices.Aep.IsPaired"));
NEARBY_LOGS(INFO) << __func__
<< ": Notifying device paired changed: " << std::boolalpha
<< new_paired_status;
for (auto& observer : observers_.GetObservers()) {
observer->DevicePairedChanged(
*discovered_devices_by_id_[deviceInfoUpdate.Id()], new_paired_status);
}
}
return winrt::fire_and_forget();
}
winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed(
DeviceWatcher sender, DeviceInformationUpdate deviceInfo) {
auto it = discovered_devices_by_id_.find(deviceInfo.Id());
if (it == discovered_devices_by_id_.end()) {
NEARBY_LOGS(WARNING) << __func__ << ": Bluetooth device "
<< winrt::to_string(deviceInfo.Id())
<< " is not in list.";
return winrt::fire_and_forget();
}
NEARBY_LOGS(INFO) << "Device removed "
<< discovered_devices_by_id_[deviceInfo.Id()]->GetName()
<< " (" << winrt::to_string(deviceInfo.Id()) << ")";
if (!IsWatcherStarted()) {
return winrt::fire_and_forget();
}
NEARBY_LOGS(INFO) << __func__ << ": Notifying bluetooth device removed";
if (discovery_callback_.device_lost_cb != nullptr) {
discovery_callback_.device_lost_cb(
*discovered_devices_by_id_[deviceInfo.Id()]);
}
for (auto& observer : observers_.GetObservers()) {
observer->DeviceRemoved(*discovered_devices_by_id_[deviceInfo.Id()]);
}
discovered_devices_by_id_.erase(deviceInfo.Id());
return winrt::fire_and_forget();
}
bool BluetoothClassicMedium::IsWatcherStarted() {
if (device_watcher_ == nullptr) {
return false;
}
DeviceWatcherStatus status = device_watcher_.Status();
return (status == DeviceWatcherStatus::Started) ||
(status == DeviceWatcherStatus::EnumerationCompleted);
}
bool BluetoothClassicMedium::IsWatcherRunning() {
if (device_watcher_ == nullptr) {
return false;
}
DeviceWatcherStatus status = device_watcher_.Status();
return (status == DeviceWatcherStatus::Started) ||
(status == DeviceWatcherStatus::EnumerationCompleted) ||
(status == DeviceWatcherStatus::Stopping);
}
bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) {
NEARBY_LOGS(INFO) << __func__
<< ": StartAdvertising is called with radio_discoverable: "
<< radio_discoverable << ".";
try {
if (rfcomm_provider_ != nullptr &&
is_radio_discoverable_ == radio_discoverable) {
NEARBY_LOGS(WARNING) << __func__
<< ": Ignore StartAdvertising due to no change to "
"current advertising.";
return true;
}
if (rfcomm_provider_ != nullptr && !StopAdvertising()) {
NEARBY_LOGS(WARNING) << __func__
<< ": Failed to StartAdvertising due to cannot stop "
"running advertising.";
return false;
}
rfcomm_provider_ =
RfcommServiceProvider::CreateAsync(
RfcommServiceId::FromUuid(winrt::guid(service_uuid_)))
.get();
server_socket_ = std::make_unique<BluetoothServerSocket>(
winrt::to_string(rfcomm_provider_.ServiceId().AsString()));
raw_server_socket_ = server_socket_.get();
if (!server_socket_->listen()) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Failed to StartAdvertising due to cannot start socket.";
server_socket_->Close();
server_socket_ = nullptr;
rfcomm_provider_ = nullptr;
return false;
}
server_socket_->SetCloseNotifier([&]() { StopAdvertising(); });
// Set the SDP attributes and start Bluetooth advertising
InitializeServiceSdpAttributes(rfcomm_provider_, service_name_);
// Start to advertising.
rfcomm_provider_.StartAdvertising(server_socket_->stream_socket_listener(),
radio_discoverable);
is_radio_discoverable_ = radio_discoverable;
NEARBY_LOGS(INFO) << ": StartListening completed successfully.";
return true;
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
NEARBY_LOGS(ERROR) << __func__ << ": Exception setting up for listen: "
<< exception.what();
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": Exception setting up for listen: " << ex.code()
<< ": " << winrt::to_string(ex.message());
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
if (server_socket_ != nullptr) {
server_socket_->Close();
server_socket_ = nullptr;
}
if (rfcomm_provider_ != nullptr) {
rfcomm_provider_ = nullptr;
}
return false;
}
}
bool BluetoothClassicMedium::StopAdvertising() {
NEARBY_LOGS(INFO) << __func__ << ": StopAdvertising is called";
try {
if (rfcomm_provider_ == nullptr) {
NEARBY_LOGS(ERROR) << __func__
<< ": Ignore StopAdvertising due to no advertising.";
return true;
}
rfcomm_provider_.StopAdvertising();
rfcomm_provider_ = nullptr;
raw_server_socket_ = nullptr;
server_socket_ = nullptr;
NEARBY_LOGS(INFO) << ": StopAdvertising completed successfully.";
return true;
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__
<< ": StopAdvertising exception: " << exception.what();
return false;
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": StopAdvertising exception: " << ex.code() << ": "
<< winrt::to_string(ex.message());
return false;
} catch (...) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown exception.";
return false;
}
}
bool BluetoothClassicMedium::InitializeServiceSdpAttributes(
RfcommServiceProvider rfcomm_provider, std::string service_name) {
try {
auto sdpWriter = DataWriter();
// Write the Service Name Attribute.
sdpWriter.WriteByte(Constants::SdpServiceNameAttributeType);
// The length of the UTF-8 encoded Service Name SDP Attribute.
sdpWriter.WriteByte(service_name.size());
// The UTF-8 encoded Service Name value.
sdpWriter.UnicodeEncoding(UnicodeEncoding::Utf8);
sdpWriter.WriteString(winrt::to_hstring(service_name));
// Set the SDP Attribute on the RFCOMM Service Provider.
rfcomm_provider.SdpRawAttributes().Insert(
Constants::SdpServiceNameAttributeId, sdpWriter.DetachBuffer());
return true;
} catch (...) {
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to InitializeServiceSdpAttributes.";
return false;
}
}
} // namespace windows
} // namespace nearby