| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/bluetooth/test/fake_bluetooth_le_device_winrt.h" |
| |
| #include <wrl/client.h> |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/win/async_operation.h" |
| #include "base/win/scoped_hstring.h" |
| #include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h" |
| #include "device/bluetooth/test/bluetooth_test_win.h" |
| #include "device/bluetooth/test/fake_device_information_pairing_winrt.h" |
| #include "device/bluetooth/test/fake_gatt_characteristic_winrt.h" |
| #include "device/bluetooth/test/fake_gatt_device_service_winrt.h" |
| #include "device/bluetooth/test/fake_gatt_device_services_result_winrt.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| using ABI::Windows::Devices::Bluetooth::BluetoothAddressType; |
| using ABI::Windows::Devices::Bluetooth::BluetoothCacheMode; |
| using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus; |
| using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Connected; |
| using ABI::Windows::Devices::Bluetooth::BluetoothConnectionStatus_Disconnected; |
| using ABI::Windows::Devices::Bluetooth::BluetoothLEDevice; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattCommunicationStatus; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattCommunicationStatus_AccessDenied; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattCommunicationStatus_ProtocolError; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattCommunicationStatus_Success; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattCommunicationStatus_Unreachable; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattDeviceService; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| GattDeviceServicesResult; |
| using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: |
| IGattDeviceService; |
| using ABI::Windows::Devices::Bluetooth::IBluetoothLEAppearance; |
| using ABI::Windows::Devices::Enumeration::DeviceAccessStatus; |
| using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation; |
| using ABI::Windows::Devices::Enumeration::IDeviceInformation; |
| using ABI::Windows::Foundation::Collections::IVectorView; |
| using ABI::Windows::Foundation::IAsyncOperation; |
| using ABI::Windows::Foundation::ITypedEventHandler; |
| using Microsoft::WRL::Make; |
| |
| } // namespace |
| |
| FakeBluetoothLEDeviceWinrt::FakeBluetoothLEDeviceWinrt( |
| BluetoothTestWinrt* bluetooth_test_winrt) |
| : bluetooth_test_winrt_(bluetooth_test_winrt) {} |
| |
| FakeBluetoothLEDeviceWinrt::~FakeBluetoothLEDeviceWinrt() = default; |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_DeviceId(HSTRING* value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_Name(HSTRING* value) { |
| if (!name_) |
| return E_FAIL; |
| |
| *value = base::win::ScopedHString::Create(*name_).release(); |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_GattServices( |
| IVectorView<GattDeviceService*>** value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_ConnectionStatus( |
| BluetoothConnectionStatus* value) { |
| *value = status_; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_BluetoothAddress(uint64_t* value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::GetGattService( |
| GUID service_uuid, |
| IGattDeviceService** service) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::add_NameChanged( |
| ITypedEventHandler<BluetoothLEDevice*, IInspectable*>* handler, |
| EventRegistrationToken* token) { |
| name_changed_handler_ = handler; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::remove_NameChanged( |
| EventRegistrationToken token) { |
| name_changed_handler_ = nullptr; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::add_GattServicesChanged( |
| ITypedEventHandler<BluetoothLEDevice*, IInspectable*>* handler, |
| EventRegistrationToken* token) { |
| gatt_services_changed_handler_ = handler; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::remove_GattServicesChanged( |
| EventRegistrationToken token) { |
| gatt_services_changed_handler_ = nullptr; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::add_ConnectionStatusChanged( |
| ITypedEventHandler<BluetoothLEDevice*, IInspectable*>* handler, |
| EventRegistrationToken* token) { |
| connection_status_changed_handler_ = handler; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::remove_ConnectionStatusChanged( |
| EventRegistrationToken token) { |
| connection_status_changed_handler_ = nullptr; |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_DeviceInformation( |
| IDeviceInformation** value) { |
| return device_information_.CopyTo(value); |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_Appearance( |
| IBluetoothLEAppearance** value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_BluetoothAddressType( |
| BluetoothAddressType* value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::get_DeviceAccessInformation( |
| IDeviceAccessInformation** value) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::RequestAccessAsync( |
| IAsyncOperation<DeviceAccessStatus>** operation) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesAsync( |
| IAsyncOperation<GattDeviceServicesResult*>** operation) { |
| auto async_op = Make<base::win::AsyncOperation<GattDeviceServicesResult*>>(); |
| gatt_services_callback_ = async_op->callback(); |
| *operation = async_op.Detach(); |
| bluetooth_test_winrt_->OnFakeBluetoothDeviceConnectGattCalled(); |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesWithCacheModeAsync( |
| BluetoothCacheMode cache_mode, |
| IAsyncOperation<GattDeviceServicesResult*>** operation) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesForUuidAsync( |
| GUID service_uuid, |
| IAsyncOperation<GattDeviceServicesResult*>** operation) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::GetGattServicesForUuidWithCacheModeAsync( |
| GUID service_uuid, |
| BluetoothCacheMode cache_mode, |
| IAsyncOperation<GattDeviceServicesResult*>** operation) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceWinrt::Close() { |
| --reference_count_; |
| fake_services_.clear(); |
| bluetooth_test_winrt_->OnFakeBluetoothGattDisconnect(); |
| return S_OK; |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::AddReference() { |
| ++reference_count_; |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::RemoveReference() { |
| --reference_count_; |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateDevicePaired(bool is_paired) { |
| device_information_ = Make<FakeDeviceInformationWinrt>( |
| Make<FakeDeviceInformationPairingWinrt>(is_paired)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulatePairingPinCode(std::string pin_code) { |
| device_information_ = Make<FakeDeviceInformationWinrt>( |
| Make<FakeDeviceInformationPairingWinrt>(std::move(pin_code))); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattConnection() { |
| status_ = BluetoothConnectionStatus_Connected; |
| connection_status_changed_handler_->Invoke(this, nullptr); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt ::SimulateGattConnectionError( |
| BluetoothDevice::ConnectErrorCode error_code) { |
| if (!gatt_services_callback_) |
| return; |
| |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>( |
| GattCommunicationStatus_ProtocolError)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattDisconnection() { |
| if (status_ == BluetoothConnectionStatus_Disconnected) { |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>( |
| GattCommunicationStatus_Unreachable)); |
| 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; |
| connection_status_changed_handler_->Invoke(this, nullptr); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattNameChange( |
| const std::string& new_name) { |
| name_ = new_name; |
| name_changed_handler_->Invoke(this, nullptr); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattServicesDiscovered( |
| const std::vector<std::string>& uuids) { |
| for (const auto& uuid : uuids) { |
| // Attribute handles need to be unique for a given BLE device. Increasing by |
| // a large number ensures enough address space for the contained |
| // characteristics and descriptors. |
| fake_services_.push_back( |
| Make<FakeGattDeviceServiceWinrt>(bluetooth_test_winrt_, this, uuid, |
| service_attribute_handle_ += 0x0400)); |
| } |
| |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>(fake_services_)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattServiceRemoved( |
| BluetoothRemoteGattService* service) { |
| auto* device_service = static_cast<BluetoothRemoteGattServiceWinrt*>(service) |
| ->GetDeviceServiceForTesting(); |
| auto iter = std::find_if(fake_services_.begin(), fake_services_.end(), |
| [device_service](const auto& fake_service) { |
| return device_service == fake_service.Get(); |
| }); |
| DCHECK(iter != fake_services_.end()); |
| fake_services_.erase(iter); |
| SimulateGattServicesChanged(); |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>(fake_services_)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattCharacteristic( |
| BluetoothRemoteGattService* service, |
| const std::string& uuid, |
| int properties) { |
| // Simulate the fake characteristic on the GATT service and trigger a GATT |
| // re-scan via GattServicesChanged(). |
| auto* const fake_service = static_cast<FakeGattDeviceServiceWinrt*>( |
| static_cast<BluetoothRemoteGattServiceWinrt*>(service) |
| ->GetDeviceServiceForTesting()); |
| DCHECK(fake_service); |
| fake_service->SimulateGattCharacteristic(uuid, properties); |
| |
| SimulateGattServicesChanged(); |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>(fake_services_)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattDescriptor( |
| BluetoothRemoteGattCharacteristic* characteristic, |
| const std::string& uuid) { |
| // Simulate the fake descriptor on the GATT service and trigger a GATT |
| // re-scan via GattServicesChanged(). |
| auto* const fake_characteristic = static_cast<FakeGattCharacteristicWinrt*>( |
| static_cast<BluetoothRemoteGattCharacteristicWinrt*>(characteristic) |
| ->GetCharacteristicForTesting()); |
| DCHECK(fake_characteristic); |
| fake_characteristic->SimulateGattDescriptor(uuid); |
| |
| SimulateGattServicesChanged(); |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>(fake_services_)); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattServicesChanged() { |
| DCHECK(gatt_services_changed_handler_); |
| gatt_services_changed_handler_->Invoke(this, nullptr); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void FakeBluetoothLEDeviceWinrt::SimulateGattServicesDiscoveryError() { |
| DCHECK(gatt_services_callback_); |
| std::move(gatt_services_callback_) |
| .Run(Make<FakeGattDeviceServicesResultWinrt>( |
| GattCommunicationStatus_ProtocolError)); |
| } |
| |
| FakeBluetoothLEDeviceStaticsWinrt::FakeBluetoothLEDeviceStaticsWinrt( |
| BluetoothTestWinrt* bluetooth_test_winrt) |
| : bluetooth_test_winrt_(bluetooth_test_winrt) {} |
| |
| FakeBluetoothLEDeviceStaticsWinrt::~FakeBluetoothLEDeviceStaticsWinrt() = |
| default; |
| |
| HRESULT FakeBluetoothLEDeviceStaticsWinrt::FromIdAsync( |
| HSTRING device_id, |
| IAsyncOperation<BluetoothLEDevice*>** operation) { |
| return E_NOTIMPL; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceStaticsWinrt::FromBluetoothAddressAsync( |
| uint64_t bluetooth_address, |
| IAsyncOperation<BluetoothLEDevice*>** operation) { |
| auto async_op = Make<base::win::AsyncOperation<BluetoothLEDevice*>>(); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(async_op->callback(), |
| Make<FakeBluetoothLEDeviceWinrt>(bluetooth_test_winrt_))); |
| *operation = async_op.Detach(); |
| return S_OK; |
| } |
| |
| HRESULT FakeBluetoothLEDeviceStaticsWinrt::GetDeviceSelector( |
| HSTRING* device_selector) { |
| return E_NOTIMPL; |
| } |
| |
| } // namespace device |