Commit 9ab40ee7 authored by Jan Wilken Doerrie's avatar Jan Wilken Doerrie Committed by Commit Bot

[Bluetooth][WinRT] Fix Gatt Disconnection

This change fixes BluetoothDeviceWinrt::DiscoonectGatt. It does so by
working around a quirk in the UWP APIs, that requires dropping all
internal references to a given device. See this GitHub issue for further
discussion: https://github.com/jasongin/noble-uwp/issues/20

Bug: 821766
Change-Id: I5bf0e4e0ce0e3b43d9cbb10e3b11e378de05e6b3
Reviewed-on: https://chromium-review.googlesource.com/1183234Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585799}
parent 33a17747
...@@ -1159,7 +1159,7 @@ TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) { ...@@ -1159,7 +1159,7 @@ TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) {
EXPECT_TRUE(device->IsConnected()); EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected()); EXPECT_TRUE(device->IsGattConnected());
SimulateGattDisconnection(device); SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count()); EXPECT_EQ(2, observer.device_changed_count());
EXPECT_FALSE(device->IsConnected()); EXPECT_FALSE(device->IsConnected());
...@@ -1859,7 +1859,7 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) { ...@@ -1859,7 +1859,7 @@ TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_); EXPECT_EQ(1, gatt_discovery_attempts_);
SimulateGattDisconnection(device); SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
SimulateGattServicesDiscovered( SimulateGattServicesDiscovered(
......
...@@ -450,7 +450,20 @@ void BluetoothDeviceWinrt::CreateGattConnectionImpl() { ...@@ -450,7 +450,20 @@ void BluetoothDeviceWinrt::CreateGattConnectionImpl() {
} }
void BluetoothDeviceWinrt::DisconnectGatt() { void BluetoothDeviceWinrt::DisconnectGatt() {
// Closing the device and disposing of all references will trigger a Gatt
// Disconnection after a short timeout. Since the Gatt Services store a
// reference to |ble_device_| as well, we need to clear them to drop all
// remaining references, so that the OS disconnects.
// Reference:
// - https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client
CloseDevice(ble_device_); CloseDevice(ble_device_);
ClearGattServices();
// Stop any pending Gatt Discovery sessions and report an error. This will
// destroy |gatt_discoverer_| and release remaining references the discoverer
// might have hold.
if (gatt_discoverer_)
OnGattDiscoveryComplete(false);
} }
HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory( HRESULT BluetoothDeviceWinrt::GetBluetoothLEDeviceStaticsActivationFactory(
...@@ -517,9 +530,7 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged( ...@@ -517,9 +530,7 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged(
DidConnectGatt(); DidConnectGatt();
} else { } else {
gatt_discoverer_.reset(); gatt_discoverer_.reset();
gatt_services_.clear(); ClearGattServices();
device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false);
DidDisconnectGatt(); DidDisconnectGatt();
} }
} }
...@@ -527,6 +538,8 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged( ...@@ -527,6 +538,8 @@ void BluetoothDeviceWinrt::OnConnectionStatusChanged(
void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device, void BluetoothDeviceWinrt::OnGattServicesChanged(IBluetoothLEDevice* ble_device,
IInspectable* object) { IInspectable* object) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Note: We don't clear out |gatt_services_| here, as we don't want to break
// existing references to Gatt Services that did not change.
device_uuids_.ClearServiceUUIDs(); device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false); SetGattServicesDiscoveryComplete(false);
adapter_->NotifyDeviceChanged(this); adapter_->NotifyDeviceChanged(this);
...@@ -581,4 +594,10 @@ void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) { ...@@ -581,4 +594,10 @@ void BluetoothDeviceWinrt::OnGattDiscoveryComplete(bool success) {
gatt_discoverer_.reset(); gatt_discoverer_.reset();
} }
void BluetoothDeviceWinrt::ClearGattServices() {
gatt_services_.clear();
device_uuids_.ClearServiceUUIDs();
SetGattServicesDiscoveryComplete(false);
}
} // namespace device } // namespace device
...@@ -119,11 +119,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice { ...@@ -119,11 +119,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceWinrt : public BluetoothDevice {
void OnGattDiscoveryComplete(bool success); void OnGattDiscoveryComplete(bool success);
void OnPairingRequested( void ClearGattServices();
ABI::Windows::Devices::Enumeration::IDeviceInformationCustomPairing*
custom_pairing,
ABI::Windows::Devices::Enumeration::IDevicePairingRequestedEventArgs*
event_args);
uint64_t raw_address_; uint64_t raw_address_;
std::string address_; std::string address_;
......
...@@ -585,7 +585,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ...@@ -585,7 +585,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif #endif
ASSERT_EQ(1u, adapter_->GetDevices().size()); ASSERT_EQ(1u, adapter_->GetDevices().size());
SimulateGattDisconnection(adapter_->GetDevices()[0]); SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
...@@ -677,7 +677,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest, ...@@ -677,7 +677,7 @@ TEST_F(BluetoothRemoteGattCharacteristicTest,
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID)
ASSERT_EQ(1u, adapter_->GetDevices().size()); ASSERT_EQ(1u, adapter_->GetDevices().size());
SimulateGattDisconnection(adapter_->GetDevices()[0]); SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
...@@ -3321,7 +3321,7 @@ TEST_F( ...@@ -3321,7 +3321,7 @@ TEST_F(
characteristic1_->WriteRemoteCharacteristic( characteristic1_->WriteRemoteCharacteristic(
std::vector<uint8_t>(), GetCallback(Call::NOT_EXPECTED), std::vector<uint8_t>(), GetCallback(Call::NOT_EXPECTED),
GetGattErrorCallback(Call::EXPECTED)); GetGattErrorCallback(Call::EXPECTED));
SimulateGattDisconnection(device_); SimulateDeviceBreaksConnection(adapter_->GetDevices()[0]);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED, EXPECT_EQ(BluetoothRemoteGattService::GATT_ERROR_FAILED,
......
...@@ -110,6 +110,11 @@ BluetoothDevice* BluetoothTestBase::SimulateClassicDevice() { ...@@ -110,6 +110,11 @@ BluetoothDevice* BluetoothTestBase::SimulateClassicDevice() {
return nullptr; return nullptr;
} }
void BluetoothTestBase::SimulateDeviceBreaksConnection(
BluetoothDevice* device) {
SimulateGattDisconnection(device);
}
bool BluetoothTestBase::SimulateLocalGattCharacteristicNotificationsRequest( bool BluetoothTestBase::SimulateLocalGattCharacteristicNotificationsRequest(
BluetoothLocalGattCharacteristic* characteristic, BluetoothLocalGattCharacteristic* characteristic,
bool start) { bool start) {
......
...@@ -272,6 +272,10 @@ class BluetoothTestBase : public testing::Test { ...@@ -272,6 +272,10 @@ class BluetoothTestBase : public testing::Test {
// Simulates GattConnection disconnecting. // Simulates GattConnection disconnecting.
virtual void SimulateGattDisconnection(BluetoothDevice* device) {} virtual void SimulateGattDisconnection(BluetoothDevice* device) {}
// Simulates an event where the OS breaks the Gatt connection. Defaults to
// SimulateGattDisconnection(device).
virtual void SimulateDeviceBreaksConnection(BluetoothDevice* device);
// Simulates success of discovering services. |uuids| is used to create a // Simulates success of discovering services. |uuids| is used to create a
// service for each UUID string. Multiple UUIDs with the same value produce // service for each UUID string. Multiple UUIDs with the same value produce
// multiple service instances. // multiple service instances.
......
...@@ -718,6 +718,14 @@ void BluetoothTestWinrt::SimulateGattDisconnection(BluetoothDevice* device) { ...@@ -718,6 +718,14 @@ void BluetoothTestWinrt::SimulateGattDisconnection(BluetoothDevice* device) {
ble_device->SimulateGattDisconnection(); ble_device->SimulateGattDisconnection();
} }
void BluetoothTestWinrt::SimulateDeviceBreaksConnection(
BluetoothDevice* device) {
auto* const ble_device =
static_cast<TestBluetoothDeviceWinrt*>(device)->ble_device();
DCHECK(ble_device);
ble_device->SimulateDeviceBreaksConnection();
}
void BluetoothTestWinrt::SimulateGattServicesDiscovered( void BluetoothTestWinrt::SimulateGattServicesDiscovered(
BluetoothDevice* device, BluetoothDevice* device,
const std::vector<std::string>& uuids) { const std::vector<std::string>& uuids) {
......
...@@ -130,6 +130,7 @@ class BluetoothTestWinrt : public BluetoothTestWin, ...@@ -130,6 +130,7 @@ class BluetoothTestWinrt : public BluetoothTestWin,
BluetoothDevice* device, BluetoothDevice* device,
BluetoothDevice::ConnectErrorCode error_code) override; BluetoothDevice::ConnectErrorCode error_code) override;
void SimulateGattDisconnection(BluetoothDevice* device) override; void SimulateGattDisconnection(BluetoothDevice* device) override;
void SimulateDeviceBreaksConnection(BluetoothDevice* device) override;
void SimulateGattServicesDiscovered( void SimulateGattServicesDiscovered(
BluetoothDevice* device, BluetoothDevice* device,
const std::vector<std::string>& uuids) override; const std::vector<std::string>& uuids) override;
......
...@@ -183,10 +183,20 @@ HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesForUuidWithCacheModeAsync( ...@@ -183,10 +183,20 @@ HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesForUuidWithCacheModeAsync(
} }
HRESULT FakeBluetoothLEDeviceWinrt::Close() { HRESULT FakeBluetoothLEDeviceWinrt::Close() {
--reference_count_;
fake_services_.clear();
bluetooth_test_winrt_->OnFakeBluetoothGattDisconnect(); bluetooth_test_winrt_->OnFakeBluetoothGattDisconnect();
return S_OK; return S_OK;
} }
void FakeBluetoothLEDeviceWinrt::AddReference() {
++reference_count_;
}
void FakeBluetoothLEDeviceWinrt::RemoveReference() {
--reference_count_;
}
void FakeBluetoothLEDeviceWinrt::SimulateDevicePaired(bool is_paired) { void FakeBluetoothLEDeviceWinrt::SimulateDevicePaired(bool is_paired) {
device_information_ = Make<FakeDeviceInformationWinrt>( device_information_ = Make<FakeDeviceInformationWinrt>(
Make<FakeDeviceInformationPairingWinrt>(is_paired)); Make<FakeDeviceInformationPairingWinrt>(is_paired));
...@@ -221,6 +231,24 @@ void FakeBluetoothLEDeviceWinrt::SimulateGattDisconnection() { ...@@ -221,6 +231,24 @@ void FakeBluetoothLEDeviceWinrt::SimulateGattDisconnection() {
return; return;
} }
// Simulate production UWP behavior that only really disconnects once all
// references to a device are dropped.
if (reference_count_ == 0u) {
status_ = BluetoothConnectionStatus_Disconnected;
connection_status_changed_handler_->Invoke(this, nullptr);
}
}
void FakeBluetoothLEDeviceWinrt::SimulateDeviceBreaksConnection() {
if (status_ == BluetoothConnectionStatus_Disconnected) {
DCHECK(gatt_services_callback_);
std::move(gatt_services_callback_)
.Run(Make<FakeGattDeviceServicesResultWinrt>(
GattCommunicationStatus_Unreachable));
return;
}
// Simulate a Gatt Disconnecion regardless of the reference count.
status_ = BluetoothConnectionStatus_Disconnected; status_ = BluetoothConnectionStatus_Disconnected;
connection_status_changed_handler_->Invoke(this, nullptr); connection_status_changed_handler_->Invoke(this, nullptr);
} }
...@@ -231,8 +259,9 @@ void FakeBluetoothLEDeviceWinrt::SimulateGattServicesDiscovered( ...@@ -231,8 +259,9 @@ void FakeBluetoothLEDeviceWinrt::SimulateGattServicesDiscovered(
// Attribute handles need to be unique for a given BLE device. Increasing by // Attribute handles need to be unique for a given BLE device. Increasing by
// a large number ensures enough address space for the contained // a large number ensures enough address space for the contained
// characteristics and descriptors. // characteristics and descriptors.
fake_services_.push_back(Make<FakeGattDeviceServiceWinrt>( fake_services_.push_back(
bluetooth_test_winrt_, uuid, service_attribute_handle_ += 0x0400)); Make<FakeGattDeviceServiceWinrt>(bluetooth_test_winrt_, this, uuid,
service_attribute_handle_ += 0x0400));
} }
DCHECK(gatt_services_callback_); DCHECK(gatt_services_callback_);
......
...@@ -114,12 +114,19 @@ class FakeBluetoothLEDeviceWinrt ...@@ -114,12 +114,19 @@ class FakeBluetoothLEDeviceWinrt
// IClosable: // IClosable:
IFACEMETHODIMP Close() override; IFACEMETHODIMP Close() override;
// We perform explicit reference counting, to be able to query the ref count
// ourselves. This is required to simulate Gatt disconnection behavior
// exhibited by the production UWP APIs.
void AddReference();
void RemoveReference();
void SimulateDevicePaired(bool is_paired); void SimulateDevicePaired(bool is_paired);
void SimulatePairingPinCode(std::string pin_code); void SimulatePairingPinCode(std::string pin_code);
void SimulateGattConnection(); void SimulateGattConnection();
void SimulateGattConnectionError( void SimulateGattConnectionError(
BluetoothDevice::ConnectErrorCode error_code); BluetoothDevice::ConnectErrorCode error_code);
void SimulateGattDisconnection(); void SimulateGattDisconnection();
void SimulateDeviceBreaksConnection();
void SimulateGattServicesDiscovered(const std::vector<std::string>& uuids); void SimulateGattServicesDiscovered(const std::vector<std::string>& uuids);
void SimulateGattServicesChanged(); void SimulateGattServicesChanged();
void SimulateGattServiceRemoved(BluetoothRemoteGattService* service); void SimulateGattServiceRemoved(BluetoothRemoteGattService* service);
...@@ -132,6 +139,7 @@ class FakeBluetoothLEDeviceWinrt ...@@ -132,6 +139,7 @@ class FakeBluetoothLEDeviceWinrt
private: private:
BluetoothTestWinrt* bluetooth_test_winrt_ = nullptr; BluetoothTestWinrt* bluetooth_test_winrt_ = nullptr;
uint32_t reference_count_ = 1u;
ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus status_ = ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus status_ =
ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Disconnected; ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Disconnected;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/win/async_operation.h" #include "base/win/async_operation.h"
#include "device/bluetooth/bluetooth_uuid.h" #include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/test/fake_bluetooth_le_device_winrt.h"
#include "device/bluetooth/test/fake_gatt_characteristic_winrt.h" #include "device/bluetooth/test/fake_gatt_characteristic_winrt.h"
#include "device/bluetooth/test/fake_gatt_characteristics_result_winrt.h" #include "device/bluetooth/test/fake_gatt_characteristics_result_winrt.h"
...@@ -38,20 +39,27 @@ using ABI::Windows::Devices::Enumeration::DeviceAccessStatus; ...@@ -38,20 +39,27 @@ using ABI::Windows::Devices::Enumeration::DeviceAccessStatus;
using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation; using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation;
using ABI::Windows::Foundation::Collections::IVectorView; using ABI::Windows::Foundation::Collections::IVectorView;
using ABI::Windows::Foundation::IAsyncOperation; using ABI::Windows::Foundation::IAsyncOperation;
using Microsoft::WRL::ComPtr;
using Microsoft::WRL::Make; using Microsoft::WRL::Make;
} // namespace } // namespace
FakeGattDeviceServiceWinrt::FakeGattDeviceServiceWinrt( FakeGattDeviceServiceWinrt::FakeGattDeviceServiceWinrt(
BluetoothTestWinrt* bluetooth_test_winrt, BluetoothTestWinrt* bluetooth_test_winrt,
ComPtr<FakeBluetoothLEDeviceWinrt> fake_device,
base::StringPiece uuid, base::StringPiece uuid,
uint16_t attribute_handle) uint16_t attribute_handle)
: bluetooth_test_winrt_(bluetooth_test_winrt), : bluetooth_test_winrt_(bluetooth_test_winrt),
fake_device_(std::move(fake_device)),
uuid_(BluetoothUUID::GetCanonicalValueAsGUID(uuid)), uuid_(BluetoothUUID::GetCanonicalValueAsGUID(uuid)),
attribute_handle_(attribute_handle), attribute_handle_(attribute_handle),
characteristic_attribute_handle_(attribute_handle_) {} characteristic_attribute_handle_(attribute_handle_) {
fake_device_->AddReference();
}
FakeGattDeviceServiceWinrt::~FakeGattDeviceServiceWinrt() = default; FakeGattDeviceServiceWinrt::~FakeGattDeviceServiceWinrt() {
fake_device_->RemoveReference();
}
HRESULT FakeGattDeviceServiceWinrt::GetCharacteristics( HRESULT FakeGattDeviceServiceWinrt::GetCharacteristics(
GUID characteristic_uuid, GUID characteristic_uuid,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
namespace device { namespace device {
class BluetoothTestWinrt; class BluetoothTestWinrt;
class FakeBluetoothLEDeviceWinrt;
class FakeGattCharacteristicWinrt; class FakeGattCharacteristicWinrt;
class FakeGattDeviceServiceWinrt class FakeGattDeviceServiceWinrt
...@@ -30,9 +31,11 @@ class FakeGattDeviceServiceWinrt ...@@ -30,9 +31,11 @@ class FakeGattDeviceServiceWinrt
ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceService3> { IGattDeviceService3> {
public: public:
FakeGattDeviceServiceWinrt(BluetoothTestWinrt* bluetooth_test_winrt, FakeGattDeviceServiceWinrt(
base::StringPiece uuid, BluetoothTestWinrt* bluetooth_test_winrt,
uint16_t attribute_handle); Microsoft::WRL::ComPtr<FakeBluetoothLEDeviceWinrt> fake_device,
base::StringPiece uuid,
uint16_t attribute_handle);
~FakeGattDeviceServiceWinrt() override; ~FakeGattDeviceServiceWinrt() override;
// IGattDeviceService: // IGattDeviceService:
...@@ -115,6 +118,7 @@ class FakeGattDeviceServiceWinrt ...@@ -115,6 +118,7 @@ class FakeGattDeviceServiceWinrt
private: private:
BluetoothTestWinrt* bluetooth_test_winrt_; BluetoothTestWinrt* bluetooth_test_winrt_;
Microsoft::WRL::ComPtr<FakeBluetoothLEDeviceWinrt> fake_device_;
GUID uuid_; GUID uuid_;
uint16_t attribute_handle_; uint16_t attribute_handle_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment