blob: 47cb964bdd97b41a61f7ed8c9ca1df088565e9dc [file] [log] [blame]
// Copyright 2015 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 "content/shell/browser/web_test/web_test_bluetooth_adapter_provider.h"
#include <set>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/format_macros.h"
#include "base/location.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_uuid.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_descriptor.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_notify_session.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::StringPiece;
using device::BluetoothAdapter;
using device::BluetoothDevice;
using device::BluetoothGattCharacteristic;
using device::BluetoothRemoteGattCharacteristic;
using device::BluetoothRemoteGattDescriptor;
using device::BluetoothRemoteGattService;
using device::BluetoothUUID;
using device::MockBluetoothAdapter;
using device::MockBluetoothDevice;
using device::MockBluetoothDiscoverySession;
using device::MockBluetoothGattCharacteristic;
using device::MockBluetoothGattConnection;
using device::MockBluetoothGattDescriptor;
using device::MockBluetoothGattNotifySession;
using device::MockBluetoothGattService;
using testing::_;
using testing::ElementsAre;
using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::ResultOf;
using testing::Return;
typedef testing::NiceMock<MockBluetoothAdapter> NiceMockBluetoothAdapter;
typedef testing::NiceMock<MockBluetoothDevice> NiceMockBluetoothDevice;
typedef testing::NiceMock<MockBluetoothDiscoverySession>
NiceMockBluetoothDiscoverySession;
typedef testing::NiceMock<MockBluetoothGattDescriptor>
NiceMockBluetoothGattDescriptor;
typedef testing::NiceMock<MockBluetoothGattCharacteristic>
NiceMockBluetoothGattCharacteristic;
typedef testing::NiceMock<MockBluetoothGattConnection>
NiceMockBluetoothGattConnection;
typedef testing::NiceMock<MockBluetoothGattService>
NiceMockBluetoothGattService;
typedef testing::NiceMock<MockBluetoothGattNotifySession>
NiceMockBluetoothGattNotifySession;
namespace {
// Bluetooth UUIDs suitable to pass to BluetoothUUID():
// Services:
const char kBatteryServiceUUID[] = "180f";
const char kBlocklistTestServiceUUID[] = "611c954a-263b-4f4a-aab6-01ddb953f985";
const char kDeviceInformationServiceUUID[] = "180a";
const char kGenericAccessServiceUUID[] = "1800";
const char kGlucoseServiceUUID[] = "1808";
const char kHealthThermometerUUID[] = "1809";
const char kHeartRateServiceUUID[] = "180d";
const char kHumanInterfaceDeviceServiceUUID[] = "1812";
const char kRequestDisconnectionServiceUUID[] =
"01d7d889-7451-419f-aeb8-d65e7b9277af";
const char kTxPowerServiceUUID[] = "1804";
// Characteristics:
const char kBlocklistExcludeReadsCharacteristicUUID[] =
"bad1c9a2-9a5b-4015-8b60-1579bbbf2135";
const char kRequestDisconnectionCharacteristicUUID[] =
"01d7d88a-7451-419f-aeb8-d65e7b9277af";
const char kBodySensorLocation[] = "2a38";
const char kDeviceNameUUID[] = "2a00";
const char kMeasurementIntervalUUID[] = "2a21";
const char kHeartRateMeasurementUUID[] = "2a37";
const char kSerialNumberStringUUID[] = "2a25";
const char kPeripheralPrivacyFlagUUID[] = "2a02";
// Descriptors:
const char kUserDescriptionUUID[] = "2901";
// Client Config is in our blocklist. It must not be writable
const char kClientConfigUUID[] = "2902";
// Blocklisted descriptor
const char kBlocklistedDescriptorUUID[] =
"bad2ddcf-60db-45cd-bef9-fd72b153cf7c";
const char kBlocklistedReadDescriptorUUID[] =
"bad3ec61-3cc3-4954-9702-7977df514114";
const char kCharacteristicUserDescription[] =
"gatt.characteristic_user_description";
// Invokes Run() on the k-th argument of the function with no arguments.
ACTION_TEMPLATE(RunCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_0_VALUE_PARAMS()) {
return std::move(std::get<k>(args)).Run();
}
// Invokes Run() on the k-th argument of the function with 1 argument.
ACTION_TEMPLATE(RunCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p0)) {
return std::move(std::get<k>(args)).Run(p0);
}
// Invokes Run() on the k-th argument of the function with the result
// of |func| as an argument.
ACTION_TEMPLATE(RunCallbackWithResult,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(func)) {
return std::move(std::get<k>(args)).Run(func());
}
// Function to iterate over the adapter's devices and return the one
// that matches the address.
ACTION_P(GetMockDevice, adapter) {
std::string address = arg0;
for (BluetoothDevice* device : adapter->GetMockDevices()) {
if (device->GetAddress() == address)
return device;
}
return nullptr;
}
std::set<BluetoothUUID> GetUUIDs(
const device::BluetoothDiscoveryFilter* filter) {
std::set<BluetoothUUID> result;
filter->GetUUIDs(result);
return result;
}
// Notifies the adapter's observers for each device id the adapter.
void NotifyDevicesAdded(MockBluetoothAdapter* adapter) {
for (BluetoothDevice* device : adapter->GetMockDevices()) {
for (auto& observer : adapter->GetObservers())
observer.DeviceAdded(adapter, device);
}
}
// Notifies the adapter's observers that the services have been discovered.
void NotifyServicesDiscovered(MockBluetoothAdapter* adapter,
MockBluetoothDevice* device) {
for (auto& observer : adapter->GetObservers())
observer.GattServicesDiscovered(adapter, device);
}
// Notifies the adapter's observers that a device has changed.
void NotifyDeviceChanged(MockBluetoothAdapter* adapter,
MockBluetoothDevice* device) {
for (auto& observer : adapter->GetObservers())
observer.DeviceChanged(adapter, device);
}
} // namespace
namespace content {
// static
scoped_refptr<BluetoothAdapter>
WebTestBluetoothAdapterProvider::GetBluetoothAdapter(
const std::string& fake_adapter_name) {
if (fake_adapter_name == "BaseAdapter")
return GetBaseAdapter();
if (fake_adapter_name == "ScanFilterCheckingAdapter")
return GetScanFilterCheckingAdapter();
if (fake_adapter_name == "EmptyAdapter")
return GetEmptyAdapter();
if (fake_adapter_name == "FailStartDiscoveryAdapter")
return GetFailStartDiscoveryAdapter();
if (fake_adapter_name == "GlucoseHeartRateAdapter")
return GetGlucoseHeartRateAdapter();
if (fake_adapter_name == "MissingServiceHeartRateAdapter")
return GetMissingServiceHeartRateAdapter();
if (fake_adapter_name == "MissingCharacteristicHeartRateAdapter")
return GetMissingCharacteristicHeartRateAdapter();
if (fake_adapter_name == "HeartRateAdapter")
return GetHeartRateAdapter();
if (fake_adapter_name == "NoNameDeviceAdapter")
return GetNoNameDeviceAdapter();
if (fake_adapter_name == "EmptyNameHeartRateAdapter")
return GetEmptyNameHeartRateAdapter();
if (fake_adapter_name == "NoNameHeartRateAdapter")
return GetNoNameHeartRateAdapter();
if (fake_adapter_name == "TwoHeartRateServicesAdapter")
return GetTwoHeartRateServicesAdapter();
if (fake_adapter_name == "DisconnectingHeartRateAdapter")
return GetDisconnectingHeartRateAdapter();
if (fake_adapter_name == "DisconnectingHealthThermometerAdapter")
return GetDisconnectingHealthThermometer(true);
if (fake_adapter_name ==
"MissingDescriptorsDisconnectingHealthThermometerAdapter")
return GetDisconnectingHealthThermometer(false);
if (fake_adapter_name == "DisconnectingDuringServiceRetrievalAdapter")
return GetServicesDiscoveredAfterReconnectionAdapter(true /* disconnect */);
if (fake_adapter_name == "ServicesDiscoveredAfterReconnectionAdapter")
return GetServicesDiscoveredAfterReconnectionAdapter(
false /* disconnect */);
if (fake_adapter_name == "DisconnectingDuringSuccessGATTOperationAdapter") {
return GetGATTOperationFinishesAfterReconnectionAdapter(
true /* disconnect */, true /* succeeds */);
}
if (fake_adapter_name == "DisconnectingDuringFailureGATTOperationAdapter") {
return GetGATTOperationFinishesAfterReconnectionAdapter(
true /* disconnect */, false /* succeeds */);
}
if (fake_adapter_name == "GATTOperationSucceedsAfterReconnectionAdapter") {
return GetGATTOperationFinishesAfterReconnectionAdapter(
false /* disconnect */, true /* succeeds */);
}
if (fake_adapter_name == "GATTOperationFailsAfterReconnectionAdapter") {
return GetGATTOperationFinishesAfterReconnectionAdapter(
false /* disconnect */, false /* succeeds */);
}
if (fake_adapter_name == "DisconnectingDuringStopNotifySessionAdapter") {
return GetStopNotifySessionFinishesAfterReconnectionAdapter(
true /* disconnect */);
}
if (fake_adapter_name ==
"StopNotifySessionFinishesAfterReconnectionAdapter") {
return GetStopNotifySessionFinishesAfterReconnectionAdapter(
false /* disconnect */);
}
if (fake_adapter_name == "BlocklistTestAdapter")
return GetBlocklistTestAdapter();
if (fake_adapter_name == "FailingConnectionsAdapter")
return GetFailingConnectionsAdapter();
if (fake_adapter_name == "FailingGATTOperationsAdapter")
return GetFailingGATTOperationsAdapter();
if (fake_adapter_name == "SecondDiscoveryFindsHeartRateAdapter")
return GetSecondDiscoveryFindsHeartRateAdapter();
if (fake_adapter_name == "DeviceEventAdapter")
return GetDeviceEventAdapter();
if (fake_adapter_name == "DevicesRemovedAdapter")
return GetDevicesRemovedAdapter();
if (fake_adapter_name == "DelayedServicesDiscoveryAdapter")
return GetDelayedServicesDiscoveryAdapter();
if (fake_adapter_name.empty())
return nullptr;
// New adapters that can be used when fuzzing the Web Bluetooth API
// should also be added to
// src/third_party/WebKit/Source/modules/
// bluetooth/testing/clusterfuzz/constraints.py.
NOTREACHED() << fake_adapter_name;
return nullptr;
}
// Adapters
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetBaseAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(
new NiceMockBluetoothAdapter());
// Using Invoke allows the adapter returned from this method to be futher
// modified and have devices added to it. The call to ::GetDevices will
// invoke ::GetConstMockDevices, returning all devices added up to that time.
ON_CALL(*adapter, GetDevices())
.WillByDefault(
Invoke(adapter.get(), &MockBluetoothAdapter::GetConstMockDevices));
// The call to ::GetDevice will invoke GetMockDevice which returns a device
// matching the address provided if the device was added to the mock.
ON_CALL(*adapter, GetDevice(_)).WillByDefault(GetMockDevice(adapter.get()));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetPresentAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetBaseAdapter());
ON_CALL(*adapter, IsPresent()).WillByDefault(Return(true));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetPoweredAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPresentAdapter());
ON_CALL(*adapter, IsPowered()).WillByDefault(Return(true));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetScanFilterCheckingAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
MockBluetoothAdapter* adapter_ptr = adapter.get();
// This fails the test with an error message listing actual and expected UUIDs
// if StartDiscoverySessionWithFilter() is called with the wrong argument.
EXPECT_CALL(
*adapter,
StartDiscoverySessionWithFilterRaw(
ResultOf(&GetUUIDs, ElementsAre(BluetoothUUID(kGlucoseServiceUUID),
BluetoothUUID(kHeartRateServiceUUID),
BluetoothUUID(kBatteryServiceUUID))),
_, _))
.WillRepeatedly(
RunCallbackWithResult<1 /* success_callback */>([adapter_ptr]() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&NotifyDevicesAdded,
base::RetainedRef(adapter_ptr)));
return GetDiscoverySession();
}));
// Any unexpected call results in the failure callback.
ON_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillByDefault(RunCallback<2 /* error_callback */>());
// We need to add a device otherwise requestDevice would reject.
adapter->AddMockDevice(GetBatteryDevice(adapter.get()));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetFailStartDiscoveryAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
ON_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillByDefault(RunCallback<2 /* error_callback */>());
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetEmptyAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
MockBluetoothAdapter* adapter_ptr = adapter.get();
ON_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillByDefault(
RunCallbackWithResult<1 /* success_callback */>([adapter_ptr]() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&NotifyDevicesAdded,
base::RetainedRef(adapter_ptr)));
return GetDiscoverySession();
}));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetGlucoseHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
adapter->AddMockDevice(GetHeartRateDevice(adapter.get()));
adapter->AddMockDevice(GetGlucoseDevice(adapter.get()));
return adapter;
}
// Adds a device to |adapter| and notifies all observers about that new device.
// Mocks can call this asynchronously to cause changes in the middle of a test.
static void AddDevice(scoped_refptr<NiceMockBluetoothAdapter> adapter,
std::unique_ptr<NiceMockBluetoothDevice> new_device) {
NiceMockBluetoothDevice* new_device_ptr = new_device.get();
adapter->AddMockDevice(std::move(new_device));
for (auto& observer : adapter->GetObservers())
observer.DeviceAdded(adapter.get(), new_device_ptr);
}
static void RemoveDevice(scoped_refptr<NiceMockBluetoothAdapter> adapter,
const std::string& device_address) {
std::unique_ptr<MockBluetoothDevice> removed_device =
adapter->RemoveMockDevice(device_address);
for (auto& observer : adapter->GetObservers())
observer.DeviceRemoved(adapter.get(), removed_device.get());
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetSecondDiscoveryFindsHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
EXPECT_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillOnce(RunCallbackWithResult<1 /* success_callback */>(
[]() { return GetDiscoverySession(); }))
.WillOnce(
RunCallbackWithResult<1 /* success_callback */>([adapter_ptr]() {
// In the second discovery session, have the adapter discover a new
// device, shortly after the session starts.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AddDevice, base::WrapRefCounted(adapter_ptr),
GetHeartRateDevice(adapter_ptr)));
return GetDiscoverySession();
}));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetDeviceEventAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
// Add ConnectedHeartRateDevice.
auto connected_hr(GetBaseDevice(adapter.get(), "Connected Heart Rate Device",
{BluetoothUUID(kHeartRateServiceUUID)},
makeMACAddress(0x0)));
connected_hr->SetConnected(true);
adapter->AddMockDevice(std::move(connected_hr));
// Add ChangingBatteryDevice with no uuids.
auto changing_battery(GetBaseDevice(adapter.get(), "Changing Battery Device",
BluetoothDevice::UUIDList(),
makeMACAddress(0x1)));
changing_battery->SetConnected(false);
NiceMockBluetoothDevice* changing_battery_ptr = changing_battery.get();
adapter->AddMockDevice(std::move(changing_battery));
// Add Non Connected Tx Power Device.
auto non_connected_tx_power(
GetBaseDevice(adapter.get(), "Non Connected Tx Power Device",
{BluetoothUUID(kTxPowerServiceUUID)}, makeMACAddress(0x2)));
non_connected_tx_power->SetConnected(false);
adapter->AddMockDevice(std::move(non_connected_tx_power));
// Add Discovery Generic Access Device with no uuids.
auto discovery_generic_access(
GetBaseDevice(adapter.get(), "Discovery Generic Access Device",
BluetoothDevice::UUIDList(), makeMACAddress(0x3)));
discovery_generic_access->SetConnected(true);
NiceMockBluetoothDevice* discovery_generic_access_ptr =
discovery_generic_access.get();
adapter->AddMockDevice(std::move(discovery_generic_access));
ON_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillByDefault(RunCallbackWithResult<1 /* success_callback */>(
[adapter_ptr, changing_battery_ptr, discovery_generic_access_ptr]() {
if (adapter_ptr->GetDevices().size() == 4) {
// Post task to add NewGlucoseDevice.
auto glucose_device(GetBaseDevice(
adapter_ptr, "New Glucose Device",
{BluetoothUUID(kGlucoseServiceUUID)}, makeMACAddress(0x4)));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AddDevice, base::WrapRefCounted(adapter_ptr),
std::move(glucose_device)));
// Add uuid and notify of device changed.
changing_battery_ptr->AddUUID(BluetoothUUID(kBatteryServiceUUID));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr),
changing_battery_ptr));
// Add uuid and notify of services discovered.
discovery_generic_access_ptr->AddUUID(
BluetoothUUID(kGenericAccessServiceUUID));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&NotifyServicesDiscovered,
base::RetainedRef(adapter_ptr),
discovery_generic_access_ptr));
}
return GetDiscoverySession();
}));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetDevicesRemovedAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetPoweredAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
// Add ConnectedHeartRateDevice.
auto connected_hr(GetBaseDevice(adapter.get(), "Connected Heart Rate Device",
{BluetoothUUID(kHeartRateServiceUUID)},
makeMACAddress(0x0)));
connected_hr->SetConnected(true);
std::string connected_hr_address = connected_hr->GetAddress();
adapter->AddMockDevice(std::move(connected_hr));
ON_CALL(*adapter, StartDiscoverySessionWithFilterRaw(_, _, _))
.WillByDefault(RunCallbackWithResult<1 /* success_callback */>(
[adapter_ptr, connected_hr_address]() {
if (adapter_ptr->GetDevices().size() == 1) {
// Post task to add NewGlucoseDevice.
auto glucose_device(GetBaseDevice(
adapter_ptr, "New Glucose Device",
{BluetoothUUID(kGlucoseServiceUUID)}, makeMACAddress(0x4)));
std::string glucose_address = glucose_device->GetAddress();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&AddDevice, base::WrapRefCounted(adapter_ptr),
std::move(glucose_device)));
// Post task to remove ConnectedHeartRateDevice.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RemoveDevice,
base::WrapRefCounted(adapter_ptr),
connected_hr_address));
// Post task to remove NewGlucoseDevice.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RemoveDevice,
base::WrapRefCounted(adapter_ptr),
glucose_address));
}
return GetDiscoverySession();
}));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetMissingServiceHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
adapter->AddMockDevice(GetHeartRateDevice(adapter.get()));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetMissingCharacteristicHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get()));
auto generic_access(GetBaseGATTService("Generic Access", device.get(),
kGenericAccessServiceUUID));
auto heart_rate(
GetBaseGATTService("Heart Rate", device.get(), kHeartRateServiceUUID));
// Intentionally NOT adding a characteristic to heart_rate service.
device->AddMockService(std::move(generic_access));
device->AddMockService(std::move(heart_rate));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetDelayedServicesDiscoveryAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get()));
MockBluetoothAdapter* adapter_ptr = adapter.get();
MockBluetoothDevice* device_ptr = device.get();
// Override the previous mock implementation of
// IsGattServicesDiscoveryComplete so that the first time the function is
// called it returns false, adds a service and posts a task to notify
// the services have been discovered. Subsequent calls to the function
// will return true.
ON_CALL(*device, IsGattServicesDiscoveryComplete())
.WillByDefault(Invoke([adapter_ptr, device_ptr] {
std::vector<BluetoothRemoteGattService*> services =
device_ptr->GetMockServices();
if (services.size() == 0) {
auto heart_rate(GetBaseGATTService("Heart Rate", device_ptr,
kHeartRateServiceUUID));
device_ptr->AddMockService(std::move(heart_rate));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyServicesDiscovered,
base::RetainedRef(adapter_ptr), device_ptr));
DCHECK(services.empty());
return false;
}
return true;
}));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get()));
// TODO(ortuno): Implement the rest of the service's characteristics
// See: http://crbug.com/529975
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetHeartRateService(adapter.get(), device.get()));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetDisconnectingHealthThermometer(
bool add_descriptors) {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
auto device(GetConnectableDevice(
adapter_ptr, "Disconnecting Health Thermometer",
std::vector<BluetoothUUID>({BluetoothUUID(kGenericAccessServiceUUID),
BluetoothUUID(kHealthThermometerUUID)})));
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetDisconnectingService(adapter.get(), device.get()));
auto health_thermometer(GetBaseGATTService("Health Thermometer", device.get(),
kHealthThermometerUUID));
// Measurement Interval
auto measurement_interval(GetBaseGATTCharacteristic(
"Measurement Interval", health_thermometer.get(),
kMeasurementIntervalUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE |
BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
NiceMockBluetoothGattCharacteristic* measurement_ptr =
measurement_interval.get();
ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_, _))
.WillByDefault(
RunCallback<0 /* success_callback */>(std::vector<uint8_t>({1})));
ON_CALL(*measurement_interval, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(RunCallback<1 /* success_callback */>());
ON_CALL(*measurement_interval, StartNotifySession_(_, _))
.WillByDefault(
RunCallbackWithResult<0 /* success_callback */>([measurement_ptr]() {
return GetBaseGATTNotifySession(measurement_ptr->GetWeakPtr());
}));
if (add_descriptors) {
const std::string descriptorName = kCharacteristicUserDescription;
auto user_description = std::make_unique<NiceMockBluetoothGattDescriptor>(
measurement_interval.get(), descriptorName,
BluetoothUUID(kUserDescriptionUUID), false /* is_local */,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
ON_CALL(*user_description, ReadRemoteDescriptor_(_, _))
.WillByDefault(
Invoke([descriptorName](
BluetoothRemoteGattDescriptor::ValueCallback& callback,
BluetoothRemoteGattDescriptor::ErrorCallback&) {
std::vector<uint8_t> value(descriptorName.begin(),
descriptorName.end());
std::move(callback).Run(value);
}));
ON_CALL(*user_description, WriteRemoteDescriptor_(_, _, _))
.WillByDefault(RunCallback<1 /* success_callback */>());
auto client_config = std::make_unique<NiceMockBluetoothGattDescriptor>(
measurement_interval.get(), "gatt.client_characteristic_configuration",
BluetoothUUID(kClientConfigUUID), false /* is_local */,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
device::BluetoothRemoteGattCharacteristic::PROPERTY_WRITE);
// Crash if WriteRemoteDescriptor called. Not using GoogleMock's Expect
// because this is used in web tests that may not report a mock
// expectation.
ON_CALL(*client_config, WriteRemoteDescriptor_(_, _, _))
.WillByDefault(
Invoke([](const std::vector<uint8_t>&, base::OnceClosure&,
BluetoothRemoteGattDescriptor::ErrorCallback&) {
NOTREACHED();
}));
auto no_read_descriptor = std::make_unique<NiceMockBluetoothGattDescriptor>(
measurement_interval.get(), kBlocklistedReadDescriptorUUID,
BluetoothUUID(kBlocklistedReadDescriptorUUID), false,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
device::BluetoothRemoteGattCharacteristic::PROPERTY_WRITE);
// Crash if ReadRemoteDescriptor called. Not using GoogleMock's Expect
// because this is used in web tests that may not report a mock
// expectation
// error correctly as a web test failure.
ON_CALL(*no_read_descriptor, ReadRemoteDescriptor_(_, _))
.WillByDefault(
Invoke([](BluetoothRemoteGattDescriptor::ValueCallback&,
BluetoothRemoteGattDescriptor::ErrorCallback&) {
NOTREACHED();
}));
// Add it here with full permission as the blocklist should prevent us from
// accessing this descriptor
auto blocklisted_descriptor =
std::make_unique<NiceMockBluetoothGattDescriptor>(
measurement_interval.get(), kBlocklistedDescriptorUUID,
BluetoothUUID(kBlocklistedDescriptorUUID), false,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ |
device::BluetoothRemoteGattCharacteristic::PROPERTY_WRITE);
measurement_interval->AddMockDescriptor(std::move(user_description));
measurement_interval->AddMockDescriptor(std::move(client_config));
measurement_interval->AddMockDescriptor(std::move(blocklisted_descriptor));
measurement_interval->AddMockDescriptor(std::move(no_read_descriptor));
}
health_thermometer->AddMockCharacteristic(std::move(measurement_interval));
device->AddMockService(std::move(health_thermometer));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetNoNameDeviceAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetConnectableDevice(adapter.get(), nullptr /* device_name */));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetEmptyNameHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get(), /* device_name */ ""));
// TODO(ortuno): Implement the rest of the service's characteristics
// See: http://crbug.com/529975
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetHeartRateService(adapter.get(), device.get()));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetNoNameHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get(), /* device_name */ nullptr));
// TODO(ortuno): Implement the rest of the service's characteristics
// See: http://crbug.com/529975
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetHeartRateService(adapter.get(), device.get()));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetTwoHeartRateServicesAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get()));
device->AddMockService(GetGenericAccessService(device.get()));
// First Heart Rate Service has one Heart Rate Measurement characteristic
// and one Body Sensor Location characteristic.
auto first_heart_rate(GetBaseGATTService("First Heart Rate", device.get(),
kHeartRateServiceUUID));
// Heart Rate Measurement
auto heart_rate_measurement(GetBaseGATTCharacteristic(
"Heart Rate Measurement", first_heart_rate.get(),
kHeartRateMeasurementUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
// Body Sensor Location Characteristic
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
body_sensor_location_chest(GetBaseGATTCharacteristic(
"Body Sensor Location Chest", first_heart_rate.get(),
kBodySensorLocation,
BluetoothRemoteGattCharacteristic::PROPERTY_READ));
first_heart_rate->AddMockCharacteristic(std::move(heart_rate_measurement));
first_heart_rate->AddMockCharacteristic(
std::move(body_sensor_location_chest));
device->AddMockService(std::move(first_heart_rate));
// Second Heart Rate Service has only one Body Sensor Location
// characteristic.
auto second_heart_rate(GetBaseGATTService("Second Heart Rate", device.get(),
kHeartRateServiceUUID));
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
body_sensor_location_wrist(GetBaseGATTCharacteristic(
"Body Sensor Location Wrist", second_heart_rate.get(),
kBodySensorLocation,
BluetoothRemoteGattCharacteristic::PROPERTY_READ));
second_heart_rate->AddMockCharacteristic(
std::move(body_sensor_location_wrist));
device->AddMockService(std::move(second_heart_rate));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetDisconnectingHeartRateAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
auto device(GetHeartRateDevice(adapter.get()));
// TODO(ortuno): Implement the rest of the service's characteristics
// See: http://crbug.com/529975
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetHeartRateService(adapter.get(), device.get()));
device->AddMockService(GetDisconnectingService(adapter.get(), device.get()));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetServicesDiscoveredAfterReconnectionAdapter(
bool disconnect) {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
auto device(GetHeartRateDevice(adapter.get()));
NiceMockBluetoothDevice* device_ptr = device.get();
// When called before IsGattDiscoveryComplete, run success callback with a new
// Gatt connection. When called after after IsGattDiscoveryComplete runs
// success callback with a new Gatt connection and notifies of services
// discovered.
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(RunCallbackWithResult<0 /* success_callback */>(
[adapter_ptr, device_ptr]() {
std::vector<BluetoothRemoteGattService*> services =
device_ptr->GetMockServices();
if (services.size() != 0) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyServicesDiscovered,
base::RetainedRef(adapter_ptr), device_ptr));
}
device_ptr->SetConnected(true);
return std::make_unique<NiceMockBluetoothGattConnection>(
adapter_ptr, device_ptr->GetAddress());
}));
// The first time this function is called we:
// 1. Add a service (This indicates that this function has been called)
// 2. If |disconnect| is true, disconnect the device.
// 3. Return false.
// The second time this function is called we just return true.
ON_CALL(*device, IsGattServicesDiscoveryComplete())
.WillByDefault(Invoke([adapter_ptr, device_ptr, disconnect] {
std::vector<BluetoothRemoteGattService*> services =
device_ptr->GetMockServices();
if (services.size() == 0) {
auto heart_rate(GetBaseGATTService("Heart Rate", device_ptr,
kHeartRateServiceUUID));
device_ptr->AddMockService(GetGenericAccessService(device_ptr));
device_ptr->AddMockService(
GetHeartRateService(adapter_ptr, device_ptr));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
DCHECK(services.empty());
return false;
}
return true;
}));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter> WebTestBluetoothAdapterProvider::
GetGATTOperationFinishesAfterReconnectionAdapter(bool disconnect,
bool succeeds) {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
auto device(GetConnectableDevice(
adapter_ptr, "GATT Operation finishes after reconnection Device",
BluetoothDevice::UUIDList({BluetoothUUID(kGenericAccessServiceUUID),
BluetoothUUID(kHealthThermometerUUID)})));
NiceMockBluetoothDevice* device_ptr = device.get();
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr](
const BluetoothDevice::GattConnectionCallback& callback,
const BluetoothDevice::ConnectErrorCallback& error_callback) {
device_ptr->SetConnected(true);
callback.Run(std::make_unique<NiceMockBluetoothGattConnection>(
adapter_ptr, device_ptr->GetAddress()));
device_ptr->RunPendingCallbacks();
}));
device->AddMockService(GetGenericAccessService(device.get()));
auto health_thermometer(GetBaseGATTService("Health Thermometer", device.get(),
kHealthThermometerUUID));
// Measurement Interval
auto measurement_interval(GetBaseGATTCharacteristic(
"Measurement Interval", health_thermometer.get(),
kMeasurementIntervalUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE |
BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
NiceMockBluetoothGattCharacteristic* measurement_ptr =
measurement_interval.get();
ON_CALL(*measurement_interval, ReadRemoteCharacteristic_(_, _))
.WillByDefault(
Invoke([adapter_ptr, device_ptr, disconnect, succeeds](
BluetoothRemoteGattCharacteristic::ValueCallback& callback,
BluetoothRemoteGattCharacteristic::ErrorCallback&
error_callback) {
base::OnceClosure pending;
if (succeeds) {
pending = base::BindOnce(std::move(callback),
std::vector<uint8_t>({1}));
} else {
pending =
base::BindOnce(std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED);
}
device_ptr->PushPendingCallback(std::move(pending));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
}));
ON_CALL(*measurement_interval, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr, disconnect, succeeds](
const std::vector<uint8_t>& value, base::OnceClosure& callback,
BluetoothRemoteGattCharacteristic::ErrorCallback&
error_callback) {
base::OnceClosure pending;
if (succeeds) {
pending = std::move(callback);
} else {
pending =
base::BindOnce(std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED);
}
device_ptr->PushPendingCallback(std::move(pending));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
}));
ON_CALL(*measurement_interval, StartNotifySession_(_, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr, measurement_ptr, disconnect, succeeds](
const BluetoothRemoteGattCharacteristic::NotifySessionCallback&
callback,
BluetoothRemoteGattCharacteristic::ErrorCallback&
error_callback) {
base::OnceClosure pending;
if (succeeds) {
pending =
base::Bind(callback, base::Passed(GetBaseGATTNotifySession(
measurement_ptr->GetWeakPtr())));
} else {
pending =
base::BindOnce(std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED);
}
device_ptr->PushPendingCallback(std::move(pending));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
}));
auto user_descriptor = std::make_unique<NiceMockBluetoothGattDescriptor>(
measurement_interval.get(), kCharacteristicUserDescription,
BluetoothUUID(kUserDescriptionUUID), false,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
ON_CALL(*user_descriptor, ReadRemoteDescriptor_(_, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr, disconnect, succeeds](
BluetoothRemoteGattDescriptor::ValueCallback& callback,
BluetoothRemoteGattDescriptor::ErrorCallback& error_callback) {
base::OnceClosure pending;
if (succeeds) {
pending = base::BindOnce(std::move(callback),
std::vector<uint8_t>({1}));
} else {
pending =
base::BindOnce(std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED);
}
device_ptr->PushPendingCallback(std::move(pending));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
}));
ON_CALL(*user_descriptor, WriteRemoteDescriptor_(_, _, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr, disconnect, succeeds](
const std::vector<uint8_t>& value, base::OnceClosure& callback,
BluetoothRemoteGattDescriptor::ErrorCallback& error_callback) {
base::OnceClosure pending;
if (succeeds) {
pending = std::move(callback);
} else {
pending =
base::BindOnce(std::move(error_callback),
BluetoothRemoteGattService::GATT_ERROR_FAILED);
}
device_ptr->PushPendingCallback(std::move(pending));
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr), device_ptr));
}
}));
measurement_interval->AddMockDescriptor(std::move(user_descriptor));
health_thermometer->AddMockCharacteristic(std::move(measurement_interval));
device->AddMockService(std::move(health_thermometer));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter> WebTestBluetoothAdapterProvider::
GetStopNotifySessionFinishesAfterReconnectionAdapter(bool disconnect) {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
NiceMockBluetoothAdapter* adapter_ptr = adapter.get();
auto device(GetConnectableDevice(
adapter_ptr, "GATT Operation finishes after reconnection Device",
BluetoothDevice::UUIDList({BluetoothUUID(kGenericAccessServiceUUID),
BluetoothUUID(kHealthThermometerUUID)})));
NiceMockBluetoothDevice* device_ptr = device.get();
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(Invoke(
[adapter_ptr, device_ptr](
const BluetoothDevice::GattConnectionCallback& callback,
const BluetoothDevice::ConnectErrorCallback& error_callback) {
device_ptr->SetConnected(true);
callback.Run(std::make_unique<NiceMockBluetoothGattConnection>(
adapter_ptr, device_ptr->GetAddress()));
device_ptr->RunPendingCallbacks();
}));
device->AddMockService(GetGenericAccessService(device.get()));
auto health_thermometer(GetBaseGATTService("Health Thermometer", device.get(),
kHealthThermometerUUID));
// Measurement Interval
auto measurement_interval(GetBaseGATTCharacteristic(
"Measurement Interval", health_thermometer.get(),
kMeasurementIntervalUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
NiceMockBluetoothGattCharacteristic* measurement_ptr =
measurement_interval.get();
ON_CALL(*measurement_interval, StartNotifySession_(_, _))
.WillByDefault(RunCallbackWithResult<0 /* success_callback */>(
[adapter_ptr, device_ptr, measurement_ptr, disconnect]() {
std::unique_ptr<NiceMockBluetoothGattNotifySession> notify_session =
std::make_unique<NiceMockBluetoothGattNotifySession>(
measurement_ptr->GetWeakPtr());
ON_CALL(*notify_session, Stop(_))
.WillByDefault(Invoke([adapter_ptr, device_ptr, disconnect](
const base::Closure& callback) {
device_ptr->PushPendingCallback(callback);
if (disconnect) {
device_ptr->SetConnected(false);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&NotifyDeviceChanged,
base::RetainedRef(adapter_ptr),
device_ptr));
}
}));
return notify_session;
}));
health_thermometer->AddMockCharacteristic(std::move(measurement_interval));
device->AddMockService(std::move(health_thermometer));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetBlocklistTestAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(kBlocklistTestServiceUUID));
uuids.push_back(BluetoothUUID(kDeviceInformationServiceUUID));
uuids.push_back(BluetoothUUID(kGenericAccessServiceUUID));
uuids.push_back(BluetoothUUID(kHeartRateServiceUUID));
uuids.push_back(BluetoothUUID(kHumanInterfaceDeviceServiceUUID));
auto device(
GetConnectableDevice(adapter.get(), "Blocklist Test Device", uuids));
device->AddMockService(GetBlocklistTestService(device.get()));
device->AddMockService(GetDeviceInformationService(device.get()));
device->AddMockService(GetGenericAccessService(device.get()));
device->AddMockService(GetHeartRateService(adapter.get(), device.get()));
device->AddMockService(GetBaseGATTService("Human Interface Device",
device.get(),
kHumanInterfaceDeviceServiceUUID));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetFailingConnectionsAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
for (int error = 0; error < BluetoothDevice::NUM_CONNECT_ERROR_CODES;
error++) {
adapter->AddMockDevice(GetUnconnectableDevice(
adapter.get(), static_cast<BluetoothDevice::ConnectErrorCode>(error)));
}
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
WebTestBluetoothAdapterProvider::GetFailingGATTOperationsAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
const std::string errorsServiceUUID = errorUUID(0xA0);
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(errorsServiceUUID));
auto device(GetConnectableDevice(adapter.get(), "Errors Device", uuids));
device->AddMockService(GetDisconnectingService(adapter.get(), device.get()));
auto service(
GetBaseGATTService("Errors Service", device.get(), errorsServiceUUID));
for (int error = BluetoothRemoteGattService::GATT_ERROR_UNKNOWN;
error <= BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED; error++) {
service->AddMockCharacteristic(GetErrorCharacteristic(
service.get(),
static_cast<BluetoothRemoteGattService::GattErrorCode>(error)));
}
device->AddMockService(std::move(service));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// Discovery Sessions
// static
std::unique_ptr<NiceMockBluetoothDiscoverySession>
WebTestBluetoothAdapterProvider::GetDiscoverySession() {
auto discovery_session =
std::make_unique<NiceMockBluetoothDiscoverySession>();
ON_CALL(*discovery_session, Stop(_, _))
.WillByDefault(RunCallback<0 /* success_callback */>());
return discovery_session;
}
// Devices
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetBaseDevice(
MockBluetoothAdapter* adapter,
const char* device_name,
device::BluetoothDevice::UUIDList uuids,
const std::string& address) {
auto device = std::make_unique<NiceMockBluetoothDevice>(
adapter, 0x1F00 /* Bluetooth class */, device_name, address,
false /* paired */, false /* connected */);
for (const auto& uuid : uuids) {
device->AddUUID(uuid);
}
// Using Invoke allows the device returned from this method to be futher
// modified and have more services added to it. The call to ::GetGattServices
// will invoke ::GetMockServices, returning all services added up to that
// time.
ON_CALL(*device, GetGattServices())
.WillByDefault(
Invoke(device.get(), &MockBluetoothDevice::GetMockServices));
// The call to BluetoothDevice::GetGattService will invoke ::GetMockService
// which returns a service matching the identifier provided if the service
// was added to the mock.
ON_CALL(*device, GetGattService(_))
.WillByDefault(
Invoke(device.get(), &MockBluetoothDevice::GetMockService));
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(RunCallback<1 /* error_callback */>(
BluetoothDevice::ERROR_UNSUPPORTED_DEVICE));
return device;
}
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetBatteryDevice(
MockBluetoothAdapter* adapter) {
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(kBatteryServiceUUID));
return GetBaseDevice(adapter, "Battery Device", uuids, makeMACAddress(0x1));
}
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetGlucoseDevice(
MockBluetoothAdapter* adapter) {
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(kGenericAccessServiceUUID));
uuids.push_back(BluetoothUUID(kGlucoseServiceUUID));
uuids.push_back(BluetoothUUID(kTxPowerServiceUUID));
return GetBaseDevice(adapter, "Glucose Device", uuids, makeMACAddress(0x2));
}
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetConnectableDevice(
device::MockBluetoothAdapter* adapter,
const char* device_name,
BluetoothDevice::UUIDList uuids,
const std::string& address) {
auto device(GetBaseDevice(adapter, device_name, uuids, address));
MockBluetoothDevice* device_ptr = device.get();
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(RunCallbackWithResult<0 /* success_callback */>(
[adapter, device_ptr]() {
device_ptr->SetConnected(true);
return std::make_unique<NiceMockBluetoothGattConnection>(
adapter, device_ptr->GetAddress());
}));
ON_CALL(*device, IsGattServicesDiscoveryComplete())
.WillByDefault(Return(true));
return device;
}
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetUnconnectableDevice(
MockBluetoothAdapter* adapter,
BluetoothDevice::ConnectErrorCode error_code,
const char* device_name) {
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(errorUUID(error_code)));
auto device(
GetBaseDevice(adapter, device_name, uuids, makeMACAddress(error_code)));
ON_CALL(*device, CreateGattConnection(_, _))
.WillByDefault(RunCallback<1 /* error_callback */>(error_code));
return device;
}
// static
std::unique_ptr<NiceMockBluetoothDevice>
WebTestBluetoothAdapterProvider::GetHeartRateDevice(
MockBluetoothAdapter* adapter,
const char* device_name) {
BluetoothDevice::UUIDList uuids;
uuids.push_back(BluetoothUUID(kGenericAccessServiceUUID));
uuids.push_back(BluetoothUUID(kHeartRateServiceUUID));
return GetConnectableDevice(adapter, device_name, uuids);
}
// Services
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetBaseGATTService(
const std::string& identifier,
MockBluetoothDevice* device,
const std::string& uuid) {
auto service = std::make_unique<NiceMockBluetoothGattService>(
device, identifier, BluetoothUUID(uuid), true /* is_primary */,
false /* is_local */);
return service;
}
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetBlocklistTestService(
device::MockBluetoothDevice* device) {
auto blocklist_test_service(
GetBaseGATTService("Blocklist Test", device, kBlocklistTestServiceUUID));
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
blocklist_exclude_reads_characteristic(GetBaseGATTCharacteristic(
"Excluded Reads Characteristic", blocklist_test_service.get(),
kBlocklistExcludeReadsCharacteristicUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
// Crash if ReadRemoteCharacteristic called. Not using GoogleMock's Expect
// because this is used in web tests that may not report a mock expectation
// error correctly as a web test failure.
ON_CALL(*blocklist_exclude_reads_characteristic,
ReadRemoteCharacteristic_(_, _))
.WillByDefault(
Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&,
BluetoothRemoteGattCharacteristic::ErrorCallback&) {
NOTREACHED();
}));
// Write response.
ON_CALL(*blocklist_exclude_reads_characteristic,
WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(RunCallback<1 /* success callback */>());
blocklist_test_service->AddMockCharacteristic(
std::move(blocklist_exclude_reads_characteristic));
return blocklist_test_service;
}
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetDeviceInformationService(
device::MockBluetoothDevice* device) {
auto device_information(GetBaseGATTService("Device Information", device,
kDeviceInformationServiceUUID));
auto serial_number_string(GetBaseGATTCharacteristic(
"Serial Number String", device_information.get(), kSerialNumberStringUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ));
// Crash if ReadRemoteCharacteristic called. Not using GoogleMock's Expect
// because this is used in web tests that may not report a mock expectation
// error correctly as a web test failure.
ON_CALL(*serial_number_string, ReadRemoteCharacteristic_(_, _))
.WillByDefault(
Invoke([](BluetoothRemoteGattCharacteristic::ValueCallback&,
BluetoothRemoteGattCharacteristic::ErrorCallback&) {
NOTREACHED();
}));
device_information->AddMockCharacteristic(std::move(serial_number_string));
return device_information;
}
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetGenericAccessService(
device::MockBluetoothDevice* device) {
auto generic_access(
GetBaseGATTService("Generic Access", device, kGenericAccessServiceUUID));
{ // Device Name:
auto device_name(GetBaseGATTCharacteristic(
"Device Name", generic_access.get(), kDeviceNameUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
// Read response.
std::vector<uint8_t> device_name_value;
if (base::Optional<std::string> name = device->GetName())
device_name_value.assign(name.value().begin(), name.value().end());
ON_CALL(*device_name, ReadRemoteCharacteristic_(_, _))
.WillByDefault(RunCallback<0>(device_name_value));
// Write response.
ON_CALL(*device_name, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(RunCallback<1 /* success callback */>());
generic_access->AddMockCharacteristic(std::move(device_name));
}
{ // Peripheral Privacy Flag:
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
peripheral_privacy_flag(GetBaseGATTCharacteristic(
"Peripheral Privacy Flag", generic_access.get(),
kPeripheralPrivacyFlagUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
// Read response.
std::vector<uint8_t> value(1);
value[0] = false;
ON_CALL(*peripheral_privacy_flag, ReadRemoteCharacteristic_(_, _))
.WillByDefault(RunCallback<0>(value));
// Crash if WriteRemoteCharacteristic called. Not using GoogleMock's Expect
// because this is used in web tests that may not report a mock
// expectation error correctly as a web test failure.
ON_CALL(*peripheral_privacy_flag, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(
Invoke([](const std::vector<uint8_t>&, base::OnceClosure&,
BluetoothRemoteGattCharacteristic::ErrorCallback&) {
NOTREACHED();
}));
generic_access->AddMockCharacteristic(std::move(peripheral_privacy_flag));
}
return generic_access;
}
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetHeartRateService(
MockBluetoothAdapter* adapter,
MockBluetoothDevice* device) {
auto heart_rate(
GetBaseGATTService("Heart Rate", device, kHeartRateServiceUUID));
// Heart Rate Measurement
auto heart_rate_measurement(GetBaseGATTCharacteristic(
"Heart Rate Measurement", heart_rate.get(), kHeartRateMeasurementUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
NiceMockBluetoothGattCharacteristic* measurement_ptr =
heart_rate_measurement.get();
ON_CALL(*heart_rate_measurement, StartNotifySession_(_, _))
.WillByDefault(RunCallbackWithResult<0 /* success_callback */>(
[adapter, measurement_ptr]() {
auto notify_session(
GetBaseGATTNotifySession(measurement_ptr->GetWeakPtr()));
std::vector<uint8_t> rate(1 /* size */);
rate[0] = 60;
notify_session->StartTestNotifications(adapter, measurement_ptr,
rate);
return notify_session;
}));
// Body Sensor Location Characteristic (Chest)
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
body_sensor_location_chest(GetBaseGATTCharacteristic(
"Body Sensor Location Chest", heart_rate.get(), kBodySensorLocation,
BluetoothRemoteGattCharacteristic::PROPERTY_READ));
ON_CALL(*body_sensor_location_chest, ReadRemoteCharacteristic_(_, _))
.WillByDefault(RunCallback<0 /* success_callback */>(
std::vector<uint8_t>({1} /* Chest */)));
// Body Sensor Location Characteristic (Wrist)
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
body_sensor_location_wrist(GetBaseGATTCharacteristic(
"Body Sensor Location Wrist", heart_rate.get(), kBodySensorLocation,
BluetoothRemoteGattCharacteristic::PROPERTY_READ));
ON_CALL(*body_sensor_location_wrist, ReadRemoteCharacteristic_(_, _))
.WillByDefault(RunCallback<0 /* success_callback */>(
std::vector<uint8_t>({2} /* Wrist */)));
heart_rate->AddMockCharacteristic(std::move(heart_rate_measurement));
heart_rate->AddMockCharacteristic(std::move(body_sensor_location_chest));
heart_rate->AddMockCharacteristic(std::move(body_sensor_location_wrist));
return heart_rate;
}
// static
std::unique_ptr<NiceMockBluetoothGattService>
WebTestBluetoothAdapterProvider::GetDisconnectingService(
MockBluetoothAdapter* adapter,
MockBluetoothDevice* device) {
// Set up a service and a characteristic to disconnect the device when it's
// written to.
std::unique_ptr<NiceMockBluetoothGattService> disconnection_service =
GetBaseGATTService("Disconnection", device,
kRequestDisconnectionServiceUUID);
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
disconnection_characteristic(GetBaseGATTCharacteristic(
"Disconnection Characteristic", disconnection_service.get(),
kRequestDisconnectionCharacteristicUUID,
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE));
ON_CALL(*disconnection_characteristic, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(Invoke(
[adapter, device](
const std::vector<uint8_t>& value, base::OnceClosure& success,
BluetoothRemoteGattCharacteristic::ErrorCallback& error) {
device->SetConnected(false);
for (auto& observer : adapter->GetObservers())
observer.DeviceChanged(adapter, device);
std::move(success).Run();
}));
disconnection_service->AddMockCharacteristic(
std::move(disconnection_characteristic));
return disconnection_service;
}
// Characteristics
// static
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
WebTestBluetoothAdapterProvider::GetBaseGATTCharacteristic(
const std::string& identifier,
MockBluetoothGattService* service,
const std::string& uuid,
BluetoothRemoteGattCharacteristic::Properties properties) {
auto characteristic = std::make_unique<NiceMockBluetoothGattCharacteristic>(
service, identifier, BluetoothUUID(uuid), false /* is_local */,
properties, BluetoothGattCharacteristic::Permission::PERMISSION_NONE);
ON_CALL(*characteristic, ReadRemoteCharacteristic_(_, _))
.WillByDefault(
RunCallback<1>(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
ON_CALL(*characteristic, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(
RunCallback<2>(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
ON_CALL(*characteristic, StartNotifySession_(_, _))
.WillByDefault(
RunCallback<1>(BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED));
return characteristic;
}
// static
std::unique_ptr<NiceMockBluetoothGattCharacteristic>
WebTestBluetoothAdapterProvider::GetErrorCharacteristic(
MockBluetoothGattService* service,
BluetoothRemoteGattService::GattErrorCode error_code) {
uint32_t error_alias = error_code + 0xA1; // Error UUIDs start at 0xA1.
auto characteristic(GetBaseGATTCharacteristic(
// Use the UUID to generate unique identifiers.
"Error Characteristic " + errorUUID(error_alias), service,
errorUUID(error_alias),
BluetoothRemoteGattCharacteristic::PROPERTY_READ |
BluetoothRemoteGattCharacteristic::PROPERTY_WRITE |
BluetoothRemoteGattCharacteristic::PROPERTY_INDICATE));
// Read response.
ON_CALL(*characteristic, ReadRemoteCharacteristic_(_, _))
.WillByDefault(RunCallback<1 /* error_callback */>(error_code));
// Write response.
ON_CALL(*characteristic, WriteRemoteCharacteristic_(_, _, _))
.WillByDefault(RunCallback<2 /* error_callback */>(error_code));
// StartNotifySession response
ON_CALL(*characteristic, StartNotifySession_(_, _))
.WillByDefault(RunCallback<1 /* error_callback */>(error_code));
// Add error descriptor to |characteristic|
auto error_descriptor = std::make_unique<NiceMockBluetoothGattDescriptor>(
characteristic.get(), kCharacteristicUserDescription,
BluetoothUUID(kUserDescriptionUUID), false,
device::BluetoothRemoteGattCharacteristic::PROPERTY_READ);
ON_CALL(*error_descriptor, ReadRemoteDescriptor_(_, _))
.WillByDefault(RunCallback<1 /* error_callback */>(error_code));
ON_CALL(*error_descriptor, WriteRemoteDescriptor_(_, _, _))
.WillByDefault(RunCallback<2 /* error_callback */>(error_code));
characteristic->AddMockDescriptor(std::move(error_descriptor));
return characteristic;
}
// Notify sessions
// static
std::unique_ptr<NiceMockBluetoothGattNotifySession>
WebTestBluetoothAdapterProvider::GetBaseGATTNotifySession(
base::WeakPtr<device::BluetoothRemoteGattCharacteristic> characteristic) {
auto session =
std::make_unique<NiceMockBluetoothGattNotifySession>(characteristic);
ON_CALL(*session, Stop(_))
.WillByDefault(testing::DoAll(
InvokeWithoutArgs(
session.get(),
&MockBluetoothGattNotifySession::StopTestNotifications),
RunCallback<0>()));
return session;
}
// Helper functions
// static
std::string WebTestBluetoothAdapterProvider::errorUUID(uint32_t alias) {
return base::StringPrintf("%08x-97e5-4cd7-b9f1-f5a427670c59", alias);
}
// static
std::string WebTestBluetoothAdapterProvider::makeMACAddress(uint64_t addr) {
return BluetoothDevice::CanonicalizeAddress(
base::StringPrintf("%012" PRIx64, addr));
}
} // namespace content