#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/floss/bluetooth_adapter_floss.h"
#include "device/bluetooth/floss/bluetooth_device_floss.h"
#include "device/bluetooth/floss/bluetooth_remote_gatt_characteristic_floss.h"
#include "device/bluetooth/floss/bluetooth_remote_gatt_descriptor_floss.h"
#include "device/bluetooth/floss/bluetooth_remote_gatt_service_floss.h"
#include "device/bluetooth/floss/fake_floss_adapter_client.h"
#include "device/bluetooth/floss/fake_floss_gatt_manager_client.h"
#include "device/bluetooth/floss/fake_floss_manager_client.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr int kGattClientId = 39;
constexpr int kUseThisAdapter = 0;
constexpr char kFakeUuidShort[] = "1812";
}
namespace floss {
using FlossCharacteristic = floss::GattCharacteristic;
class BluetoothGattFlossTest : public testing::Test {
public:
void SetUp() override {
FlossDBusManager::GetSetterForTesting();
InitializeAdapter();
EnableAdapter();
SetClientRegistered();
}
FakeFlossManagerClient* GetFakeManagerClient() {
return static_cast<FakeFlossManagerClient*>(
FlossDBusManager::Get()->GetManagerClient());
}
FakeFlossAdapterClient* GetFakeAdapterClient() {
return static_cast<FakeFlossAdapterClient*>(
floss::FlossDBusManager::Get()->GetAdapterClient());
}
FakeFlossGattManagerClient* GetFakeGattManagerClient() {
return static_cast<FakeFlossGattManagerClient*>(
FlossDBusManager::Get()->GetGattManagerClient());
}
void InitializeAdapter() {
adapter_ = BluetoothAdapterFloss::CreateAdapter();
GetFakeManagerClient()->SetDefaultEnabled(true);
base::RunLoop run_loop;
adapter_->Initialize(run_loop.QuitClosure());
run_loop.Run();
ASSERT_TRUE(adapter_);
ASSERT_TRUE(adapter_->IsInitialized());
}
void EnableAdapter() {
ASSERT_TRUE(adapter_.get() != nullptr);
GetFakeManagerClient()->NotifyObservers(
base::BindLambdaForTesting([](FlossManagerClient::Observer* observer) {
observer->AdapterEnabledChanged(kUseThisAdapter,
true);
}));
GetFakeAdapterClient()->SetConnected(
FakeFlossAdapterClient::kBondedAddress1, true);
base::RunLoop().RunUntilIdle();
}
void DiscoverDevices() {
ASSERT_TRUE(adapter_.get() != nullptr);
adapter_->StartDiscoverySession(
std::string(), base::DoNothing(), base::DoNothing());
}
void SetClientRegistered() {
GetFakeGattManagerClient()->GattClientRegistered(GattStatus::kSuccess,
kGattClientId);
}
void SetAclConnectionState(std::string address, bool connected) {
FlossDeviceId device;
device.address = address;
GetFakeAdapterClient()->NotifyObservers(base::BindLambdaForTesting(
[&connected, &device](FlossAdapterClient::Observer* observer) {
if (connected) {
observer->AdapterDeviceConnected(device);
} else {
observer->AdapterDeviceDisconnected(device);
}
}));
}
void SetGattConnectionState(GattStatus status,
bool connected,
std::string address) {
GetFakeGattManagerClient()->GattClientConnectionState(status, kGattClientId,
connected, address);
}
void SetGattSearchComplete(std::string address,
const std::vector<GattService>& services,
GattStatus status) {
GetFakeGattManagerClient()->GattSearchComplete(address, services, status);
}
void SetGattConfigureMtu(std::string address,
int32_t mtu,
GattStatus status) {
GetFakeGattManagerClient()->GattConfigureMtu(address, mtu, status);
}
GattService CreateFakeServiceFor(const device::BluetoothUUID& uuid) {
GattService underlying_service;
underlying_service.uuid = uuid;
underlying_service.instance_id = 1;
underlying_service.service_type = 0;
return underlying_service;
}
base::test::SingleThreadTaskEnvironment task_environment_;
scoped_refptr<device::BluetoothAdapter> adapter_;
};
TEST_F(BluetoothGattFlossTest, ConnectAndResolveServices) {
device::BluetoothDevice* paired_device =
adapter_->GetDevice(FakeFlossAdapterClient::kBondedAddress1);
ASSERT_TRUE(paired_device != nullptr);
base::RunLoop loop;
paired_device->CreateGattConnection(
base::BindLambdaForTesting(
[&paired_device, &loop](
std::unique_ptr<device::BluetoothGattConnection> conn,
std::optional<device::BluetoothDevice::ConnectErrorCode> error) {
EXPECT_FALSE(error.has_value());
EXPECT_TRUE(conn->IsConnected());
EXPECT_EQ(paired_device->GetAddress(), conn->GetDeviceAddress());
loop.Quit();
}),
std::nullopt);
SetAclConnectionState(paired_device->GetAddress(), true);
SetGattConnectionState(GattStatus::kSuccess, true,
paired_device->GetAddress());
EXPECT_TRUE(paired_device->IsConnected());
EXPECT_FALSE(paired_device->IsGattServicesDiscoveryComplete());
SetGattSearchComplete(paired_device->GetAddress(), {},
GattStatus::kSuccess);
EXPECT_TRUE(paired_device->IsConnected());
EXPECT_TRUE(paired_device->IsGattServicesDiscoveryComplete());
loop.RunUntilIdle();
}
TEST_F(BluetoothGattFlossTest, UpgradeToFullDiscovery) {
device::BluetoothDevice* paired_device =
adapter_->GetDevice(FakeFlossAdapterClient::kBondedAddress1);
ASSERT_TRUE(paired_device != nullptr);
device::BluetoothUUID fake_uuid(kFakeUuidShort);
std::optional<device::BluetoothUUID> fake_uuid_optional = fake_uuid;
GattService fake_service = CreateFakeServiceFor(fake_uuid);
paired_device->CreateGattConnection(base::DoNothing(), fake_uuid_optional);
SetGattConfigureMtu(paired_device->GetAddress(), 500, GattStatus::kSuccess);
SetGattConnectionState(GattStatus::kSuccess, true,
paired_device->GetAddress());
EXPECT_TRUE(paired_device->IsConnected());
EXPECT_FALSE(paired_device->IsGattServicesDiscoveryComplete());
SetGattSearchComplete(paired_device->GetAddress(),
{fake_service}, GattStatus::kSuccess);
EXPECT_TRUE(paired_device->IsConnected());
EXPECT_FALSE(paired_device->IsGattServicesDiscoveryComplete());
paired_device->CreateGattConnection(base::DoNothing(),
std::nullopt);
EXPECT_FALSE(paired_device->IsGattServicesDiscoveryComplete());
SetGattSearchComplete(paired_device->GetAddress(),
{fake_service}, GattStatus::kSuccess);
EXPECT_TRUE(paired_device->IsGattServicesDiscoveryComplete());
base::RunLoop().RunUntilIdle();
}
TEST_F(BluetoothGattFlossTest, TranslateReadWriteAuthentication) {
std::vector<std::pair<std::pair<uint32_t, uint32_t>, AuthRequired>>
property_to_auth_read_map = {
{{0, 0}, AuthRequired::kNoAuth},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_READ, 0},
AuthRequired::kNoAuth},
{{
FlossCharacteristic::GATT_CHAR_PROP_BIT_READ,
FlossCharacteristic::GATT_PERM_READ_ENCRYPTED,
},
AuthRequired::kNoMitm},
{{
FlossCharacteristic::GATT_CHAR_PROP_BIT_READ,
FlossCharacteristic::GATT_PERM_READ_ENC_MITM,
},
AuthRequired::kReqMitm},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_READ,
FlossCharacteristic::GATT_PERM_READ_ENCRYPTED |
FlossCharacteristic::GATT_PERM_READ_ENC_MITM},
AuthRequired::kReqMitm},
};
std::vector<std::pair<std::pair<uint32_t, uint32_t>, AuthRequired>>
property_to_auth_write_map = {
{{0, 0}, AuthRequired::kNoAuth},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE, 0},
AuthRequired::kNoAuth},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_SIGNED},
AuthRequired::kNoAuth},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENCRYPTED},
AuthRequired::kNoMitm},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENC_MITM},
AuthRequired::kReqMitm},
{{
FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENCRYPTED |
FlossCharacteristic::GATT_PERM_WRITE_ENC_MITM,
},
AuthRequired::kReqMitm},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENCRYPTED |
FlossCharacteristic::GATT_PERM_WRITE_SIGNED},
AuthRequired::kSignedNoMitm},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENC_MITM |
FlossCharacteristic::GATT_PERM_WRITE_SIGNED},
AuthRequired::kSignedReqMitm},
{{FlossCharacteristic::GATT_CHAR_PROP_BIT_WRITE,
FlossCharacteristic::GATT_PERM_WRITE_ENCRYPTED |
FlossCharacteristic::GATT_PERM_WRITE_ENC_MITM |
FlossCharacteristic::GATT_PERM_WRITE_SIGNED},
AuthRequired::kSignedReqMitm},
};
device::BluetoothDevice* device =
adapter_->GetDevice(FakeFlossAdapterClient::kBondedAddress1);
GattService underlying_service;
underlying_service.uuid = device::BluetoothUUID(kFakeUuidShort);
underlying_service.instance_id = 1;
underlying_service.service_type = 0;
auto service = BluetoothRemoteGattServiceFloss::Create(
static_cast<BluetoothAdapterFloss*>(adapter_.get()),
static_cast<BluetoothDeviceFloss*>(device), underlying_service);
for (const auto& [pair, auth] : property_to_auth_read_map) {
const auto& [props, perms] = pair;
GattCharacteristic tmp;
tmp.uuid = device::BluetoothUUID("1912");
tmp.instance_id = 2;
tmp.properties = props;
tmp.permissions = perms;
auto characteristic =
BluetoothRemoteGattCharacteristicFloss::Create(service.get(), &tmp);
EXPECT_EQ(characteristic->GetAuthForRead(), auth);
}
for (const auto& [pair, auth] : property_to_auth_write_map) {
const auto& [props, perms] = pair;
GattCharacteristic tmp;
tmp.uuid = device::BluetoothUUID("1912");
tmp.instance_id = 2;
tmp.properties = props;
tmp.permissions = perms;
auto characteristic =
BluetoothRemoteGattCharacteristicFloss::Create(service.get(), &tmp);
EXPECT_EQ(characteristic->GetAuthForWrite(), auth);
}
}
TEST_F(BluetoothGattFlossTest, VerifyAllIdentifiers) {
device::BluetoothDevice* device =
adapter_->GetDevice(FakeFlossAdapterClient::kBondedAddress1);
GattService underlying_service;
underlying_service.uuid = device::BluetoothUUID(kFakeUuidShort);
underlying_service.instance_id = 16;
underlying_service.service_type = 0;
auto service = BluetoothRemoteGattServiceFloss::Create(
static_cast<BluetoothAdapterFloss*>(adapter_.get()),
static_cast<BluetoothDeviceFloss*>(device), underlying_service);
EXPECT_EQ(service->GetIdentifier(),
base::StringPrintf("%s-%s/%04x", device->GetAddress().c_str(),
service->GetUUID().value().c_str(), 16));
GattCharacteristic underlying_characteristic;
underlying_characteristic.uuid = device::BluetoothUUID(kFakeUuidShort);
underlying_characteristic.instance_id = 47;
auto characteristic = BluetoothRemoteGattCharacteristicFloss::Create(
service.get(), &underlying_characteristic);
EXPECT_EQ(characteristic->GetIdentifier(),
base::StringPrintf("%s-%s/%04x/%04x", device->GetAddress().c_str(),
service->GetUUID().value().c_str(), 16, 47));
GattDescriptor underlying_descriptor;
underlying_descriptor.uuid = device::BluetoothUUID(kFakeUuidShort);
underlying_descriptor.instance_id = 72;
auto descriptor = BluetoothRemoteGattDescriptorFloss::Create(
service.get(), characteristic.get(), &underlying_descriptor);
EXPECT_EQ(
descriptor->GetIdentifier(),
base::StringPrintf("%s-%s/%04x/%04x/%04x", device->GetAddress().c_str(),
service->GetUUID().value().c_str(), 16, 47, 72));
}
}