/* * 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 #include #include #include #include #include #include "bta/include/bta_vc_api.h" #include "bta/test/common/bta_gatt_api_mock.h" #include "bta/test/common/bta_gatt_queue_mock.h" #include "bta/test/common/btm_api_mock.h" #include "bta/test/common/mock_csis_client.h" #include "bta/vc/types.h" #include "gatt/database_builder.h" #include "hardware/bt_gatt_types.h" #include "include/bind_helpers.h" #include "osi/test/alarm_mock.h" #include "stack/include/bt_uuid16.h" #include "stack/include/btm_status.h" #include "test/common/mock_functions.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" void btif_storage_add_volume_control(const RawAddress& /*addr*/, bool /*auto_conn*/) {} struct alarm_t { alarm_callback_t cb = nullptr; void* data = nullptr; bool on_main_loop = false; }; using ::testing::NiceMock; namespace bluetooth { namespace vc { namespace internal { namespace { using base::Bind; using base::Unretained; using bluetooth::aics::GainMode; using bluetooth::aics::Mute; using bluetooth::vc::ConnectionState; using bluetooth::vc::VolumeControlCallbacks; using testing::_; using testing::DoAll; using testing::DoDefault; using testing::Invoke; using testing::Mock; using testing::NotNull; using testing::Return; using testing::SaveArg; using testing::SetArgPointee; using testing::WithArg; RawAddress GetTestAddress(int index) { EXPECT_LT(index, UINT8_MAX); RawAddress result = {{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast(index)}}; return result; } class MockVolumeControlCallbacks : public VolumeControlCallbacks { public: MockVolumeControlCallbacks() = default; MockVolumeControlCallbacks(const MockVolumeControlCallbacks&) = delete; MockVolumeControlCallbacks& operator=(const MockVolumeControlCallbacks&) = delete; ~MockVolumeControlCallbacks() override = default; MOCK_METHOD((void), OnConnectionState, (ConnectionState state, const RawAddress& address), (override)); MOCK_METHOD((void), OnDeviceAvailable, (const RawAddress& address, uint8_t num_offset, uint8_t num_inputs), (override)); MOCK_METHOD((void), OnVolumeStateChanged, (const RawAddress& address, uint8_t volume, bool mute, uint8_t flags, bool isAutonomous), (override)); MOCK_METHOD((void), OnGroupVolumeStateChanged, (int group_id, uint8_t volume, bool mute, bool isAutonomous), (override)); MOCK_METHOD((void), OnExtAudioOutVolumeOffsetChanged, (const RawAddress& address, uint8_t ext_output_id, int16_t offset), (override)); MOCK_METHOD((void), OnExtAudioOutLocationChanged, (const RawAddress& address, uint8_t ext_output_id, uint32_t location), (override)); MOCK_METHOD((void), OnExtAudioOutDescriptionChanged, (const RawAddress& address, uint8_t ext_output_id, std::string descr), (override)); MOCK_METHOD((void), OnExtAudioInStateChanged, (const RawAddress& address, uint8_t ext_input_id, int8_t gain_setting, Mute mute, GainMode gain_mode), (override)); MOCK_METHOD((void), OnExtAudioInSetGainSettingFailed, (const RawAddress& address, uint8_t ext_input_id), (override)); MOCK_METHOD((void), OnExtAudioInSetMuteFailed, (const RawAddress& address, uint8_t ext_input_id), (override)); MOCK_METHOD((void), OnExtAudioInSetGainModeFailed, (const RawAddress& address, uint8_t ext_input_id), (override)); MOCK_METHOD((void), OnExtAudioInStatusChanged, (const RawAddress& address, uint8_t ext_input_id, VolumeInputStatus status), (override)); MOCK_METHOD((void), OnExtAudioInTypeChanged, (const RawAddress& address, uint8_t ext_input_id, VolumeInputType type), (override)); MOCK_METHOD((void), OnExtAudioInGainSettingPropertiesChanged, (const RawAddress& address, uint8_t ext_input_id, uint8_t unit, int8_t min, int8_t max), (override)); MOCK_METHOD((void), OnExtAudioInDescriptionChanged, (const RawAddress& address, uint8_t ext_input_id, std::string description, bool is_writable), (override)); }; class VolumeControlTest : public ::testing::Test { private: void set_sample_database(uint16_t conn_id, bool vcs, bool vcs_broken, bool aics, bool aics_broken, bool vocs, bool vocs_broken) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true); builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00), GATT_CHAR_PROP_BIT_READ); /* 0x0004-0x000f RFU */ if (vcs) { /* VCS */ builder.AddService(0x0010, 0x0026, kVolumeControlUuid, true); if (aics) { builder.AddIncludedService(0x0011, kVolumeAudioInputUuid, 0x0030, 0x003e); builder.AddIncludedService(0x0012, kVolumeAudioInputUuid, 0x0050, 0x005f); } if (vocs) { builder.AddIncludedService(0x0013, kVolumeOffsetUuid, 0x0070, 0x0079); builder.AddIncludedService(0x0014, kVolumeOffsetUuid, 0x0080, 0x008b); } /* 0x0015-0x001f RFU */ builder.AddCharacteristic(0x0020, 0x0021, kVolumeControlStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); if (!vcs_broken) { builder.AddCharacteristic(0x0023, 0x0024, kVolumeControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); } builder.AddCharacteristic(0x0025, 0x0026, kVolumeFlagsUuid, GATT_CHAR_PROP_BIT_READ); /* 0x0027-0x002f RFU */ if (aics) { /* AICS 1st instance */ builder.AddService(0x0030, 0x003e, kVolumeAudioInputUuid, false); builder.AddCharacteristic(0x0031, 0x0032, kVolumeAudioInputStateUuid, GATT_CHAR_PROP_BIT_READ); builder.AddDescriptor(0x0033, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0034, 0x0035, kVolumeAudioInputGainSettingPropertiesUuid, GATT_CHAR_PROP_BIT_READ); builder.AddCharacteristic(0x0036, 0x0037, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ); builder.AddCharacteristic(0x0038, 0x0039, kVolumeAudioInputStatusUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x003a, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x003b, 0x003c, kVolumeAudioInputControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic(0x003d, 0x003e, kVolumeAudioInputDescriptionUuid, GATT_CHAR_PROP_BIT_READ); /* 0x003f-0x004f RFU */ /* AICS 2nd instance */ builder.AddService(0x0050, 0x005f, kVolumeAudioInputUuid, false); builder.AddCharacteristic(0x0051, 0x0052, kVolumeAudioInputStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0053, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); if (!aics_broken) { builder.AddCharacteristic(0x0054, 0x0055, kVolumeAudioInputGainSettingPropertiesUuid, GATT_CHAR_PROP_BIT_READ); } builder.AddCharacteristic(0x0056, 0x0057, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ); builder.AddCharacteristic(0x0058, 0x0059, kVolumeAudioInputStatusUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x005a, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x005b, 0x005c, kVolumeAudioInputControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic( 0x005d, 0x005e, kVolumeAudioInputDescriptionUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x005f, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); /* 0x0060-0x006f RFU */ } if (vocs) { /* VOCS 1st instance */ builder.AddService(0x0070, 0x0079, kVolumeOffsetUuid, false); builder.AddCharacteristic(0x0071, 0x0072, kVolumeOffsetStateUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0073, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0074, 0x0075, kVolumeOffsetLocationUuid, GATT_CHAR_PROP_BIT_READ); builder.AddCharacteristic(0x0076, 0x0077, kVolumeOffsetControlPointUuid, GATT_CHAR_PROP_BIT_WRITE); builder.AddCharacteristic(0x0078, 0x0079, kVolumeOffsetOutputDescriptionUuid, GATT_CHAR_PROP_BIT_READ); /* 0x007a-0x007f RFU */ /* VOCS 2nd instance */ 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)); if (!vocs_broken) { 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)); } } /* 0x008c-0x008f RFU */ /* GATTS */ builder.AddService(0x0090, 0x0093, Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER), true); builder.AddCharacteristic(0x0091, 0x0092, Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD), GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0093, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); services_map[conn_id] = builder.Build().Services(); ON_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)) .WillByDefault(Invoke([&](uint16_t conn_id, uint16_t handle, GATT_READ_OP_CB cb, void* cb_data) -> void { std::vector value; switch (handle) { case 0x0003: /* device name */ value.resize(20); break; case 0x0021: /* volume state */ value.resize(3); break; case 0x0026: /* volume flags */ value.resize(1); break; case 0x0032: // 1st AICS instance case 0x0052: // 2nd AICS instance /* audio input state */ value.resize(4); break; case 0x0035: // 1st AICS instance case 0x0055: // 2nd AICS instance /* audio input gain settings */ value.resize(3); break; case 0x0037: // 1st AICS instance case 0x0057: // 2nd AICS instance /* audio input type */ value.resize(1); break; case 0x0039: // 1st AICS instance case 0x0059: // 2nd AICS instance /* audio input status */ value.resize(1); break; case 0x003e: // 1st AICS instance case 0x005e: // 2nd AICS instance /* audio input description */ value.resize(14); break; case 0x0072: // 1st VOCS instance case 0x0082: // 2nd VOCS instance /* offset state */ value.resize(3); break; case 0x0075: // 1st VOCS instance case 0x0085: // 2nd VOCS instance /* offset location */ value.resize(4); break; case 0x0079: // 1st VOCS instance case 0x008a: // 2nd VOCS instance /* offset output description */ value.resize(10); break; default: FAIL(); return; } if (do_not_respond_to_reads) { return; } cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); })); ON_CALL(gatt_queue, ReadMultiCharacteristic(conn_id, _, _, _)) .WillByDefault(Invoke([&](uint16_t conn_id, tBTA_GATTC_MULTI& handles, GATT_READ_MULTI_OP_CB cb, void* cb_data) -> void { std::vector value; auto add_element = [&](uint8_t data[], uint8_t len) -> void { // LE order, 2 octets value.push_back(len); value.push_back(0x00); uint8_t* p = &data[0]; for (size_t i = 0; i < len; i++) { value.push_back(*p++); } }; for (size_t i = 0; i < handles.num_attr; i++) { switch (handles.handles[i]) { case 0x0003: { /* device name */ uint8_t name[] = "UnknownName"; add_element(name, sizeof(name)); break; } case 0x0021: { /* state */ uint8_t state[3] = {0x00, 0x00, 0x00}; add_element(state, sizeof(state)); break; } case 0x0026: { /* volume flags */ uint8_t flags[] = {0x01}; add_element(flags, sizeof(flags)); break; } case 0x0032: // 1st AICS instance case 0x0052: // 2nd AICS instance { /* audio input state */ uint8_t state[4] = {0x01, 0x01, 0x01, 0x00}; add_element(state, sizeof(state)); break; } case 0x0035: // 1st AICS instance case 0x0055: // 2nd AICS instance { /* audio input gain settings */ uint8_t gain_settings[3] = {0x01, 0x01, 0x01}; add_element(gain_settings, sizeof(gain_settings)); break; } case 0x0037: // 1st AICS instance case 0x0057: // 2nd AICS instance { /* audio input type */ uint8_t type[] = {0x01}; add_element(type, sizeof(type)); break; } case 0x0039: // 1st AICS instance case 0x0059: // 2nd AICS instance { /* audio input status */ uint8_t status[] = {0x00}; add_element(status, sizeof(status)); break; } case 0x003e: // 1st AICS instance case 0x005e: // 2nd AICS instance { /* audio input description */ uint8_t dest[] = "input"; add_element(dest, sizeof(dest)); break; } case 0x0072: // 1st VOCS instance case 0x0082: // 2nd VOCS instance { /* offset state */ uint8_t state[3] = {0x00, 0x20, 0x00}; add_element(state, sizeof(state)); break; } case 0x0075: // 1st VOCS instance case 0x0085: // 2nd VOCS instance { /* offset location */ uint8_t location[4] = {0x00, 0x02, 0x00, 0x01}; add_element(location, sizeof(location)); break; } case 0x0079: // 1st VOCS instance case 0x008a: // 2nd VOCS instance { /* offset output description */ uint8_t dest[] = "VOCS_D"; add_element(dest, sizeof(dest)); break; } default: FAIL(); return; } } if (do_not_respond_to_reads) { return; } cb(conn_id, GATT_SUCCESS, handles, value.size(), value.data(), cb_data); })); } protected: bool do_not_respond_to_reads = false; void SetUp(void) override { __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE); com::android::bluetooth::flags::provider_->leaudio_add_aics_support(true); bluetooth::manager::SetMockBtmInterface(&btm_interface); MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_); gatt::SetMockBtaGattInterface(&gatt_interface); gatt::SetMockBtaGattQueue(&gatt_queue); reset_mock_function_count_map(); ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(DoAll(Return(true))); // default action for GetCharacteristic function call ON_CALL(gatt_interface, GetCharacteristic(_, _)) .WillByDefault( Invoke([&](uint16_t conn_id, uint16_t handle) -> const gatt::Characteristic* { std::list& services = services_map[conn_id]; for (auto const& service : services) { for (auto const& characteristic : service.characteristics) { if (characteristic.value_handle == handle) { return &characteristic; } } } return nullptr; })); // default action for GetOwningService function call ON_CALL(gatt_interface, GetOwningService(_, _)) .WillByDefault(Invoke([&](uint16_t conn_id, uint16_t handle) -> const gatt::Service* { std::list& services = services_map[conn_id]; for (auto const& service : services) { if (service.handle <= handle && service.end_handle >= handle) { return &service; } } return nullptr; })); // default action for GetServices function call ON_CALL(gatt_interface, GetServices(_)) .WillByDefault(WithArg<0>(Invoke([&](uint16_t conn_id) -> std::list* { return &services_map[conn_id]; }))); // default action for RegisterForNotifications function call ON_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _)) .WillByDefault(Return(GATT_SUCCESS)); // default action for DeregisterForNotifications function call ON_CALL(gatt_interface, DeregisterForNotifications(gatt_if, _, _)) .WillByDefault(Return(GATT_SUCCESS)); // default action for WriteDescriptor function call ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _)) .WillByDefault(Invoke([](uint16_t conn_id, uint16_t handle, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) -> void { if (cb) { cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); } })); auto mock_alarm = AlarmMock::Get(); ON_CALL(*mock_alarm, AlarmNew(_)).WillByDefault(Invoke([](const char* /*name*/) { return new alarm_t(); })); ON_CALL(*mock_alarm, AlarmFree(_)).WillByDefault(Invoke([](alarm_t* alarm) { if (alarm) { free(alarm); } })); ON_CALL(*mock_alarm, AlarmCancel(_)).WillByDefault(Invoke([](alarm_t* alarm) { if (alarm) { alarm->cb = nullptr; alarm->data = nullptr; alarm->on_main_loop = false; } })); ON_CALL(*mock_alarm, AlarmIsScheduled(_)).WillByDefault(Invoke([](const alarm_t* alarm) { if (alarm) { return alarm->cb != nullptr; } return false; })); ON_CALL(*mock_alarm, AlarmSet(_, _, _, _)) .WillByDefault(Invoke( [](alarm_t* alarm, uint64_t /*interval_ms*/, alarm_callback_t cb, void* data) { if (alarm) { alarm->data = data; alarm->cb = cb; } })); ON_CALL(*mock_alarm, AlarmSetOnMloop(_, _, _, _)) .WillByDefault(Invoke( [](alarm_t* alarm, uint64_t /*interval_ms*/, alarm_callback_t cb, void* data) { if (alarm) { alarm->on_main_loop = true; alarm->data = data; alarm->cb = cb; } })); } void TearDown(void) override { com::android::bluetooth::flags::provider_->reset_flags(); services_map.clear(); gatt::SetMockBtaGattQueue(nullptr); gatt::SetMockBtaGattInterface(nullptr); bluetooth::manager::SetMockBtmInterface(nullptr); AlarmMock::Reset(); } void TestAppRegister(void) { BtaAppRegisterCallback app_register_callback; EXPECT_CALL(gatt_interface, AppRegister(_, _, _)) .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback))); VolumeControl::Initialize(&callbacks, base::DoNothing()); ASSERT_TRUE(gatt_callback); ASSERT_TRUE(app_register_callback); app_register_callback.Run(gatt_if, GATT_SUCCESS); ASSERT_TRUE(VolumeControl::IsVolumeControlRunning()); } void TestAppUnregister(void) { EXPECT_CALL(gatt_interface, AppDeregister(gatt_if)); VolumeControl::CleanUp(); ASSERT_FALSE(VolumeControl::IsVolumeControlRunning()); gatt_callback = nullptr; } void TestConnect(const RawAddress& address) { // by default indicate link as encrypted ON_CALL(btm_interface, BTM_IsEncrypted(address, _)).WillByDefault(DoAll(Return(true))); EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, true)); VolumeControl::Get()->Connect(address); Mock::VerifyAndClearExpectations(&gatt_interface); } void TestRemove(const RawAddress& address, uint16_t conn_id) { EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, true)); if (conn_id) { EXPECT_CALL(gatt_interface, Close(conn_id)); } else { EXPECT_CALL(gatt_interface, Close(conn_id)).Times(0); } VolumeControl::Get()->Remove(address); Mock::VerifyAndClearExpectations(&gatt_interface); } void TestDisconnect(const RawAddress& address, uint16_t conn_id) { if (conn_id) { EXPECT_CALL(gatt_interface, Close(conn_id)); } else { EXPECT_CALL(gatt_interface, Close(conn_id)).Times(0); } VolumeControl::Get()->Disconnect(address); Mock::VerifyAndClearExpectations(&gatt_interface); } void TestAddFromStorage(const RawAddress& address) { // by default indicate link as encrypted ON_CALL(btm_interface, BTM_IsEncrypted(address, _)).WillByDefault(DoAll(Return(true))); EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, true)); VolumeControl::Get()->AddFromStorage(address); } void TestSubscribeNotifications(const RawAddress& address, uint16_t conn_id, const std::map& handle_pairs) { SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(address); GetConnectedEvent(address, conn_id); EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _)).WillRepeatedly(DoDefault()); EXPECT_CALL(gatt_interface, RegisterForNotifications(_, _, _)).WillRepeatedly(DoDefault()); std::vector notify_value({0x01, 0x00}); for (auto const& handles : handle_pairs) { EXPECT_CALL(gatt_queue, WriteDescriptor(conn_id, handles.second, notify_value, GATT_WRITE, _, _)) .WillOnce(DoDefault()); EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, address, handles.first)) .WillOnce(DoDefault()); } GetSearchCompleteEvent(conn_id); TestAppUnregister(); } void TestReadCharacteristic(const RawAddress& address, uint16_t conn_id, std::vector handles) { SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(address); GetConnectedEvent(address, conn_id); tBTA_GATTC_MULTI received_to_read_1{}; tBTA_GATTC_MULTI received_to_read_2{}; if (!com::android::bluetooth::flags::le_ase_read_multiple_variable()) { EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)).WillRepeatedly(DoDefault()); for (auto const& handle : handles) { EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, handle, _, _)).WillOnce(DoDefault()); } } else { EXPECT_CALL(gatt_queue, ReadMultiCharacteristic(_, _, _, _)).Times(testing::AtLeast(1)); } GetSearchCompleteEvent(conn_id); TestAppUnregister(); } void GetConnectedEvent(const RawAddress& address, uint16_t conn_id, tGATT_STATUS status = GATT_SUCCESS) { tBTA_GATTC_OPEN event_data = { .status = status, .conn_id = conn_id, .client_if = gatt_if, .remote_bda = address, .transport = BT_TRANSPORT_LE, .mtu = 240, }; gatt_callback(BTA_GATTC_OPEN_EVT, reinterpret_cast(&event_data)); } void GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) { tBTA_GATTC_CLOSE event_data = { .conn_id = conn_id, .status = GATT_SUCCESS, .client_if = gatt_if, .remote_bda = address, .reason = GATT_CONN_TERMINATE_PEER_USER, }; gatt_callback(BTA_GATTC_CLOSE_EVT, reinterpret_cast(&event_data)); } void GetSearchCompleteEvent(uint16_t conn_id) { tBTA_GATTC_SEARCH_CMPL event_data = { .conn_id = conn_id, .status = GATT_SUCCESS, }; gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, reinterpret_cast(&event_data)); } void GetEncryptionCompleteEvt(const RawAddress& bda) { tBTA_GATTC cb_data{}; cb_data.enc_cmpl.client_if = gatt_if; cb_data.enc_cmpl.remote_bda = bda; gatt_callback(BTA_GATTC_ENC_CMPL_CB_EVT, &cb_data); } void SetEncryptionResult(const RawAddress& address, bool success) { ON_CALL(btm_interface, BTM_IsEncrypted(address, _)).WillByDefault(DoAll(Return(false))); ON_CALL(btm_interface, IsLinkKeyKnown(address, _)).WillByDefault(DoAll(Return(true))); ON_CALL(btm_interface, SetEncryption(address, _, _, _, BTM_BLE_SEC_ENCRYPT)) .WillByDefault( Invoke([&success, this](const RawAddress& bd_addr, tBT_TRANSPORT transport, tBTM_SEC_CALLBACK* p_callback, void* p_ref_data, tBTM_BLE_SEC_ACT /*sec_act*/) -> tBTM_STATUS { if (p_callback) { p_callback(bd_addr, transport, p_ref_data, success ? tBTM_STATUS::BTM_SUCCESS : tBTM_STATUS::BTM_FAILED_ON_SECURITY); } GetEncryptionCompleteEvt(bd_addr); return tBTM_STATUS::BTM_SUCCESS; })); EXPECT_CALL(btm_interface, SetEncryption(address, _, _, _, BTM_BLE_SEC_ENCRYPT)).Times(1); } void SetSampleDatabaseVCS(uint16_t conn_id) { set_sample_database(conn_id, true, false, false, false, false, false); } void SetSampleDatabaseAICS(uint16_t conn_id) { set_sample_database(conn_id, true, false, true, false, false, false); } void SetSampleDatabaseAICSBroken(uint16_t conn_id) { set_sample_database(conn_id, true, false, true, true, true, false); } void SetSampleDatabaseNoVCS(uint16_t conn_id) { set_sample_database(conn_id, false, false, true, false, true, false); } void SetSampleDatabaseVCSBroken(uint16_t conn_id) { set_sample_database(conn_id, true, true, true, false, true, false); } void SetSampleDatabaseVOCS(uint16_t conn_id) { set_sample_database(conn_id, true, false, false, false, true, false); } void SetSampleDatabaseVOCSBroken(uint16_t conn_id) { set_sample_database(conn_id, true, false, true, false, true, true); } void SetSampleDatabase(uint16_t conn_id) { set_sample_database(conn_id, true, false, true, false, true, false); } NiceMock callbacks; NiceMock btm_interface; MockCsisClient mock_csis_client_module_; NiceMock gatt_interface; NiceMock gatt_queue; tBTA_GATTC_CBACK* gatt_callback; const uint8_t gatt_if = 0xff; std::map> services_map; }; TEST_F(VolumeControlTest, test_get_uninitialized) { ASSERT_DEATH(VolumeControl::Get(), ""); } TEST_F(VolumeControlTest, test_initialize) { bool init_cb_called = false; BtaAppRegisterCallback app_register_callback; EXPECT_CALL(gatt_interface, AppRegister(_, _, _)) .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback))); VolumeControl::Initialize( &callbacks, base::Bind([](bool* init_cb_called) { *init_cb_called = true; }, &init_cb_called)); ASSERT_TRUE(gatt_callback); ASSERT_TRUE(app_register_callback); app_register_callback.Run(gatt_if, GATT_SUCCESS); ASSERT_TRUE(init_cb_called); ASSERT_TRUE(VolumeControl::IsVolumeControlRunning()); VolumeControl::CleanUp(); } TEST_F(VolumeControlTest, test_initialize_twice) { VolumeControl::Initialize(&callbacks, base::DoNothing()); VolumeControl* volume_control_p = VolumeControl::Get(); VolumeControl::Initialize(&callbacks, base::DoNothing()); ASSERT_EQ(volume_control_p, VolumeControl::Get()); VolumeControl::CleanUp(); } TEST_F(VolumeControlTest, test_cleanup_initialized) { VolumeControl::Initialize(&callbacks, base::DoNothing()); VolumeControl::CleanUp(); ASSERT_FALSE(VolumeControl::IsVolumeControlRunning()); } TEST_F(VolumeControlTest, test_cleanup_uninitialized) { VolumeControl::CleanUp(); ASSERT_FALSE(VolumeControl::IsVolumeControlRunning()); } TEST_F(VolumeControlTest, test_app_registration) { TestAppRegister(); TestAppUnregister(); } TEST_F(VolumeControlTest, test_connect) { TestAppRegister(); TestConnect(GetTestAddress(0)); TestAppUnregister(); } TEST_F(VolumeControlTest, test_connect_after_remove) { TestAppRegister(); const RawAddress test_address = GetTestAddress(0); uint16_t conn_id = 1; TestConnect(test_address); GetConnectedEvent(test_address, conn_id); Mock::VerifyAndClearExpectations(&callbacks); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1); TestRemove(test_address, conn_id); Mock::VerifyAndClearExpectations(&callbacks); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1); ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(DoAll(Return(false))); VolumeControl::Get()->Connect(test_address); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_reconnect_after_interrupted_discovery) { const RawAddress test_address = GetTestAddress(0); // Initial connection - no callback calls yet as we want to disconnect in the // middle SetSampleDatabaseVOCS(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(0); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0); GetConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&callbacks); // Remote disconnects in the middle of the service discovery EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); GetDisconnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&callbacks); // This time let the service discovery pass ON_CALL(gatt_interface, ServiceSearchRequest(_, _)) .WillByDefault(Invoke([&](uint16_t conn_id, const bluetooth::Uuid* p_srvc_uuid) -> void { if (*p_srvc_uuid == kVolumeControlUuid) { GetSearchCompleteEvent(conn_id); } })); // Remote is being connected by another GATT client EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)); GetConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&callbacks); // Request connect when the remote was already connected by another service EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); VolumeControl::Get()->Connect(test_address); // The GetConnectedEvent(test_address, 1); should not be triggered here, since // GATT implementation will not send this event for the already connected // device Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_verify_opportunistic_connect_active_after_connect_timeout) { const RawAddress address = GetTestAddress(0); TestAppRegister(); TestAddFromStorage(address); Mock::VerifyAndClearExpectations(&gatt_interface); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address)).Times(1); TestConnect(address); EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, _)).Times(0); EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, true)).Times(1); GetConnectedEvent(address, 1, GATT_ERROR); Mock::VerifyAndClearExpectations(&callbacks); Mock::VerifyAndClearExpectations(&gatt_interface); TestAppUnregister(); } TEST_F(VolumeControlTest, test_reconnect_after_timeout) { const RawAddress address = GetTestAddress(0); // Initial connection SetSampleDatabaseVOCS(1); TestAppRegister(); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, address)).Times(0); TestConnect(address); // Disconnect not connected device - upper layer times out and needs a // disconnection event to leave the transient Connecting state EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address)); EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, _)).Times(0); TestDisconnect(address, 0); // Above the device was not connected and we got Disconnect request from the // upper layer - it means it has timed-out but still wants to connect, thus // native is still doing background or opportunistic connect. Let the remote // device reconnect now. ON_CALL(gatt_interface, ServiceSearchRequest(_, _)) .WillByDefault(Invoke([&](uint16_t conn_id, const bluetooth::Uuid* p_srvc_uuid) -> void { if (*p_srvc_uuid == kVolumeControlUuid) { GetSearchCompleteEvent(conn_id); } })); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, address)); EXPECT_CALL(callbacks, OnDeviceAvailable(address, 2, _)); GetConnectedEvent(address, 1); Mock::VerifyAndClearExpectations(&callbacks); // Make sure that the upper layer gets the disconnection event even if not // connecting actively anymore due to the mentioned time-out mechanism. EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address)); GetDisconnectedEvent(address, 1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_add_from_storage) { TestAppRegister(); TestAddFromStorage(GetTestAddress(0)); TestAppUnregister(); } TEST_F(VolumeControlTest, test_remove_non_connected) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); TestRemove(test_address, 0); TestAppUnregister(); } TEST_F(VolumeControlTest, test_remove_connected) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, 1); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); TestDisconnect(test_address, 1); TestAppUnregister(); } TEST_F(VolumeControlTest, test_disconnect_non_connected) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); TestDisconnect(test_address, 0); TestAppUnregister(); } TEST_F(VolumeControlTest, test_disconnect_connected) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, 1); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); TestDisconnect(test_address, 1); TestAppUnregister(); } TEST_F(VolumeControlTest, test_disconnected) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, 1); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); GetDisconnectedEvent(test_address, 1); TestAppUnregister(); } TEST_F(VolumeControlTest, test_disconnected_while_autoconnect) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestAddFromStorage(test_address); GetConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&gatt_interface); // autoconnect - don't indicate disconnection EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0); GetDisconnectedEvent(test_address, 1); TestAppUnregister(); } TEST_F(VolumeControlTest, test_disconnect_when_link_key_gone) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestAddFromStorage(test_address); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(false))); ON_CALL(btm_interface, SetEncryption(test_address, _, _, _, BTM_BLE_SEC_ENCRYPT)) .WillByDefault(Return(tBTM_STATUS::BTM_ERR_KEY_MISSING)); // autoconnect - don't indicate disconnection EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0); EXPECT_CALL(gatt_interface, Close(1)); GetConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&btm_interface); TestAppUnregister(); } TEST_F(VolumeControlTest, test_reconnect_after_encryption_failed) { const RawAddress test_address = GetTestAddress(0); TestAppRegister(); TestAddFromStorage(test_address); SetEncryptionResult(test_address, false); // autoconnect - don't indicate disconnection EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0); GetConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&btm_interface); SetEncryptionResult(test_address, true); GetConnectedEvent(test_address, 1); TestAppUnregister(); } TEST_F(VolumeControlTest, test_service_discovery_completed_before_encryption) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVCS(1); TestAppRegister(); TestConnect(test_address); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(false))); ON_CALL(btm_interface, IsLinkKeyKnown(test_address, _)).WillByDefault(DoAll(Return(true))); ON_CALL(btm_interface, SetEncryption(test_address, _, _, _, _)) .WillByDefault(Return(tBTM_STATUS::BTM_SUCCESS)); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(0); uint16_t conn_id = 1; GetConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); Mock::VerifyAndClearExpectations(&btm_interface); Mock::VerifyAndClearExpectations(&callbacks); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(1); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(true))); EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)); GetEncryptionCompleteEvt(test_address); GetSearchCompleteEvent(conn_id); Mock::VerifyAndClearExpectations(&callbacks); Mock::VerifyAndClearExpectations(&gatt_interface); TestAppUnregister(); } TEST_F(VolumeControlTest, test_discovery_vcs_found) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVCS(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, _, _)); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_discovery_vcs_not_found) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseNoVCS(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_discovery_vcs_broken) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVCSBroken(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_subscribe_vcs_volume_state) { std::map handles({{0x0021, 0x0022}}); TestSubscribeNotifications(GetTestAddress(0), 1, handles); } TEST_F(VolumeControlTest, test_subscribe_vocs_offset_state) { std::map handles({{0x0072, 0x0073}, {0x0082, 0x0083}}); TestSubscribeNotifications(GetTestAddress(0), 1, handles); } TEST_F(VolumeControlTest, test_subscribe_vocs_offset_location) { std::map handles({{0x0085, 0x0086}}); TestSubscribeNotifications(GetTestAddress(0), 1, handles); } TEST_F(VolumeControlTest, test_subscribe_vocs_output_description) { std::map handles({{0x008a, 0x008b}}); TestSubscribeNotifications(GetTestAddress(0), 1, handles); } TEST_F(VolumeControlTest, test_read_vcs_volume_state) { const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, true)).Times(1); std::vector handles({0x0021}); TestReadCharacteristic(test_address, 1, handles); } TEST_F(VolumeControlTest, test_read_vcs_volume_flags) { std::vector handles({0x0026}); TestReadCharacteristic(GetTestAddress(0), 1, handles); } TEST_F(VolumeControlTest, test_read_vocs_volume_offset) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false); const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0072, 0x0082}); TestReadCharacteristic(test_address, 1, handles); Mock::VerifyAndClearExpectations(&callbacks); } TEST_F(VolumeControlTest, test_read_vocs_volume_offset_multi) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true); const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0072, 0x0082}); TestReadCharacteristic(test_address, 1, handles); Mock::VerifyAndClearExpectations(&callbacks); } TEST_F(VolumeControlTest, test_read_vocs_offset_location) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false); const RawAddress test_address = GetTestAddress(0); // It is called twice because after connect read is done once and second read is coming from the // test. EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0075, 0x0085}); TestReadCharacteristic(test_address, 1, handles); Mock::VerifyAndClearExpectations(&callbacks); } TEST_F(VolumeControlTest, test_read_vocs_offset_location_multi) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true); const RawAddress test_address = GetTestAddress(0); // It is called twice because after connect read is done once and second read is coming from the // test. EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0075, 0x0085}); TestReadCharacteristic(test_address, 1, handles); Mock::VerifyAndClearExpectations(&callbacks); } TEST_F(VolumeControlTest, test_read_vocs_output_description) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false); const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0079, 0x008a}); TestReadCharacteristic(test_address, 1, handles); } TEST_F(VolumeControlTest, test_read_vocs_output_description_multi) { com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true); const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1); std::vector handles({0x0079, 0x008a}); TestReadCharacteristic(test_address, 1, handles); } TEST_F(VolumeControlTest, test_discovery_vocs_found) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVOCS(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_discovery_vocs_not_found) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVCS(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 0, _)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_discovery_vocs_broken) { const RawAddress test_address = GetTestAddress(0); SetSampleDatabaseVOCSBroken(1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 1, _)); GetConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&callbacks); TestAppUnregister(); } TEST_F(VolumeControlTest, test_read_vcs_database_out_of_sync) { const RawAddress test_address = GetTestAddress(0); EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, true)); std::vector handles({0x0021}); uint16_t conn_id = 1; SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, conn_id); EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)).WillRepeatedly(DoDefault()); for (auto const& handle : handles) { EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, handle, _, _)).WillOnce(DoDefault()); } GetSearchCompleteEvent(conn_id); /* Simulate database change on the remote side. */ ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)) .WillByDefault(Invoke([this](uint16_t conn_id, uint16_t handle, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) { auto* svc = gatt::FindService(services_map[conn_id], handle); if (svc == nullptr) { return; } tGATT_STATUS status = GATT_DATABASE_OUT_OF_SYNC; if (cb) { cb(conn_id, status, handle, value.size(), value.data(), cb_data); } })); ON_CALL(gatt_interface, ServiceSearchRequest(_, _)).WillByDefault(Return()); EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)); VolumeControl::Get()->SetVolume(test_address, 15); Mock::VerifyAndClearExpectations(&gatt_interface); TestAppUnregister(); } class VolumeControlCallbackTest : public VolumeControlTest { protected: const RawAddress test_address = GetTestAddress(0); uint16_t conn_id = 22; void SetUp(void) override { VolumeControlTest::SetUp(); SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); } void TearDown(void) override { TestAppUnregister(); VolumeControlTest::TearDown(); } void GetNotificationEvent(uint16_t handle, const std::vector& value) { tBTA_GATTC_NOTIFY event_data = { .conn_id = conn_id, .bda = test_address, .handle = handle, .len = (uint8_t)value.size(), .is_notify = true, }; std::copy(value.begin(), value.end(), event_data.value); gatt_callback(BTA_GATTC_NOTIF_EVT, reinterpret_cast(&event_data)); } }; TEST_F(VolumeControlCallbackTest, test_volume_state_changed_stress) { std::vector value({0x03, 0x01, 0x02}); EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, 0x03, true, _, true)); GetNotificationEvent(0x0021, value); } TEST_F(VolumeControlCallbackTest, test_volume_state_changed_malformed) { EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, _)).Times(0); std::vector too_short({0x03, 0x01}); GetNotificationEvent(0x0021, too_short); std::vector too_long({0x03, 0x01, 0x02, 0x03}); GetNotificationEvent(0x0021, too_long); } TEST_F(VolumeControlCallbackTest, audio_input_state_changed__invalid_mute__is_rejected) { uint8_t invalid_mute = 0x03; std::vector value({0x03, invalid_mute, (uint8_t)GainMode::MANUAL, 0x04}); EXPECT_CALL(callbacks, OnExtAudioInStateChanged(_, _, _, _, _)).Times(0); GetNotificationEvent(0x0032, value); } TEST_F(VolumeControlCallbackTest, audio_input_state_changed__invalid_gain_mode__is_rejected) { uint8_t invalid_gain_mode = 0x06; std::vector value({0x03, (uint8_t)Mute::MUTED, invalid_gain_mode, 0x04}); EXPECT_CALL(callbacks, OnExtAudioInStateChanged(_, _, _, _, _)).Times(0); GetNotificationEvent(0x0032, value); } TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed__muted) { std::vector value({0x03, (uint8_t)Mute::MUTED, (uint8_t)GainMode::MANUAL, 0x04}); EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, 0x03, Mute::MUTED, GainMode::MANUAL)); GetNotificationEvent(0x0032, value); } TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed__disabled) { std::vector value({0x03, (uint8_t)Mute::DISABLED, (uint8_t)GainMode::MANUAL, 0x04}); EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, 0x03, Mute::DISABLED, GainMode::MANUAL)); GetNotificationEvent(0x0032, value); } TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed_malformed) { EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, _, _, _)).Times(0); std::vector too_short({0x03, 0x01, 0x02}); GetNotificationEvent(0x0032, too_short); std::vector too_long({0x03, 0x01, 0x02, 0x04, 0x05}); GetNotificationEvent(0x0032, too_long); } TEST_F(VolumeControlCallbackTest, test_audio_gain_props_changed) { std::vector value({0x03, 0x01, 0x02}); EXPECT_CALL(callbacks, OnExtAudioInGainSettingPropertiesChanged(test_address, _, 0x03, 0x01, 0x02)); GetNotificationEvent(0x0055, value); } TEST_F(VolumeControlCallbackTest, test_audio_gain_props_changed_malformed) { EXPECT_CALL(callbacks, OnExtAudioInGainSettingPropertiesChanged(test_address, _, _, _, _)) .Times(0); std::vector too_short({0x03, 0x01}); GetNotificationEvent(0x0055, too_short); std::vector too_long({0x03, 0x01, 0x02, 0x03}); GetNotificationEvent(0x0055, too_long); } TEST_F(VolumeControlCallbackTest, test_audio_input_status_changed) { std::vector value({static_cast(bluetooth::vc::VolumeInputStatus::Inactive)}); EXPECT_CALL(callbacks, OnExtAudioInStatusChanged(test_address, _, bluetooth::vc::VolumeInputStatus::Inactive)); GetNotificationEvent(0x0039, value); } TEST_F(VolumeControlCallbackTest, test_audio_input_status_changed_malformed) { EXPECT_CALL(callbacks, OnExtAudioInStatusChanged(test_address, _, _)).Times(0); std::vector too_short(0); GetNotificationEvent(0x0039, too_short); std::vector too_long({0x03, 0x01}); GetNotificationEvent(0x0039, too_long); } TEST_F(VolumeControlCallbackTest, test_audio_input_description_changed) { std::string description = "SPDIF"; std::vector value(description.begin(), description.end()); EXPECT_CALL(callbacks, OnExtAudioInDescriptionChanged(test_address, _, description, _)); GetNotificationEvent(0x005e, value); } TEST_F(VolumeControlCallbackTest, test_volume_offset_changed) { std::vector value({0x04, 0x05, 0x06}); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, 0x0504)); GetNotificationEvent(0x0082, value); } TEST_F(VolumeControlCallbackTest, test_volume_offset_changed_malformed) { EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(0); std::vector too_short({0x04}); GetNotificationEvent(0x0082, too_short); std::vector too_long({0x04, 0x05, 0x06, 0x07}); GetNotificationEvent(0x0082, too_long); } TEST_F(VolumeControlCallbackTest, test_offset_location_changed) { std::vector value({0x01, 0x02, 0x03, 0x04}); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201)); GetNotificationEvent(0x0085, value); } TEST_F(VolumeControlCallbackTest, test_offset_location_changed_malformed) { EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(0); std::vector too_short({0x04}); GetNotificationEvent(0x0085, too_short); std::vector too_long({0x04, 0x05, 0x06}); GetNotificationEvent(0x0085, too_long); } TEST_F(VolumeControlCallbackTest, test_audio_output_description_changed) { std::string descr = "left"; std::vector value(descr.begin(), descr.end()); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr)); GetNotificationEvent(0x008a, value); } class VolumeControlValueGetTest : public VolumeControlTest { protected: const RawAddress test_address = GetTestAddress(0); uint16_t conn_id = 22; GATT_READ_OP_CB cb; void* cb_data; uint16_t handle; void SetUp(void) override { VolumeControlTest::SetUp(); SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)) .WillOnce(DoAll(SaveArg<1>(&handle), SaveArg<2>(&cb), SaveArg<3>(&cb_data))); } void TearDown(void) override { TestAppUnregister(); cb = nullptr; cb_data = nullptr; handle = 0; VolumeControlTest::TearDown(); } }; TEST_F(VolumeControlValueGetTest, test_get_ext_audio_out_volume_offset) { VolumeControl::Get()->GetExtAudioOutVolumeOffset(test_address, 1); EXPECT_TRUE(cb); std::vector value({0x01, 0x02, 0x03}); EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, 0x0201)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_out_location) { VolumeControl::Get()->GetExtAudioOutLocation(test_address, 2); EXPECT_TRUE(cb); std::vector value({0x01, 0x02, 0x03, 0x04}); EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_out_description) { VolumeControl::Get()->GetExtAudioOutDescription(test_address, 2); EXPECT_TRUE(cb); std::string descr = "right"; std::vector value(descr.begin(), descr.end()); EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_state) { VolumeControl::Get()->GetExtAudioInState(test_address, 1); EXPECT_TRUE(cb); std::vector value({0x01, (uint8_t)Mute::NOT_MUTED, (uint8_t)GainMode::MANUAL, 0x03}); EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, 1, 0x01, Mute::NOT_MUTED, GainMode::MANUAL)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_status) { VolumeControl::Get()->GetExtAudioInStatus(test_address, 0); EXPECT_TRUE(cb); std::vector value({static_cast(bluetooth::vc::VolumeInputStatus::Active)}); EXPECT_CALL(callbacks, OnExtAudioInStatusChanged(test_address, 0, bluetooth::vc::VolumeInputStatus::Active)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_gain_props) { VolumeControl::Get()->GetExtAudioInGainProps(test_address, 0); EXPECT_TRUE(cb); std::vector value({0x01, 0x02, 0x03}); EXPECT_CALL(callbacks, OnExtAudioInGainSettingPropertiesChanged(test_address, 0, 0x01, 0x02, 0x03)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_description) { VolumeControl::Get()->GetExtAudioInDescription(test_address, 1); EXPECT_TRUE(cb); std::string description = "AUX-IN"; std::vector value(description.begin(), description.end()); EXPECT_CALL(callbacks, OnExtAudioInDescriptionChanged(test_address, 1, description, _)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_type) { VolumeControl::Get()->GetExtAudioInType(test_address, 1); EXPECT_TRUE(cb); std::vector value({static_cast(bluetooth::vc::VolumeInputType::Ambient)}); EXPECT_CALL(callbacks, OnExtAudioInTypeChanged(test_address, 1, bluetooth::vc::VolumeInputType::Ambient)); cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data); } class VolumeControlValueSetTest : public VolumeControlTest { protected: const RawAddress test_address = GetTestAddress(0); uint16_t conn_id = 22; void SetUp(void) override { VolumeControlTest::SetUp(); SetSampleDatabase(conn_id); TestAppRegister(); TestConnect(test_address); GetConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); ON_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, _, GATT_WRITE, _, _)) .WillByDefault([this](uint16_t conn_id, uint16_t /*handle*/, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) { uint8_t write_rsp; std::vector ntf_value({value[0], 0, static_cast(value[1] + 1)}); switch (value[0]) { case 0x06: // mute ntf_value[1] = 1; break; case 0x05: // unmute break; case 0x04: // set abs. volume ntf_value[0] = value[2]; ntf_value[1] = (value[2] ? 0 : 1); break; case 0x03: // unmute rel. up break; case 0x02: // unmute rel. down break; case 0x01: // rel. up break; case 0x00: // rel. down break; default: break; } GetNotificationEvent(0x0021, ntf_value); cb(conn_id, GATT_SUCCESS, 0x0024, 0, &write_rsp, cb_data); }); } void GetNotificationEvent(uint16_t handle, const std::vector& value) { tBTA_GATTC_NOTIFY event_data = { .conn_id = conn_id, .bda = test_address, .handle = handle, .len = (uint8_t)value.size(), .is_notify = true, }; std::copy(value.begin(), value.end(), event_data.value); gatt_callback(BTA_GATTC_NOTIF_EVT, reinterpret_cast(&event_data)); } void TearDown(void) override { TestAppUnregister(); VolumeControlTest::TearDown(); } }; TEST_F(VolumeControlValueSetTest, test_volume_operation_failed) { const std::vector vol_x10({0x04, 0x00, 0x10}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1); ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)) .WillByDefault(Invoke([this](uint16_t conn_id, uint16_t handle, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) { auto* svc = gatt::FindService(services_map[conn_id], handle); if (svc == nullptr) { return; } tGATT_STATUS status = GATT_ERROR; if (cb) { cb(conn_id, status, handle, value.size(), value.data(), cb_data); } })); EXPECT_CALL(*AlarmMock::Get(), AlarmSetOnMloop(_, _, _, _)).Times(1); EXPECT_CALL(*AlarmMock::Get(), AlarmCancel(_)).Times(1); VolumeControl::Get()->SetVolume(test_address, 0x10); Mock::VerifyAndClearExpectations(&gatt_queue); Mock::VerifyAndClearExpectations(AlarmMock::Get()); } TEST_F(VolumeControlValueSetTest, test_volume_operation_failed_due_to_device_disconnection) { const std::vector vol_x10({0x04, 0x00, 0x10}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1); ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)) .WillByDefault(Invoke([](uint16_t /*conn_id*/, uint16_t /*handle*/, std::vector /*value*/, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB /*cb*/, void* /*cb_data*/) { /* Do nothing */ })); EXPECT_CALL(*AlarmMock::Get(), AlarmSetOnMloop(_, _, _, _)).Times(0); alarm_callback_t active_alarm_cb = nullptr; EXPECT_CALL(*AlarmMock::Get(), AlarmSetOnMloop(_, _, _, _)) .WillOnce(Invoke([&](alarm_t* alarm, uint64_t /*interval_ms*/, alarm_callback_t cb, void* /*data*/) { if (alarm) { alarm->on_main_loop = true; alarm->cb = cb; active_alarm_cb = cb; } })); ON_CALL(*AlarmMock::Get(), AlarmCancel(_)).WillByDefault(Invoke([&](alarm_t* alarm) { if (alarm) { alarm->cb = nullptr; alarm->on_main_loop = false; active_alarm_cb = nullptr; } })); VolumeControl::Get()->SetVolume(test_address, 0x10); Mock::VerifyAndClearExpectations(&gatt_queue); Mock::VerifyAndClearExpectations(AlarmMock::Get()); ASSERT_NE(active_alarm_cb, nullptr); EXPECT_CALL(*AlarmMock::Get(), AlarmCancel(_)).Times(1); EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)); GetDisconnectedEvent(test_address, conn_id); ASSERT_EQ(active_alarm_cb, nullptr); Mock::VerifyAndClearExpectations(&callbacks); } TEST_F(VolumeControlValueSetTest, test_set_volume) { const std::vector vol_x10({0x04, 0x00, 0x10}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1); VolumeControl::Get()->SetVolume(test_address, 0x10); // Same volume level should not be applied twice const std::vector vol_x10_2({0x04, 0x01, 0x10}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10_2, GATT_WRITE, _, _)) .Times(0); VolumeControl::Get()->SetVolume(test_address, 0x10); const std::vector vol_x20({0x04, 0x01, 0x20}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x20, GATT_WRITE, _, _)).Times(1); VolumeControl::Get()->SetVolume(test_address, 0x20); } TEST_F(VolumeControlValueSetTest, test_set_volume_stress) { uint8_t n = 100; uint8_t change_cnt = 0; uint8_t vol = 1; for (uint8_t i = 1; i < n; i++) { const std::vector vol_x10({0x04, change_cnt, vol}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)) .Times(1); VolumeControl::Get()->SetVolume(test_address, vol); Mock::VerifyAndClearExpectations(&gatt_queue); change_cnt++; vol++; } } TEST_F(VolumeControlValueSetTest, test_set_volume_stress_2) { uint8_t change_cnt = 0; uint8_t vol = 1; // In this test we simulate notification coming later and operations will be // queued ON_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, _, GATT_WRITE, _, _)) .WillByDefault([](uint16_t conn_id, uint16_t handle, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) { uint8_t write_rsp; switch (value[0]) { case 0x04: // set abs. volume break; default: break; } cb(conn_id, GATT_SUCCESS, handle, 0, &write_rsp, cb_data); }); const std::vector vol_x10({0x04, /*change_cnt*/ 0, 0x10}); std::vector ntf_value_x10({0x10, 0, 1}); const std::vector vol_x11({0x04, /*change_cnt*/ 1, 0x11}); std::vector ntf_value_x11({0x11, 0, 2}); const std::vector vol_x12({0x04, /*change_cnt*/ 2, 0x12}); std::vector ntf_value_x12({0x12, 0, 3}); const std::vector vol_x13({0x04, /*change_cnt*/ 3, 0x13}); std::vector ntf_value_x13({0x13, 0, 4}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x11, GATT_WRITE, _, _)).Times(1); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x12, GATT_WRITE, _, _)).Times(1); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x13, GATT_WRITE, _, _)).Times(1); VolumeControl::Get()->SetVolume(test_address, 0x10); VolumeControl::Get()->SetVolume(test_address, 0x11); GetNotificationEvent(0x0021, ntf_value_x10); GetNotificationEvent(0x0021, ntf_value_x11); VolumeControl::Get()->SetVolume(test_address, 0x12); VolumeControl::Get()->SetVolume(test_address, 0x13); GetNotificationEvent(0x0021, ntf_value_x12); GetNotificationEvent(0x0021, ntf_value_x13); Mock::VerifyAndClearExpectations(&gatt_queue); } TEST_F(VolumeControlValueSetTest, test_set_volume_stress_3) { uint8_t change_cnt = 0; uint8_t vol = 1; /* In this test we simulate notification coming later and operations will be * queued but some will be removed from the queue */ ON_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, _, GATT_WRITE, _, _)) .WillByDefault([](uint16_t conn_id, uint16_t handle, std::vector value, tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, void* cb_data) { uint8_t write_rsp; switch (value[0]) { case 0x04: // set abs. volume break; default: break; } cb(conn_id, GATT_SUCCESS, handle, 0, &write_rsp, cb_data); }); const std::vector vol_x10({0x04, /*change_cnt*/ 0, 0x10}); std::vector ntf_value_x10({0x10, 0, 1}); const std::vector vol_x11({0x04, /*change_cnt*/ 1, 0x11}); std::vector ntf_value_x11({0x11, 0, 2}); const std::vector vol_x12({0x04, /*change_cnt*/ 1, 0x12}); std::vector ntf_value_x12({0x12, 0, 3}); const std::vector vol_x13({0x04, /*change_cnt*/ 1, 0x13}); std::vector ntf_value_x13({0x13, 0, 4}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x10, GATT_WRITE, _, _)).Times(1); // Those two belowe will be removed from the queue EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x11, GATT_WRITE, _, _)).Times(0); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x12, GATT_WRITE, _, _)).Times(0); // This one shall be sent with a change count 1. EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, vol_x13, GATT_WRITE, _, _)).Times(1); VolumeControl::Get()->SetVolume(test_address, 0x10); VolumeControl::Get()->SetVolume(test_address, 0x11); VolumeControl::Get()->SetVolume(test_address, 0x12); VolumeControl::Get()->SetVolume(test_address, 0x13); GetNotificationEvent(0x0021, ntf_value_x10); GetNotificationEvent(0x0021, ntf_value_x11); GetNotificationEvent(0x0021, ntf_value_x12); GetNotificationEvent(0x0021, ntf_value_x13); Mock::VerifyAndClearExpectations(&gatt_queue); } TEST_F(VolumeControlValueSetTest, test_mute_unmute) { std::vector mute_x0({0x06, 0x00}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, mute_x0, GATT_WRITE, _, _)).Times(1); // Don't mute when already muted std::vector mute_x1({0x06, 0x01}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, mute_x1, GATT_WRITE, _, _)).Times(0); VolumeControl::Get()->Mute(test_address); VolumeControl::Get()->Mute(test_address); // Needs to be muted to unmute std::vector unmute_x1({0x05, 0x01}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, unmute_x1, GATT_WRITE, _, _)) .Times(1); // Don't unmute when already unmuted std::vector unmute_x2({0x05, 0x02}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0024, unmute_x2, GATT_WRITE, _, _)) .Times(0); VolumeControl::Get()->UnMute(test_address); VolumeControl::Get()->UnMute(test_address); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_out_volume_offset) { std::vector expected_data({0x01, 0x00, 0x34, 0x12}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0088, expected_data, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioOutVolumeOffset(test_address, 2, 0x1234); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_out_location) { std::vector expected_data({0x44, 0x33, 0x22, 0x11}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x0085, expected_data, GATT_WRITE_NO_RSP, _, _)); VolumeControl::Get()->SetExtAudioOutLocation(test_address, 2, 0x11223344); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_out_location_non_writable) { EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0); VolumeControl::Get()->SetExtAudioOutLocation(test_address, 1, 0x11223344); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_out_description) { std::string descr = "right front"; std::vector expected_data(descr.begin(), descr.end()); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x008a, expected_data, GATT_WRITE_NO_RSP, _, _)); VolumeControl::Get()->SetExtAudioOutDescription(test_address, 2, descr); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_out_description_non_writable) { std::string descr = "left front"; EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0); VolumeControl::Get()->SetExtAudioOutDescription(test_address, 1, descr); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_description) { std::string descr = "HDMI"; std::vector expected_data(descr.begin(), descr.end()); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005e, expected_data, GATT_WRITE_NO_RSP, _, _)); VolumeControl::Get()->SetExtAudioInDescription(test_address, 1, descr); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_description_non_writable) { std::string descr = "AUX"; std::vector expected_data(descr.begin(), descr.end()); EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0); VolumeControl::Get()->SetExtAudioInDescription(test_address, 0, descr); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_setting) { std::vector expected_data({0x01, 0x00, 0x34}); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, expected_data, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioInGainSetting(test_address, 1, 0x34); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_mode) { std::vector mode_manual({0x04, 0x00}); // 0x04 is the opcode for Manual EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mode_manual, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioInGainMode(test_address, 1, GainMode::MANUAL); std::vector mode_automatic({0x05, 0x00}); // 0x05 is the opcode for Automatic EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mode_automatic, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioInGainMode(test_address, 1, GainMode::AUTOMATIC); } TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_mute) { std::vector mute({0x03, 0x00}); // 0x03 is the opcode for Mute EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mute, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioInMute(test_address, 1, Mute::MUTED); std::vector unmute({0x02, 0x00}); // 0x02 is the opcode for UnMute EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, unmute, GATT_WRITE, _, _)); VolumeControl::Get()->SetExtAudioInMute(test_address, 1, Mute::NOT_MUTED); } class VolumeControlCsis : public VolumeControlTest { protected: const RawAddress test_address_1 = GetTestAddress(0); const RawAddress test_address_2 = GetTestAddress(1); std::vector csis_group = {test_address_1, test_address_2}; uint16_t conn_id_1 = 22; uint16_t conn_id_2 = 33; int group_id = 5; void SetUp(void) override { VolumeControlTest::SetUp(); ON_CALL(mock_csis_client_module_, Get()).WillByDefault(Return(&mock_csis_client_module_)); // Report working CSIS ON_CALL(mock_csis_client_module_, IsCsisClientRunning()).WillByDefault(Return(true)); ON_CALL(mock_csis_client_module_, GetDeviceList(_)).WillByDefault(Return(csis_group)); ON_CALL(mock_csis_client_module_, GetGroupId(_, _)).WillByDefault(Return(group_id)); SetSampleDatabase(conn_id_1); SetSampleDatabase(conn_id_2); TestAppRegister(); } void TearDown(void) override { TestAppUnregister(); VolumeControlTest::TearDown(); } void GetNotificationEvent(uint16_t conn_id, const RawAddress& test_address, uint16_t handle, const std::vector& value) { tBTA_GATTC_NOTIFY event_data = { .conn_id = conn_id, .bda = test_address, .handle = handle, .len = (uint8_t)value.size(), .is_notify = true, }; std::copy(value.begin(), value.end(), event_data.value); gatt_callback(BTA_GATTC_NOTIF_EVT, reinterpret_cast(&event_data)); } }; TEST_F(VolumeControlCsis, test_set_volume) { TestConnect(test_address_1); GetConnectedEvent(test_address_1, conn_id_1); GetSearchCompleteEvent(conn_id_1); TestConnect(test_address_2); GetConnectedEvent(test_address_2, conn_id_2); GetSearchCompleteEvent(conn_id_2); /* Set value for the group */ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _)); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_2, 0x0024, _, GATT_WRITE, _, _)); VolumeControl::Get()->SetVolume(group_id, 10); /* Now inject notification and make sure callback is sent up to Java layer */ EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, true, false)); std::vector value({0x03, 0x01, 0x02}); GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value); GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value); /* Verify exactly one operation with this exact value is queued for each * device */ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _)).Times(1); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_2, 0x0024, _, GATT_WRITE, _, _)).Times(1); VolumeControl::Get()->SetVolume(test_address_1, 20); VolumeControl::Get()->SetVolume(test_address_2, 20); VolumeControl::Get()->SetVolume(test_address_1, 20); VolumeControl::Get()->SetVolume(test_address_2, 20); EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address_1, 20, false, _, false)); EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address_2, 20, false, _, false)); std::vector value2({20, 0x00, 0x03}); GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value2); GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value2); } TEST_F(VolumeControlCsis, test_set_volume_device_not_ready) { /* Make sure we did not get responds to the initial reads, * so that the device was not marked as ready yet. */ do_not_respond_to_reads = true; TestConnect(test_address_1); GetConnectedEvent(test_address_1, conn_id_1); GetSearchCompleteEvent(conn_id_1); TestConnect(test_address_2); GetConnectedEvent(test_address_2, conn_id_2); GetSearchCompleteEvent(conn_id_2); /* Set value for the group */ EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_1, 0x0024, _, GATT_WRITE, _, _)).Times(0); EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id_2, 0x0024, _, GATT_WRITE, _, _)).Times(0); VolumeControl::Get()->SetVolume(group_id, 10); } TEST_F(VolumeControlCsis, autonomus_test_set_volume) { TestConnect(test_address_1); GetConnectedEvent(test_address_1, conn_id_1); GetSearchCompleteEvent(conn_id_1); TestConnect(test_address_2); GetConnectedEvent(test_address_2, conn_id_2); GetSearchCompleteEvent(conn_id_2); /* Now inject notification and make sure callback is sent up to Java layer */ EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true)); std::vector value({0x03, 0x00, 0x02}); GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value); GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value); } TEST_F(VolumeControlCsis, autonomus_single_device_test_set_volume) { TestConnect(test_address_1); GetConnectedEvent(test_address_1, conn_id_1); GetSearchCompleteEvent(conn_id_1); TestConnect(test_address_2); GetConnectedEvent(test_address_2, conn_id_2); GetSearchCompleteEvent(conn_id_2); /* Disconnect one device. */ EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address_1)); GetDisconnectedEvent(test_address_1, conn_id_1); /* Now inject notification and make sure callback is sent up to Java layer */ EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true)); std::vector value({0x03, 0x00, 0x02}); GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value); } } // namespace } // namespace internal } // namespace vc } // namespace bluetooth