blob: 7d1d3d4d1431d61bc181972a2ec5a7801d5c7f69 [file] [log] [blame]
// 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