blob: 38facdd31125c609ace81ef69a14cdb8d7e3bc8a [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 "chromecast/device/bluetooth/le/gatt_client_manager_impl.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromecast/device/bluetooth/bluetooth_util.h"
#include "chromecast/device/bluetooth/le/remote_characteristic.h"
#include "chromecast/device/bluetooth/le/remote_descriptor.h"
#include "chromecast/device/bluetooth/le/remote_device_impl.h"
#include "chromecast/device/bluetooth/le/remote_service.h"
#include "chromecast/device/bluetooth/shlib/mock_gatt_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Return;
namespace chromecast {
namespace bluetooth {
namespace {
const bluetooth_v2_shlib::Addr kTestAddr1 = {
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
const bluetooth_v2_shlib::Addr kTestAddr2 = {
{0x10, 0x11, 0x12, 0x13, 0x14, 0x15}};
const bluetooth_v2_shlib::Addr kTestAddr3 = {
{0x20, 0x21, 0x22, 0x23, 0x24, 0x25}};
const bluetooth_v2_shlib::Addr kTestAddr4 = {
{0x30, 0x31, 0x32, 0x33, 0x34, 0x35}};
class MockGattClientManagerObserver : public GattClientManager::Observer {
public:
MOCK_METHOD2(OnConnectChanged,
void(scoped_refptr<RemoteDevice> device, bool connected));
MOCK_METHOD2(OnMtuChanged, void(scoped_refptr<RemoteDevice> device, int mtu));
MOCK_METHOD2(OnServicesUpdated,
void(scoped_refptr<RemoteDevice> device,
std::vector<scoped_refptr<RemoteService>> services));
MOCK_METHOD3(OnCharacteristicNotification,
void(scoped_refptr<RemoteDevice> device,
scoped_refptr<RemoteCharacteristic> characteristic,
std::vector<uint8_t> value));
};
std::vector<bluetooth_v2_shlib::Gatt::Service> GenerateServices() {
std::vector<bluetooth_v2_shlib::Gatt::Service> ret;
bluetooth_v2_shlib::Gatt::Service service;
bluetooth_v2_shlib::Gatt::Characteristic characteristic;
bluetooth_v2_shlib::Gatt::Descriptor descriptor;
service.uuid = {{0x1}};
service.handle = 0x1;
service.primary = true;
characteristic.uuid = {{0x1, 0x1}};
characteristic.handle = 0x2;
characteristic.permissions =
static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
bluetooth_v2_shlib::Gatt::PERMISSION_READ |
bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_NOTIFY;
descriptor.uuid = {{0x1, 0x1, 0x1}};
descriptor.handle = 0x3;
descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
bluetooth_v2_shlib::Gatt::PERMISSION_READ |
bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
characteristic.descriptors.push_back(descriptor);
descriptor.uuid = RemoteDescriptor::kCccdUuid;
descriptor.handle = 0x4;
descriptor.permissions = static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
bluetooth_v2_shlib::Gatt::PERMISSION_READ |
bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
characteristic.descriptors.push_back(descriptor);
service.characteristics.push_back(characteristic);
characteristic.uuid = {{0x1, 0x2}};
characteristic.handle = 0x5;
characteristic.permissions =
static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
bluetooth_v2_shlib::Gatt::PERMISSION_READ |
bluetooth_v2_shlib::Gatt::PERMISSION_WRITE);
characteristic.properties =
static_cast<bluetooth_v2_shlib::Gatt::Properties>(0);
characteristic.descriptors.clear();
service.characteristics.push_back(characteristic);
ret.push_back(service);
service.uuid = {{0x2}};
service.handle = 0x6;
service.primary = true;
service.characteristics.clear();
ret.push_back(service);
return ret;
}
class GattClientManagerTest : public ::testing::Test {
public:
void SetUp() override {
fake_task_runner_ = new base::TestMockTimeTaskRunner();
message_loop_ =
std::make_unique<base::MessageLoop>(base::MessageLoop::TYPE_DEFAULT);
message_loop_->SetTaskRunner(fake_task_runner_);
gatt_client_ = std::make_unique<bluetooth_v2_shlib::MockGattClient>();
gatt_client_manager_ =
std::make_unique<GattClientManagerImpl>(gatt_client_.get());
observer_ = std::make_unique<MockGattClientManagerObserver>();
// Normally bluetooth_manager does this.
gatt_client_->SetDelegate(gatt_client_manager_.get());
gatt_client_manager_->Initialize(fake_task_runner_);
gatt_client_manager_->AddObserver(observer_.get());
}
void TearDown() override {
gatt_client_->SetDelegate(nullptr);
gatt_client_manager_->RemoveObserver(observer_.get());
gatt_client_manager_->Finalize();
fake_task_runner_ = nullptr;
}
scoped_refptr<RemoteDevice> GetDevice(const bluetooth_v2_shlib::Addr& addr) {
scoped_refptr<RemoteDevice> ret;
gatt_client_manager_->GetDevice(
addr, base::BindOnce(
[](scoped_refptr<RemoteDevice>* ret_ptr,
scoped_refptr<RemoteDevice> result) { *ret_ptr = result; },
&ret));
return ret;
}
std::vector<scoped_refptr<RemoteService>> GetServices(RemoteDevice* device) {
std::vector<scoped_refptr<RemoteService>> ret;
device->GetServices(base::BindOnce(
[](std::vector<scoped_refptr<RemoteService>>* ret_ptr,
std::vector<scoped_refptr<RemoteService>> result) {
*ret_ptr = result;
},
&ret));
return ret;
}
scoped_refptr<RemoteService> GetServiceByUuid(
RemoteDevice* device,
const bluetooth_v2_shlib::Uuid& uuid) {
scoped_refptr<RemoteService> ret;
device->GetServiceByUuid(uuid, base::BindOnce(
[](scoped_refptr<RemoteService>* ret_ptr,
scoped_refptr<RemoteService> result) {
*ret_ptr = result;
},
&ret));
return ret;
}
void Connect(const bluetooth_v2_shlib::Addr& addr) {
EXPECT_CALL(*gatt_client_, Connect(addr)).WillOnce(Return(true));
scoped_refptr<RemoteDevice> device = GetDevice(addr);
EXPECT_CALL(cb_, Run(true));
device->Connect(cb_.Get());
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true));
delegate->OnConnectChanged(addr, true /* status */, true /* connected */);
delegate->OnGetServices(addr, {});
ASSERT_TRUE(device->IsConnected());
}
scoped_refptr<base::TestMockTimeTaskRunner> fake_task_runner_;
base::MockCallback<RemoteDevice::StatusCallback> cb_;
std::unique_ptr<base::MessageLoop> message_loop_;
std::unique_ptr<GattClientManagerImpl> gatt_client_manager_;
std::unique_ptr<bluetooth_v2_shlib::MockGattClient> gatt_client_;
std::unique_ptr<MockGattClientManagerObserver> observer_;
};
} // namespace
TEST_F(GattClientManagerTest, RemoteDeviceConnect) {
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
EXPECT_FALSE(device->IsConnected());
EXPECT_EQ(kTestAddr1, device->addr());
// These should fail if we're not connected.
EXPECT_CALL(cb_, Run(false));
device->Disconnect(cb_.Get());
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb;
EXPECT_CALL(rssi_cb, Run(false, _));
device->ReadRemoteRssi(rssi_cb.Get());
EXPECT_CALL(cb_, Run(false));
device->RequestMtu(512, cb_.Get());
EXPECT_CALL(cb_, Run(false));
device->ConnectionParameterUpdate(10, 10, 50, 100, cb_.Get());
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
device->Connect(cb_.Get());
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
true /* connected */);
EXPECT_CALL(*observer_, OnConnectChanged(device, true));
delegate->OnGetServices(kTestAddr1, {});
EXPECT_TRUE(device->IsConnected());
base::MockCallback<
base::OnceCallback<void(std::vector<scoped_refptr<RemoteDevice>>)>>
get_connected_callback;
const std::vector<scoped_refptr<RemoteDevice>> kExpectedDevices({device});
EXPECT_CALL(get_connected_callback, Run(kExpectedDevices));
gatt_client_manager_->GetConnectedDevices(get_connected_callback.Get());
base::RunLoop().RunUntilIdle();
EXPECT_CALL(*gatt_client_, Disconnect(kTestAddr1)).WillOnce(Return(true));
device->Disconnect({});
EXPECT_TRUE(device->IsConnected());
EXPECT_CALL(*observer_, OnConnectChanged(device, false));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
false /* connected */);
EXPECT_FALSE(device->IsConnected());
fake_task_runner_->RunUntilIdle();
}
TEST_F(GattClientManagerTest, RemoteDeviceConnectConcurrent) {
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device1 = GetDevice(kTestAddr1);
scoped_refptr<RemoteDevice> device2 = GetDevice(kTestAddr2);
scoped_refptr<RemoteDevice> device3 = GetDevice(kTestAddr3);
scoped_refptr<RemoteDevice> device4 = GetDevice(kTestAddr4);
base::MockCallback<RemoteDevice::StatusCallback> cb1;
base::MockCallback<RemoteDevice::StatusCallback> cb2;
base::MockCallback<RemoteDevice::StatusCallback> cb3;
base::MockCallback<RemoteDevice::StatusCallback> cb4;
// Only the 1st Connect request will be executed immediately. The rest will be
// queued.
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
device1->Connect(cb1.Get());
device2->Connect(cb2.Get());
device3->Connect(cb3.Get());
device4->Connect(cb4.Get());
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
true /* connected */);
// Queued Connect requests will not be called until we receive OnGetServices
// of the current Connect request if it is successful.
EXPECT_CALL(cb1, Run(true));
EXPECT_CALL(*gatt_client_, Connect(kTestAddr2)).WillOnce(Return(false));
EXPECT_CALL(cb2, Run(false));
// If the Connect request fails in the initial request (not in the callback),
// the next queued request will be executed immediately.
EXPECT_CALL(*gatt_client_, Connect(kTestAddr3)).WillOnce(Return(true));
delegate->OnGetServices(kTestAddr1, {});
EXPECT_CALL(cb3, Run(false));
EXPECT_CALL(*gatt_client_, Connect(kTestAddr4)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr3, true /* status */,
false /* connected */);
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr4)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr4, true /* status */,
true /* connected */);
EXPECT_CALL(cb4, Run(true));
delegate->OnGetServices(kTestAddr4, {});
EXPECT_TRUE(device1->IsConnected());
EXPECT_FALSE(device2->IsConnected());
EXPECT_FALSE(device3->IsConnected());
EXPECT_TRUE(device4->IsConnected());
base::MockCallback<base::OnceCallback<void(size_t)>>
get_num_connected_callback;
EXPECT_CALL(get_num_connected_callback, Run(2));
gatt_client_manager_->GetNumConnected(get_num_connected_callback.Get());
base::RunLoop().RunUntilIdle();
}
TEST_F(GattClientManagerTest, ConnectTimeout) {
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
// Issue a Connect request
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
device->Connect(cb_.Get());
// Let Connect request timeout
base::TestMockTimeTaskRunner::ScopedContext context(fake_task_runner_);
// We should expect to receive Connect failure message
EXPECT_CALL(cb_, Run(false));
fake_task_runner_->FastForwardBy(GattClientManagerImpl::kConnectTimeout);
EXPECT_FALSE(device->IsConnected());
}
TEST_F(GattClientManagerTest, GetServicesTimeout) {
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
// Issue a Connect request and let Connect succeed
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
device->Connect(cb_.Get());
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
true /* connected */);
// Let GetServices request timeout
base::TestMockTimeTaskRunner::ScopedContext context(fake_task_runner_);
// We should request a disconnect.
EXPECT_CALL(*gatt_client_, Disconnect(kTestAddr1)).WillOnce(Return(true));
fake_task_runner_->FastForwardBy(GattClientManagerImpl::kConnectTimeout);
// Make sure we issued a disconnect.
testing::Mock::VerifyAndClearExpectations(gatt_client_.get());
EXPECT_CALL(cb_, Run(false));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
false /* connected */);
EXPECT_FALSE(device->IsConnected());
}
TEST_F(GattClientManagerTest, RemoteDeviceReadRssi) {
static const int kRssi = -34;
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
Connect(kTestAddr1);
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb;
EXPECT_CALL(*gatt_client_, ReadRemoteRssi(kTestAddr1)).WillOnce(Return(true));
device->ReadRemoteRssi(rssi_cb.Get());
EXPECT_CALL(rssi_cb, Run(true, kRssi));
delegate->OnReadRemoteRssi(kTestAddr1, true /* status */, kRssi);
}
TEST_F(GattClientManagerTest, ReadRemoteRssiTimeout) {
static const int kRssi = -34;
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
Connect(kTestAddr1);
// Issue a ReadRemoteRssi request.
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb;
EXPECT_CALL(*gatt_client_, ReadRemoteRssi(kTestAddr1)).WillOnce(Return(true));
device->ReadRemoteRssi(rssi_cb.Get());
// Let ReadRemoteRssi request timeout.
base::TestMockTimeTaskRunner::ScopedContext context(fake_task_runner_);
// We should expect to receive ReadRemoteRssi failure message.
EXPECT_CALL(rssi_cb, Run(false, 0));
fake_task_runner_->FastForwardBy(
GattClientManagerImpl::kReadRemoteRssiTimeout);
// The following callback should be ignored.
delegate->OnReadRemoteRssi(kTestAddr1, true /* status */, kRssi);
// Device should remain connected.
EXPECT_TRUE(device->IsConnected());
}
TEST_F(GattClientManagerTest, RemoteDeviceReadRssiConcurrent) {
static const int kRssi1 = -34;
static const int kRssi3 = -68;
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device1 = GetDevice(kTestAddr1);
scoped_refptr<RemoteDevice> device2 = GetDevice(kTestAddr2);
scoped_refptr<RemoteDevice> device3 = GetDevice(kTestAddr3);
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb1;
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb2;
base::MockCallback<RemoteDevice::RssiCallback> rssi_cb3;
// Only the 1st ReadRemoteRssi request will be executed immediately. The rest
// will be queued.
EXPECT_CALL(*gatt_client_, ReadRemoteRssi(kTestAddr1)).WillOnce(Return(true));
device1->ReadRemoteRssi(rssi_cb1.Get());
device2->ReadRemoteRssi(rssi_cb2.Get());
device3->ReadRemoteRssi(rssi_cb3.Get());
// Queued ReadRemoteRssi requests will not be called until we receive
// OnGetServices of the current Connect request if it is successful.
EXPECT_CALL(rssi_cb1, Run(true, kRssi1));
EXPECT_CALL(*gatt_client_, ReadRemoteRssi(kTestAddr2))
.WillOnce(Return(false));
EXPECT_CALL(rssi_cb2, Run(false, _));
// If the ReadRemoteRssi request fails in the initial request (not in the
// callback), the next queued request will be executed immediately.
EXPECT_CALL(*gatt_client_, ReadRemoteRssi(kTestAddr3)).WillOnce(Return(true));
delegate->OnReadRemoteRssi(kTestAddr1, true, kRssi1);
EXPECT_CALL(rssi_cb3, Run(true, kRssi3));
delegate->OnReadRemoteRssi(kTestAddr3, true, kRssi3);
}
TEST_F(GattClientManagerTest, RemoteDeviceRequestMtu) {
static const int kMtu = 512;
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
Connect(kTestAddr1);
EXPECT_EQ(RemoteDevice::kDefaultMtu, device->GetMtu());
EXPECT_CALL(*gatt_client_, RequestMtu(kTestAddr1, kMtu))
.WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
device->RequestMtu(kMtu, cb_.Get());
EXPECT_CALL(*observer_, OnMtuChanged(device, kMtu));
delegate->OnMtuChanged(kTestAddr1, true, kMtu);
EXPECT_EQ(kMtu, device->GetMtu());
fake_task_runner_->RunUntilIdle();
}
TEST_F(GattClientManagerTest, RemoteDeviceConnectionParameterUpdate) {
const int kMinInterval = 10;
const int kMaxInterval = 10;
const int kLatency = 50;
const int kTimeout = 100;
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
EXPECT_CALL(*gatt_client_,
ConnectionParameterUpdate(kTestAddr1, kMinInterval, kMaxInterval,
kLatency, kTimeout))
.WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
device->ConnectionParameterUpdate(kMinInterval, kMaxInterval, kLatency,
kTimeout, cb_.Get());
}
TEST_F(GattClientManagerTest, RemoteDeviceServices) {
const auto kServices = GenerateServices();
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
std::vector<scoped_refptr<RemoteService>> services;
EXPECT_EQ(0ul, GetServices(device.get()).size());
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(kServices.size(), GetServices(device.get()).size());
for (const auto& service : kServices) {
scoped_refptr<RemoteService> remote_service =
GetServiceByUuid(device.get(), service.uuid);
ASSERT_TRUE(remote_service);
EXPECT_EQ(service.uuid, remote_service->uuid());
EXPECT_EQ(service.handle, remote_service->handle());
EXPECT_EQ(service.primary, remote_service->primary());
EXPECT_EQ(service.characteristics.size(),
remote_service->GetCharacteristics().size());
for (const auto& characteristic : service.characteristics) {
scoped_refptr<RemoteCharacteristic> remote_char =
remote_service->GetCharacteristicByUuid(characteristic.uuid);
ASSERT_TRUE(remote_char);
EXPECT_EQ(characteristic.uuid, remote_char->uuid());
EXPECT_EQ(characteristic.handle, remote_char->handle());
EXPECT_EQ(characteristic.permissions, remote_char->permissions());
EXPECT_EQ(characteristic.properties, remote_char->properties());
EXPECT_EQ(characteristic.descriptors.size(),
remote_char->GetDescriptors().size());
for (const auto& descriptor : characteristic.descriptors) {
scoped_refptr<RemoteDescriptor> remote_desc =
remote_char->GetDescriptorByUuid(descriptor.uuid);
ASSERT_TRUE(remote_desc);
EXPECT_EQ(descriptor.uuid, remote_desc->uuid());
EXPECT_EQ(descriptor.handle, remote_desc->handle());
EXPECT_EQ(descriptor.permissions, remote_desc->permissions());
}
}
}
}
TEST_F(GattClientManagerTest, RemoteDeviceCharacteristic) {
const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
const std::vector<uint8_t> kTestData2 = {0x4, 0x5, 0x6};
const std::vector<uint8_t> kTestData3 = {0x7, 0x8, 0x9};
const auto kServices = GenerateServices();
const bluetooth_v2_shlib::Gatt::Client::AuthReq kAuthReq =
bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_MITM;
const bluetooth_v2_shlib::Gatt::WriteType kWriteType =
bluetooth_v2_shlib::Gatt::WRITE_TYPE_DEFAULT;
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(kServices.size(), services.size());
auto service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_GE(characteristics.size(), 1ul);
auto characteristic = characteristics[0];
EXPECT_CALL(*gatt_client_,
WriteCharacteristic(kTestAddr1, characteristic->characteristic(),
kAuthReq, kWriteType, kTestData1))
.WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
characteristic->WriteAuth(kAuthReq, kWriteType, kTestData1, cb_.Get());
delegate->OnCharacteristicWriteResponse(kTestAddr1, true,
characteristic->handle());
EXPECT_CALL(*gatt_client_,
ReadCharacteristic(kTestAddr1, characteristic->characteristic(),
kAuthReq))
.WillOnce(Return(true));
base::MockCallback<RemoteCharacteristic::ReadCallback> read_cb;
EXPECT_CALL(read_cb, Run(true, kTestData2));
characteristic->ReadAuth(kAuthReq, read_cb.Get());
delegate->OnCharacteristicReadResponse(kTestAddr1, true,
characteristic->handle(), kTestData2);
EXPECT_CALL(*gatt_client_,
SetCharacteristicNotification(
kTestAddr1, characteristic->characteristic(), true))
.WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
characteristic->SetNotification(true, cb_.Get());
EXPECT_CALL(*observer_,
OnCharacteristicNotification(device, characteristic, kTestData3));
delegate->OnNotification(kTestAddr1, characteristic->handle(), kTestData3);
fake_task_runner_->RunUntilIdle();
}
TEST_F(GattClientManagerTest,
RemoteDeviceCharacteristicSetRegisterNotification) {
const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
const auto kServices = GenerateServices();
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(kServices.size(), services.size());
scoped_refptr<RemoteService> service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_GE(characteristics.size(), 1ul);
scoped_refptr<RemoteCharacteristic> characteristic = characteristics[0];
scoped_refptr<RemoteDescriptor> cccd =
characteristic->GetDescriptorByUuid(RemoteDescriptor::kCccdUuid);
ASSERT_TRUE(cccd);
EXPECT_CALL(*gatt_client_,
SetCharacteristicNotification(
kTestAddr1, characteristic->characteristic(), true))
.WillOnce(Return(true));
std::vector<uint8_t> cccd_enable_notification = {
std::begin(bluetooth::RemoteDescriptor::kEnableNotificationValue),
std::end(bluetooth::RemoteDescriptor::kEnableNotificationValue)};
EXPECT_CALL(*gatt_client_, WriteDescriptor(kTestAddr1, cccd->descriptor(), _,
cccd_enable_notification))
.WillOnce(Return(true));
characteristic->SetRegisterNotification(true, cb_.Get());
EXPECT_CALL(cb_, Run(true));
delegate->OnDescriptorWriteResponse(kTestAddr1, true, cccd->handle());
EXPECT_CALL(*observer_,
OnCharacteristicNotification(device, characteristic, kTestData1));
delegate->OnNotification(kTestAddr1, characteristic->handle(), kTestData1);
fake_task_runner_->RunUntilIdle();
}
TEST_F(GattClientManagerTest, RemoteDeviceDescriptor) {
const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
const std::vector<uint8_t> kTestData2 = {0x4, 0x5, 0x6};
const bluetooth_v2_shlib::Gatt::Client::AuthReq kAuthReq =
bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_MITM;
const auto kServices = GenerateServices();
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(kServices.size(), services.size());
auto service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_GE(characteristics.size(), 1ul);
auto characteristic = characteristics[0];
std::vector<scoped_refptr<RemoteDescriptor>> descriptors =
characteristic->GetDescriptors();
ASSERT_GE(descriptors.size(), 1ul);
auto descriptor = descriptors[0];
EXPECT_CALL(*gatt_client_,
WriteDescriptor(kTestAddr1, descriptor->descriptor(), kAuthReq,
kTestData1))
.WillOnce(Return(true));
EXPECT_CALL(cb_, Run(true));
descriptor->WriteAuth(kAuthReq, kTestData1, cb_.Get());
delegate->OnDescriptorWriteResponse(kTestAddr1, true, descriptor->handle());
EXPECT_CALL(*gatt_client_,
ReadDescriptor(kTestAddr1, descriptor->descriptor(), kAuthReq))
.WillOnce(Return(true));
base::MockCallback<RemoteDescriptor::ReadCallback> read_cb;
EXPECT_CALL(read_cb, Run(true, kTestData2));
descriptor->ReadAuth(kAuthReq, read_cb.Get());
delegate->OnDescriptorReadResponse(kTestAddr1, true, descriptor->handle(),
kTestData2);
}
TEST_F(GattClientManagerTest, FakeCccd) {
std::vector<bluetooth_v2_shlib::Gatt::Service> input_services(1);
input_services[0].uuid = {{0x1}};
input_services[0].handle = 0x1;
input_services[0].primary = true;
bluetooth_v2_shlib::Gatt::Characteristic input_characteristic;
input_characteristic.uuid = {{0x1, 0x1}};
input_characteristic.handle = 0x2;
input_characteristic.permissions = bluetooth_v2_shlib::Gatt::PERMISSION_READ;
input_characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_NOTIFY;
input_services[0].characteristics.push_back(input_characteristic);
// Test indicate as well
input_characteristic.uuid = {{0x1, 0x2}};
input_characteristic.handle = 0x3;
input_characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_INDICATE;
input_services[0].characteristics.push_back(input_characteristic);
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, input_services);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(input_services.size(), services.size());
auto service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_EQ(2u, characteristics.size());
for (const auto& characteristic : characteristics) {
// A CCCD should have been created.
std::vector<scoped_refptr<RemoteDescriptor>> descriptors =
characteristic->GetDescriptors();
ASSERT_EQ(descriptors.size(), 1ul);
auto descriptor = descriptors[0];
EXPECT_EQ(RemoteDescriptor::kCccdUuid, descriptor->uuid());
EXPECT_EQ(static_cast<bluetooth_v2_shlib::Gatt::Permissions>(
bluetooth_v2_shlib::Gatt::PERMISSION_READ |
bluetooth_v2_shlib::Gatt::PERMISSION_WRITE),
descriptor->permissions());
}
}
TEST_F(GattClientManagerTest, WriteType) {
const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
bluetooth_v2_shlib::Gatt::Service service;
bluetooth_v2_shlib::Gatt::Characteristic characteristic;
service.uuid = {{0x1}};
service.handle = 0x1;
service.primary = true;
characteristic.uuid = {{0x1, 0x1}};
characteristic.handle = 0x2;
characteristic.permissions = bluetooth_v2_shlib::Gatt::PERMISSION_WRITE;
characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_WRITE;
service.characteristics.push_back(characteristic);
characteristic.uuid = {{0x1, 0x2}};
characteristic.handle = 0x3;
characteristic.permissions = bluetooth_v2_shlib::Gatt::PERMISSION_WRITE;
characteristic.properties =
bluetooth_v2_shlib::Gatt::PROPERTY_WRITE_NO_RESPONSE;
service.characteristics.push_back(characteristic);
characteristic.uuid = {{0x1, 0x3}};
characteristic.handle = 0x4;
characteristic.permissions = bluetooth_v2_shlib::Gatt::PERMISSION_WRITE;
characteristic.properties = bluetooth_v2_shlib::Gatt::PROPERTY_SIGNED_WRITE;
service.characteristics.push_back(characteristic);
Connect(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, {service});
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(1u, services.size());
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
services[0]->GetCharacteristics();
ASSERT_EQ(3u, characteristics.size());
using WriteType = bluetooth_v2_shlib::Gatt::WriteType;
// The current implementation of RemoteDevice will put the characteristics in
// the order reported by libcast_bluetooth.
const WriteType kWriteTypes[] = {WriteType::WRITE_TYPE_DEFAULT,
WriteType::WRITE_TYPE_NO_RESPONSE,
WriteType::WRITE_TYPE_SIGNED};
for (size_t i = 0; i < characteristics.size(); ++i) {
const auto& characteristic = characteristics[i];
EXPECT_CALL(
*gatt_client_,
WriteCharacteristic(kTestAddr1, characteristic->characteristic(),
bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_NONE,
kWriteTypes[i], kTestData1))
.WillOnce(Return(true));
base::MockCallback<RemoteCharacteristic::StatusCallback> write_cb;
EXPECT_CALL(write_cb, Run(true));
characteristic->Write(kTestData1, write_cb.Get());
delegate->OnCharacteristicWriteResponse(kTestAddr1, true,
characteristic->handle());
}
}
TEST_F(GattClientManagerTest, ConnectMultiple) {
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
for (size_t i = 0; i < 5; ++i) {
Connect(kTestAddr1);
EXPECT_TRUE(device->IsConnected());
EXPECT_CALL(*gatt_client_, Disconnect(kTestAddr1)).WillOnce(Return(true));
device->Disconnect({});
delegate->OnConnectChanged(kTestAddr1, true /* status */,
false /* connected */);
EXPECT_FALSE(device->IsConnected());
}
}
TEST_F(GattClientManagerTest, GetServicesFailOnConnect) {
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
device->Connect(cb_.Get());
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
EXPECT_CALL(cb_, Run(false));
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(false));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
true /* connected */);
EXPECT_FALSE(device->IsConnected());
}
TEST_F(GattClientManagerTest, GetServicesSuccessAfterConnectCallback) {
const auto kServices = GenerateServices();
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
// Callback that checks when Connect()'s callback returns, GetServices returns
// the correct services.
bool cb_called = false;
auto cb = base::BindOnce(
[](GattClientManagerTest* gcmt,
const std::vector<bluetooth_v2_shlib::Gatt::Service>*
expected_services,
bool* cb_called, bool success) {
EXPECT_TRUE(success);
*cb_called = true;
auto device = gcmt->GetDevice(kTestAddr1);
auto services = gcmt->GetServices(device.get());
EXPECT_EQ(expected_services->size(), services.size());
},
this, &kServices, &cb_called);
EXPECT_CALL(*gatt_client_, Connect(kTestAddr1)).WillOnce(Return(true));
device->Connect(std::move(cb));
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
EXPECT_CALL(*gatt_client_, GetServices(kTestAddr1)).WillOnce(Return(true));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
true /* connected */);
// Connect's callback should not be called until service discovery is
// complete.
EXPECT_FALSE(cb_called);
delegate->OnGetServices(kTestAddr1, kServices);
EXPECT_TRUE(cb_called);
}
TEST_F(GattClientManagerTest, Queuing) {
const std::vector<uint8_t> kTestData1 = {0x1, 0x2, 0x3};
const std::vector<uint8_t> kTestData2 = {0x4, 0x5, 0x6};
const std::vector<uint8_t> kTestData3 = {0x7, 0x8, 0x9};
const auto kServices = GenerateServices();
const bluetooth_v2_shlib::Gatt::Client::AuthReq kAuthReq =
bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_MITM;
const bluetooth_v2_shlib::Gatt::WriteType kWriteType =
bluetooth_v2_shlib::Gatt::WRITE_TYPE_DEFAULT;
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(kServices.size(), services.size());
auto service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_GE(characteristics.size(), 2ul);
auto characteristic1 = characteristics[0];
auto characteristic2 = characteristics[1];
// Issue a write to one characteristic.
EXPECT_CALL(*gatt_client_,
WriteCharacteristic(kTestAddr1, characteristic1->characteristic(),
kAuthReq, kWriteType, kTestData1))
.WillOnce(Return(true));
characteristic1->WriteAuth(kAuthReq, kWriteType, kTestData1, cb_.Get());
// Issue a read to another characteristic. The shlib should not get the call
// until after the read's callback.
EXPECT_CALL(*gatt_client_,
ReadCharacteristic(kTestAddr1, characteristic2->characteristic(),
kAuthReq))
.Times(0);
base::MockCallback<RemoteCharacteristic::ReadCallback> read_cb;
characteristic2->ReadAuth(kAuthReq, read_cb.Get());
EXPECT_CALL(cb_, Run(true));
EXPECT_CALL(*gatt_client_,
ReadCharacteristic(kTestAddr1, characteristic2->characteristic(),
kAuthReq))
.WillOnce(Return(true));
delegate->OnCharacteristicWriteResponse(kTestAddr1, true,
characteristic1->handle());
EXPECT_CALL(read_cb, Run(true, kTestData2));
delegate->OnCharacteristicReadResponse(kTestAddr1, true,
characteristic2->handle(), kTestData2);
base::RunLoop().RunUntilIdle();
}
TEST_F(GattClientManagerTest, CommandTimeout) {
const std::vector<uint8_t> kTestData = {0x7, 0x8, 0x9};
const auto kServices = GenerateServices();
const auto kAuthReq = bluetooth_v2_shlib::Gatt::Client::AUTH_REQ_MITM;
const auto kWriteType = bluetooth_v2_shlib::Gatt::WRITE_TYPE_DEFAULT;
// Connect a device and get services.
Connect(kTestAddr1);
scoped_refptr<RemoteDevice> device = GetDevice(kTestAddr1);
bluetooth_v2_shlib::Gatt::Client::Delegate* delegate =
gatt_client_->delegate();
delegate->OnServicesAdded(kTestAddr1, kServices);
std::vector<scoped_refptr<RemoteService>> services =
GetServices(device.get());
ASSERT_EQ(kServices.size(), services.size());
auto service = services[0];
std::vector<scoped_refptr<RemoteCharacteristic>> characteristics =
service->GetCharacteristics();
ASSERT_GE(characteristics.size(), 1ul);
auto characteristic1 = characteristics[0];
// Issue a write to one characteristic.
EXPECT_CALL(*gatt_client_,
WriteCharacteristic(kTestAddr1, characteristic1->characteristic(),
kAuthReq, kWriteType, kTestData))
.WillOnce(Return(true));
characteristic1->WriteAuth(kAuthReq, kWriteType, kTestData, cb_.Get());
// Let the command timeout
base::TestMockTimeTaskRunner::ScopedContext context(fake_task_runner_);
// We should request a disconnect.
EXPECT_CALL(*gatt_client_, Disconnect(kTestAddr1)).WillOnce(Return(true));
fake_task_runner_->FastForwardBy(RemoteDeviceImpl::kCommandTimeout);
// Make sure we issued a disconnect.
testing::Mock::VerifyAndClearExpectations(gatt_client_.get());
// The operation should fail.
EXPECT_CALL(cb_, Run(false));
delegate->OnConnectChanged(kTestAddr1, true /* status */,
false /* connected */);
}
} // namespace bluetooth
} // namespace chromecast