/* * 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 #include #include #include "bta_gatt_api_mock.h" #include "bta_gatt_queue_mock.h" #include "btm_api_mock.h" #include "gatt/database_builder.h" #include "stack/include/bt_uuid16.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) { EXPECT_LT(index, UINT8_MAX); RawAddress result = { {0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast(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)); devices_->Disconnect(gatt_if); } TEST_F(VolumeControlDevicesTest, test_control_point_operation) { uint8_t opcode = 50; std::vector 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 data_expected({opcode, change_counter}); EXPECT_CALL(gatt_queue, WriteCharacteristic(connection_id, control_point_handle, data_expected, GATT_WRITE, _, _)); } const std::vector* 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 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 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 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 devices = {test_address}; const std::vector* 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.AddIncludedService(0x0004, kVolumeOffsetUuid, 0x0060, 0x0069); builder.AddIncludedService(0x0005, kVolumeOffsetUuid, 0x0080, 0x008b); 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(0x0060, 0x0069, kVolumeOffsetUuid, false); builder.AddCharacteristic( 0x0061, 0x0062, kVolumeOffsetStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0063, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0064, 0x0065, kVolumeOffsetLocationUuid, GATT_CHAR_PROP_BIT_READ); builder.AddCharacteristic(0x0066, 0x0067, kVolumeOffsetControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic(0x0068, 0x0069, kVolumeOffsetOutputDescriptionUuid, GATT_CHAR_PROP_BIT_READ); builder.AddService(0x0080, 0x008b, kVolumeOffsetUuid, false); builder.AddCharacteristic( 0x0081, 0x0082, kVolumeOffsetStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0083, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0084, 0x0085, kVolumeOffsetLocationUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0086, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0087, 0x0088, kVolumeOffsetControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic( 0x0089, 0x008a, kVolumeOffsetOutputDescriptionUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x008b, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); 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 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_service_vocs_incomplete) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x000a, kVolumeControlUuid, true); builder.AddIncludedService(0x0002, kVolumeOffsetUuid, 0x000b, 0x0013); builder.AddCharacteristic( 0x0003, 0x0004, kVolumeControlStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0005, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0006, 0x0007, kVolumeControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic( 0x0008, 0x0009, kVolumeFlagsUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x000a, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddService(0x000b, 0x0013, kVolumeOffsetUuid, false); builder.AddCharacteristic( 0x000c, 0x000d, kVolumeOffsetStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x000e, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x000f, 0x0010, kVolumeOffsetLocationUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0011, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0012, 0x0013, kVolumeOffsetControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); /* no Audio Output Description characteristic */ services = builder.Build().Services(); ASSERT_EQ(true, device->UpdateHandles()); ASSERT_EQ((size_t)0, device->audio_offsets.Size()); ASSERT_EQ(0x0004, device->volume_state_handle); ASSERT_EQ(0x0005, device->volume_state_ccc_handle); ASSERT_EQ(0x0007, device->volume_control_point_handle); ASSERT_EQ(0x0009, device->volume_flags_handle); ASSERT_EQ(0x000a, device->volume_flags_ccc_handle); ASSERT_EQ(true, device->HasHandles()); } TEST_F(VolumeControlDeviceTest, test_service_vocs_found) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x000a, kVolumeControlUuid, true); builder.AddIncludedService(0x0002, kVolumeOffsetUuid, 0x000b, 0x0015); builder.AddCharacteristic( 0x0003, 0x0004, kVolumeControlStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0005, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0006, 0x0007, kVolumeControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic( 0x0008, 0x0009, kVolumeFlagsUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x000a, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddService(0x000b, 0x0015, kVolumeOffsetUuid, false); builder.AddCharacteristic( 0x000c, 0x000d, kVolumeOffsetStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x000e, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x000f, 0x0010, kVolumeOffsetLocationUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0011, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0012, 0x0013, kVolumeOffsetControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic(0x0014, 0x0015, kVolumeOffsetOutputDescriptionUuid, GATT_CHAR_PROP_BIT_READ); services = builder.Build().Services(); ASSERT_EQ(true, device->UpdateHandles()); ASSERT_EQ((size_t)1, device->audio_offsets.Size()); VolumeOffset* offset = device->audio_offsets.FindByServiceHandle(0x000b); ASSERT_NE(nullptr, offset); ASSERT_EQ(0x000d, offset->state_handle); ASSERT_EQ(0x000e, offset->state_ccc_handle); ASSERT_EQ(0x0010, offset->audio_location_handle); ASSERT_EQ(0x0011, offset->audio_location_ccc_handle); ASSERT_EQ(0x0013, offset->control_point_handle); ASSERT_EQ(0x0015, offset->audio_descr_handle); ASSERT_EQ(0x0000, offset->audio_descr_ccc_handle); ASSERT_EQ(true, device->HasHandles()); } TEST_F(VolumeControlDeviceTest, test_multiple_services_found) { SetSampleDatabase1(); ASSERT_EQ((size_t)2, device->audio_offsets.Size()); VolumeOffset* offset_1 = device->audio_offsets.FindById(1); VolumeOffset* offset_2 = device->audio_offsets.FindById(2); ASSERT_NE(nullptr, offset_1); ASSERT_NE(nullptr, offset_2); ASSERT_NE(offset_1->service_handle, offset_2->service_handle); } TEST_F(VolumeControlDeviceTest, test_services_changed) { SetSampleDatabase1(); ASSERT_NE((size_t)0, device->audio_offsets.Size()); 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((size_t)0, device->audio_offsets.Size()); 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 register_for_notification_data({0x01, 0x00}); std::map expected_to_read_write{ {0x0011, 0x0012} /* volume control state */, {0x0062, 0x0063} /* volume offset state 1 */, {0x0082, 0x0083} /* volume offset state 2 */}; 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 requested_handles; ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _)) .WillByDefault(Invoke( [&requested_handles]( uint16_t conn_id, uint16_t handle, std::vector 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 register_for_notification_data({0x01, 0x00}); std::vector expected_to_read{ 0x0016 /* volume flags */, 0x0065 /* audio location 1 */, 0x0069 /* audio output description 1 */, 0x0085 /* audio location 1 */, 0x008a /* audio output description 1 */}; std::map expected_to_write_value_ccc_handle_map{ {0x0085, 0x0086} /* audio location ccc 2 */, {0x008a, 0x008b} /* audio output description ccc */ }; 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 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 expected_data({0x01, 0x55, 0x02, 0x03}); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0014, expected_data, GATT_WRITE, write_cb, nullptr)); std::vector arg({0x02, 0x03}); device->ControlPointOperation(0x01, &arg, write_cb, nullptr); } TEST_F(VolumeControlDeviceTest, test_get_ext_audio_out_volume_offset) { GATT_READ_OP_CB read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, uint8_t* value, void* data) {}; SetSampleDatabase1(); EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0062, read_cb, nullptr)); device->GetExtAudioOutVolumeOffset(1, read_cb, nullptr); } TEST_F(VolumeControlDeviceTest, test_get_ext_audio_out_location) { GATT_READ_OP_CB read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, uint8_t* value, void* data) {}; SetSampleDatabase1(); EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0085, read_cb, nullptr)); device->GetExtAudioOutLocation(2, read_cb, nullptr); } TEST_F(VolumeControlDeviceTest, test_set_ext_audio_out_location) { SetSampleDatabase1(); std::vector expected_data({0x44, 0x33, 0x22, 0x11}); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0085, expected_data, GATT_WRITE_NO_RSP, nullptr, nullptr)); device->SetExtAudioOutLocation(2, 0x11223344); } TEST_F(VolumeControlDeviceTest, test_set_ext_audio_out_location_non_writable) { SetSampleDatabase1(); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0); device->SetExtAudioOutLocation(1, 0x11223344); } TEST_F(VolumeControlDeviceTest, test_get_ext_audio_out_description) { GATT_READ_OP_CB read_cb = [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, uint8_t* value, void* data) {}; SetSampleDatabase1(); EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x008a, read_cb, nullptr)); device->GetExtAudioOutDescription(2, read_cb, nullptr); } TEST_F(VolumeControlDeviceTest, test_set_ext_audio_out_description) { SetSampleDatabase1(); std::string descr = "right front"; std::vector expected_data(descr.begin(), descr.end()); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x008a, expected_data, GATT_WRITE_NO_RSP, nullptr, nullptr)); device->SetExtAudioOutDescription(2, descr); } TEST_F(VolumeControlDeviceTest, test_set_ext_audio_out_description_non_writable) { SetSampleDatabase1(); std::string descr = "left front"; EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0); device->SetExtAudioOutDescription(1, descr); } TEST_F(VolumeControlDeviceTest, test_ext_audio_out_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(); VolumeOffset* offset = device->audio_offsets.FindById(1); ASSERT_NE(nullptr, offset); offset->change_counter = 0x09; std::vector expected_data({0x0b, 0x09}); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0067, expected_data, GATT_WRITE, write_cb, nullptr)); device->ExtAudioOutControlPointOperation(1, 0x0b, nullptr, write_cb, nullptr); } TEST_F(VolumeControlDeviceTest, test_ext_audio_out_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(); VolumeOffset* offset = device->audio_offsets.FindById(1); ASSERT_NE(nullptr, offset); offset->change_counter = 0x09; std::vector expected_data({0x0b, 0x09, 0x01, 0x02, 0x03, 0x04}); std::vector arg({0x01, 0x02, 0x03, 0x04}); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, 0x0067, expected_data, GATT_WRITE, write_cb, nullptr)); device->ExtAudioOutControlPointOperation(1, 0x0b, &arg, write_cb, nullptr); } } // namespace internal } // namespace vc } // namespace bluetooth