blob: 691669275ddd8a6b154de1059df79014504d9abe [file] [log] [blame]
/*
* Copyright 2021 HIMSA II K/S - www.himsa.com.
* Represented by EHIMA - www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "devices.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <map>
#include "bta_gatt_api_mock.h"
#include "bta_gatt_queue_mock.h"
#include "btm_api_mock.h"
#include "gatt/database_builder.h"
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"
namespace bluetooth {
namespace vc {
namespace internal {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::Test;
RawAddress GetTestAddress(int index) {
CHECK_LT(index, UINT8_MAX);
RawAddress result = {
{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
return result;
}
class VolumeControlDevicesTest : public ::testing::Test {
protected:
void SetUp() override {
devices_ = new VolumeControlDevices();
gatt::SetMockBtaGattInterface(&gatt_interface);
gatt::SetMockBtaGattQueue(&gatt_queue);
}
void TearDown() override {
gatt::SetMockBtaGattQueue(nullptr);
gatt::SetMockBtaGattInterface(nullptr);
delete devices_;
}
VolumeControlDevices* devices_ = nullptr;
gatt::MockBtaGattInterface gatt_interface;
gatt::MockBtaGattQueue gatt_queue;
};
TEST_F(VolumeControlDevicesTest, test_add) {
RawAddress test_address_0 = GetTestAddress(0);
ASSERT_EQ((size_t)0, devices_->Size());
devices_->Add(test_address_0, true);
ASSERT_EQ((size_t)1, devices_->Size());
}
TEST_F(VolumeControlDevicesTest, test_add_twice) {
RawAddress test_address_0 = GetTestAddress(0);
ASSERT_EQ((size_t)0, devices_->Size());
devices_->Add(test_address_0, true);
devices_->Add(test_address_0, true);
ASSERT_EQ((size_t)1, devices_->Size());
}
TEST_F(VolumeControlDevicesTest, test_remove) {
RawAddress test_address_0 = GetTestAddress(0);
RawAddress test_address_1 = GetTestAddress(1);
devices_->Add(test_address_0, true);
devices_->Add(test_address_1, true);
ASSERT_EQ((size_t)2, devices_->Size());
devices_->Remove(test_address_0);
ASSERT_EQ((size_t)1, devices_->Size());
}
TEST_F(VolumeControlDevicesTest, test_clear) {
RawAddress test_address_0 = GetTestAddress(0);
ASSERT_EQ((size_t)0, devices_->Size());
devices_->Add(test_address_0, true);
ASSERT_EQ((size_t)1, devices_->Size());
devices_->Clear();
ASSERT_EQ((size_t)0, devices_->Size());
}
TEST_F(VolumeControlDevicesTest, test_find_by_address) {
RawAddress test_address_0 = GetTestAddress(0);
RawAddress test_address_1 = GetTestAddress(1);
RawAddress test_address_2 = GetTestAddress(2);
devices_->Add(test_address_0, true);
devices_->Add(test_address_1, false);
devices_->Add(test_address_2, true);
VolumeControlDevice* device = devices_->FindByAddress(test_address_1);
ASSERT_NE(nullptr, device);
ASSERT_EQ(test_address_1, device->address);
}
TEST_F(VolumeControlDevicesTest, test_find_by_conn_id) {
RawAddress test_address_0 = GetTestAddress(0);
devices_->Add(test_address_0, true);
VolumeControlDevice* test_device = devices_->FindByAddress(test_address_0);
test_device->connection_id = 0x0005;
ASSERT_NE(nullptr, devices_->FindByConnId(test_device->connection_id));
}
TEST_F(VolumeControlDevicesTest, test_disconnect) {
RawAddress test_address_0 = GetTestAddress(0);
RawAddress test_address_1 = GetTestAddress(1);
devices_->Add(test_address_0, true);
devices_->Add(test_address_1, true);
VolumeControlDevice* test_device_0 = devices_->FindByAddress(test_address_0);
test_device_0->connection_id = 0x0005;
tGATT_IF gatt_if = 8;
EXPECT_CALL(gatt_interface, Close(test_device_0->connection_id));
EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, test_address_1, _));
devices_->Disconnect(gatt_if);
}
TEST_F(VolumeControlDevicesTest, test_control_point_operation) {
uint8_t opcode = 50;
std::vector<RawAddress> devices;
for (int i = 5; i > 0; i--) {
RawAddress test_address = GetTestAddress(i);
devices.push_back(test_address);
uint8_t change_counter = 10 * i;
uint16_t control_point_handle = 0x0020 + i;
uint16_t connection_id = i;
devices_->Add(test_address, true);
VolumeControlDevice* device = devices_->FindByAddress(test_address);
device->connection_id = connection_id;
device->change_counter = change_counter;
device->volume_control_point_handle = control_point_handle;
std::vector<uint8_t> data_expected({opcode, change_counter});
EXPECT_CALL(gatt_queue,
WriteCharacteristic(connection_id, control_point_handle,
data_expected, GATT_WRITE, _, _));
}
const std::vector<uint8_t>* arg = nullptr;
GATT_WRITE_OP_CB cb = nullptr;
void* cb_data = nullptr;
devices_->ControlPointOperation(devices, opcode, arg, cb, cb_data);
}
TEST_F(VolumeControlDevicesTest, test_control_point_operation_args) {
uint8_t opcode = 60;
uint8_t arg_1 = 0x02;
uint8_t arg_2 = 0x05;
std::vector<RawAddress> devices;
for (int i = 5; i > 0; i--) {
RawAddress test_address = GetTestAddress(i);
devices.push_back(test_address);
uint8_t change_counter = 10 * i;
uint16_t control_point_handle = 0x0020 + i;
uint16_t connection_id = i;
devices_->Add(test_address, true);
VolumeControlDevice* device = devices_->FindByAddress(test_address);
device->connection_id = connection_id;
device->change_counter = change_counter;
device->volume_control_point_handle = control_point_handle;
std::vector<uint8_t> data_expected({opcode, change_counter, arg_1, arg_2});
EXPECT_CALL(gatt_queue,
WriteCharacteristic(connection_id, control_point_handle,
data_expected, GATT_WRITE, _, _));
}
std::vector<uint8_t> arg({arg_1, arg_2});
GATT_WRITE_OP_CB cb = nullptr;
void* cb_data = nullptr;
devices_->ControlPointOperation(devices, opcode, &arg, cb, cb_data);
}
TEST_F(VolumeControlDevicesTest, test_control_point_skip_not_connected) {
RawAddress test_address = GetTestAddress(1);
devices_->Add(test_address, true);
VolumeControlDevice* device = devices_->FindByAddress(test_address);
device->connection_id = GATT_INVALID_CONN_ID;
uint16_t control_point_handle = 0x0020;
device->volume_control_point_handle = control_point_handle;
EXPECT_CALL(gatt_queue,
WriteCharacteristic(_, control_point_handle, _, _, _, _))
.Times(0);
uint8_t opcode = 5;
std::vector<RawAddress> devices = {test_address};
const std::vector<uint8_t>* arg = nullptr;
GATT_WRITE_OP_CB cb = nullptr;
void* cb_data = nullptr;
devices_->ControlPointOperation(devices, opcode, arg, cb, cb_data);
}
class VolumeControlDeviceTest : public ::testing::Test {
protected:
void SetUp() override {
device = new VolumeControlDevice(GetTestAddress(1), true);
gatt::SetMockBtaGattInterface(&gatt_interface);
gatt::SetMockBtaGattQueue(&gatt_queue);
bluetooth::manager::SetMockBtmInterface(&btm_interface);
ON_CALL(gatt_interface, GetCharacteristic(_, _))
.WillByDefault(
Invoke([&](uint16_t conn_id,
uint16_t handle) -> const gatt::Characteristic* {
for (auto const& service : services) {
for (auto const& characteristic : service.characteristics) {
if (characteristic.value_handle == handle) {
return &characteristic;
}
}
}
return nullptr;
}));
ON_CALL(gatt_interface, GetOwningService(_, _))
.WillByDefault(Invoke(
[&](uint16_t conn_id, uint16_t handle) -> const gatt::Service* {
for (auto const& service : services) {
if (service.handle <= handle && service.end_handle >= handle) {
return &service;
}
}
return nullptr;
}));
ON_CALL(gatt_interface, GetServices(_)).WillByDefault(Return(&services));
}
void TearDown() override {
bluetooth::manager::SetMockBtmInterface(nullptr);
gatt::SetMockBtaGattQueue(nullptr);
gatt::SetMockBtaGattInterface(nullptr);
delete device;
}
/* sample database 1xVCS, 2xAICS, 2xVOCS */
void SetSampleDatabase1(void) {
gatt::DatabaseBuilder builder;
builder.AddService(0x0001, 0x0016, kVolumeControlUuid, true);
builder.AddCharacteristic(
0x0010, 0x0011, kVolumeControlStateUuid,
GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
builder.AddDescriptor(0x0012,
Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
builder.AddCharacteristic(0x0013, 0x0014, kVolumeControlPointUuid,
GATT_CHAR_PROP_BIT_WRITE);
builder.AddCharacteristic(0x0015, 0x0016, kVolumeFlagsUuid,
GATT_CHAR_PROP_BIT_READ);
builder.AddService(0x00a0, 0x00a3,
Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER), true);
builder.AddCharacteristic(0x00a1, 0x00a2,
Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD),
GATT_CHAR_PROP_BIT_NOTIFY);
builder.AddDescriptor(0x00a3,
Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
services = builder.Build().Services();
ASSERT_EQ(true, device->UpdateHandles());
}
/* sample database no VCS */
void SetSampleDatabase2(void) {
gatt::DatabaseBuilder builder;
builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true);
builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00),
GATT_CHAR_PROP_BIT_READ);
services = builder.Build().Services();
ASSERT_EQ(false, device->UpdateHandles());
}
VolumeControlDevice* device = nullptr;
gatt::MockBtaGattInterface gatt_interface;
gatt::MockBtaGattQueue gatt_queue;
bluetooth::manager::MockBtmInterface btm_interface;
std::list<gatt::Service> services;
};
TEST_F(VolumeControlDeviceTest, test_service_volume_control_not_found) {
SetSampleDatabase2();
ASSERT_EQ(false, device->HasHandles());
}
TEST_F(VolumeControlDeviceTest, test_service_volume_control_incomplete) {
gatt::DatabaseBuilder builder;
builder.AddService(0x0001, 0x0006, kVolumeControlUuid, true);
builder.AddCharacteristic(
0x0002, 0x0003, kVolumeControlStateUuid,
GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
builder.AddDescriptor(0x0004, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
builder.AddCharacteristic(0x0005, 0x0006, kVolumeControlPointUuid,
GATT_CHAR_PROP_BIT_WRITE);
/* no Volume Control Flags characteristic */
services = builder.Build().Services();
ASSERT_EQ(false, device->UpdateHandles());
ASSERT_EQ(0x0000, device->volume_state_handle);
ASSERT_EQ(0x0000, device->volume_state_ccc_handle);
ASSERT_EQ(0x0000, device->volume_control_point_handle);
ASSERT_EQ(0x0000, device->volume_flags_handle);
ASSERT_EQ(0x0000, device->volume_flags_ccc_handle);
ASSERT_EQ(false, device->HasHandles());
}
TEST_F(VolumeControlDeviceTest, test_services_changed) {
SetSampleDatabase1();
ASSERT_NE(0, device->volume_state_handle);
ASSERT_NE(0, device->volume_control_point_handle);
ASSERT_NE(0, device->volume_flags_handle);
ASSERT_EQ(true, device->HasHandles());
SetSampleDatabase2();
ASSERT_EQ(0, device->volume_state_handle);
ASSERT_EQ(0, device->volume_control_point_handle);
ASSERT_EQ(0, device->volume_flags_handle);
ASSERT_EQ(false, device->HasHandles());
}
TEST_F(VolumeControlDeviceTest, test_enqueue_initial_requests) {
SetSampleDatabase1();
tGATT_IF gatt_if = 0x0001;
std::vector<uint8_t> register_for_notification_data({0x01, 0x00});
std::map<uint16_t, uint16_t> expected_to_read_write{
{0x0011, 0x0012} /* volume control state */};
for (auto const& handle_pair : expected_to_read_write) {
EXPECT_CALL(gatt_queue, ReadCharacteristic(_, handle_pair.first, _, _));
EXPECT_CALL(gatt_queue, WriteDescriptor(_, handle_pair.second,
register_for_notification_data,
GATT_WRITE, _, _));
EXPECT_CALL(gatt_interface,
RegisterForNotifications(gatt_if, _, handle_pair.first));
}
auto chrc_read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, uint8_t* value, void* data) {};
auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, const uint8_t* value,
void* data) {};
ASSERT_EQ(true, device->EnqueueInitialRequests(gatt_if, chrc_read_cb,
cccd_write_cb));
};
TEST_F(VolumeControlDeviceTest, test_device_ready) {
SetSampleDatabase1();
// grab all the handles requested
std::vector<uint16_t> requested_handles;
ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
.WillByDefault(Invoke(
[&requested_handles](
uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value,
tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb,
void* cb_data) -> void { requested_handles.push_back(handle); }));
ON_CALL(gatt_queue, ReadCharacteristic(_, _, _, _))
.WillByDefault(Invoke(
[&requested_handles](uint16_t conn_id, uint16_t handle,
GATT_READ_OP_CB cb, void* cb_data) -> void {
requested_handles.push_back(handle);
}));
auto chrc_read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, uint8_t* value, void* data) {};
auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, const uint8_t* value,
void* data) {};
ASSERT_EQ(true, device->EnqueueInitialRequests(0x0001, chrc_read_cb,
cccd_write_cb));
ASSERT_NE((size_t)0, requested_handles.size());
// indicate non-pending requests
ASSERT_EQ(false, device->device_ready);
device->VerifyReady(0xffff);
for (uint16_t handle : requested_handles) {
ASSERT_EQ(false, device->device_ready);
device->VerifyReady(handle);
}
ASSERT_EQ(true, device->device_ready);
}
TEST_F(VolumeControlDeviceTest, test_enqueue_remaining_requests) {
SetSampleDatabase1();
tGATT_IF gatt_if = 0x0001;
std::vector<uint8_t> register_for_notification_data({0x01, 0x00});
std::vector<uint16_t> expected_to_read{0x0016 /* volume flags */};
std::map<uint16_t, uint16_t> expected_to_write_value_ccc_handle_map{};
for (uint16_t handle : expected_to_read) {
EXPECT_CALL(gatt_queue, ReadCharacteristic(_, handle, _, _));
}
for (auto const& handle_pair : expected_to_write_value_ccc_handle_map) {
EXPECT_CALL(gatt_queue, WriteDescriptor(_, handle_pair.second,
register_for_notification_data,
GATT_WRITE, _, _));
EXPECT_CALL(gatt_interface,
RegisterForNotifications(gatt_if, _, handle_pair.first));
}
auto chrc_read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
uint16_t len, uint8_t* value, void* data) {};
auto cccd_write_cb = [](uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len, const uint8_t* value,
void* data) {};
device->EnqueueRemainingRequests(gatt_if, chrc_read_cb, cccd_write_cb);
}
TEST_F(VolumeControlDeviceTest, test_check_link_encrypted) {
ON_CALL(btm_interface, BTM_IsEncrypted(_, _))
.WillByDefault(DoAll(Return(true)));
ASSERT_EQ(true, device->IsEncryptionEnabled());
ON_CALL(btm_interface, BTM_IsEncrypted(_, _))
.WillByDefault(DoAll(Return(false)));
ASSERT_NE(true, device->IsEncryptionEnabled());
}
TEST_F(VolumeControlDeviceTest, test_control_point_operation) {
GATT_WRITE_OP_CB write_cb = [](uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len,
const uint8_t* value, void* data) {};
SetSampleDatabase1();
device->change_counter = 0x01;
std::vector<uint8_t> expected_data({0x03, 0x01});
EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0014, expected_data,
GATT_WRITE, write_cb, nullptr));
device->ControlPointOperation(0x03, nullptr, write_cb, nullptr);
}
TEST_F(VolumeControlDeviceTest, test_control_point_operation_arg) {
GATT_WRITE_OP_CB write_cb = [](uint16_t conn_id, tGATT_STATUS status,
uint16_t handle, uint16_t len,
const uint8_t* value, void* data) {};
SetSampleDatabase1();
device->change_counter = 0x55;
std::vector<uint8_t> expected_data({0x01, 0x55, 0x02, 0x03});
EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0014, expected_data,
GATT_WRITE, write_cb, nullptr));
std::vector<uint8_t> arg({0x02, 0x03});
device->ControlPointOperation(0x01, &arg, write_cb, nullptr);
}
} // namespace internal
} // namespace vc
} // namespace bluetooth