/* * 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 "bind_helpers.h" #include "bta_csis_api.h" #include "bta_dm_api_mock.h" #include "bta_gatt_api_mock.h" #include "bta_gatt_queue_mock.h" #include "bta_le_audio_uuids.h" #include "btif/include/btif_profile_storage.h" #include "btm_api_mock.h" #include "csis_types.h" #include "gatt/database_builder.h" #include "hardware/bt_gatt_types.h" #include "stack/include/bt_uuid16.h" #include "test/common/mock_functions.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" bool gatt_cl_read_sirk_req(const RawAddress& /*peer_bda*/, base::OnceCallback /*cb*/) { return true; } namespace bluetooth { namespace csis { namespace internal { namespace { using base::Bind; using base::Closure; using base::Unretained; using bluetooth::csis::ConnectionState; using bluetooth::csis::CsisClient; using bluetooth::csis::CsisClientCallbacks; using bluetooth::csis::CsisClientInterface; using bluetooth::csis::CsisGroupLockStatus; using bluetooth::groups::DeviceGroups; using testing::_; using testing::AtLeast; 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; // Disables most likely false-positives from base::SplitString() extern "C" const char* __asan_default_options() { return "detect_container_overflow=0"; } RawAddress GetTestAddress(int index) { EXPECT_LT(index, UINT8_MAX); RawAddress result = {{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast(index)}}; return result; } /* Csis lock callback */ class MockCsisLockCallback { public: MockCsisLockCallback() = default; MockCsisLockCallback(const MockCsisLockCallback&) = delete; MockCsisLockCallback& operator=(const MockCsisLockCallback&) = delete; ~MockCsisLockCallback() = default; MOCK_METHOD((void), CsisGroupLockCb, (int group_id, bool locked, CsisGroupLockStatus status)); }; static MockCsisLockCallback* csis_lock_callback_mock; void SetMockCsisLockCallback(MockCsisLockCallback* mock) { csis_lock_callback_mock = mock; } /* Csis callbacks to JNI */ class MockCsisCallbacks : public CsisClientCallbacks { public: MockCsisCallbacks() = default; MockCsisCallbacks(const MockCsisCallbacks&) = delete; MockCsisCallbacks& operator=(const MockCsisCallbacks&) = delete; ~MockCsisCallbacks() override = default; MOCK_METHOD((void), OnConnectionState, (const RawAddress& address, ConnectionState state), (override)); MOCK_METHOD((void), OnDeviceAvailable, (const RawAddress& address, int group_id, int group_size, int rank, const bluetooth::Uuid& uuid), (override)); MOCK_METHOD((void), OnSetMemberAvailable, (const RawAddress& address, int group_id), (override)); MOCK_METHOD((void), OnGroupLockChanged, (int group_id, bool locked, bluetooth::csis::CsisGroupLockStatus status), (override)); MOCK_METHOD((void), OnGattCsisWriteLockRsp, (uint16_t conn_id, tGATT_STATUS status, uint16_t handle, void* data)); }; class CsisClientTest : public ::testing::Test { private: void set_sample_cap_included_database(uint16_t conn_id, bool csis, bool csis_broken, uint8_t rank, uint8_t sirk_msb = 1) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true); builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00), GATT_CHAR_PROP_BIT_READ); if (csis) { builder.AddService(0x0005, 0x0009, bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE), true); builder.AddIncludedService(0x0006, kCsisServiceUuid, 0x0010, 0x0030); builder.AddService(0x0010, 0x0030, kCsisServiceUuid, true); builder.AddCharacteristic(0x0020, 0x0021, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0023, 0x0024, kCsisSizeUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0025, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x0026, 0x0027, kCsisLockUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY | GATT_CHAR_PROP_BIT_WRITE); builder.AddDescriptor(0x0028, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0029, 0x0030, kCsisRankUuid, GATT_CHAR_PROP_BIT_READ); } if (csis_broken) { builder.AddCharacteristic(0x0020, 0x0021, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); } 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([rank, sirk_msb](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: value.assign(17, 1); value[16] = sirk_msb; break; case 0x0024: value.resize(1); break; case 0x0027: value.resize(1); break; case 0x0030: value.resize(1); value.assign(1, rank); break; default: FAIL(); return; } cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); })); } void set_sample_database(uint16_t conn_id, bool csis, bool csis_broken, uint8_t rank, uint8_t sirk_msb = 1) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true); builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00), GATT_CHAR_PROP_BIT_READ); if (csis) { builder.AddService(0x0010, 0x0030, kCsisServiceUuid, true); builder.AddCharacteristic(0x0020, 0x0021, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0023, 0x0024, kCsisSizeUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0025, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x0026, 0x0027, kCsisLockUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY | GATT_CHAR_PROP_BIT_WRITE); builder.AddDescriptor(0x0028, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0029, 0x0030, kCsisRankUuid, GATT_CHAR_PROP_BIT_READ); } if (csis_broken) { builder.AddCharacteristic(0x0020, 0x0021, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); } 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([rank, sirk_msb](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: value.assign(17, 1); value[16] = sirk_msb; break; case 0x0024: value.resize(1); break; case 0x0027: value.resize(1); break; case 0x0030: value.resize(1); value.assign(1, rank); break; default: FAIL(); return; } cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); })); } void set_sample_database_double_csis(uint16_t conn_id, uint8_t rank_1, uint8_t rank_2, bool broken, uint8_t sirk1_infill = 1, uint8_t sirk2_infill = 2) { gatt::DatabaseBuilder builder; builder.AddService(0x0001, 0x0003, Uuid::From16Bit(0x1800), true); builder.AddCharacteristic(0x0002, 0x0003, Uuid::From16Bit(0x2a00), GATT_CHAR_PROP_BIT_READ); builder.AddService(0x0010, 0x0026, bluetooth::Uuid::From16Bit(0x1850), true); builder.AddIncludedService(0x0011, kCsisServiceUuid, 0x0031, 0x0041); builder.AddCharacteristic(0x0031, 0x0032, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0033, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0034, 0x0035, kCsisSizeUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0036, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x0037, 0x0038, kCsisLockUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY | GATT_CHAR_PROP_BIT_WRITE); builder.AddDescriptor(0x0039, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0040, 0x0041, kCsisRankUuid, GATT_CHAR_PROP_BIT_READ); if (broken) { builder.AddCharacteristic(0x0020, 0x0021, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0022, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); } builder.AddService(0x0042, 0x0044, bluetooth::Uuid::From16Bit(0x1860), true); builder.AddIncludedService(0x0043, kCsisServiceUuid, 0x0045, 0x0055); builder.AddCharacteristic(0x0045, 0x0046, kCsisSirkUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0047, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0048, 0x0049, kCsisSizeUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); builder.AddDescriptor(0x0050, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic( 0x0051, 0x0052, kCsisLockUuid, GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY | GATT_CHAR_PROP_BIT_WRITE); builder.AddDescriptor(0x0053, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); builder.AddCharacteristic(0x0054, 0x0055, kCsisRankUuid, GATT_CHAR_PROP_BIT_READ); 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([sirk1_infill, sirk2_infill, rank_1, rank_2]( 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 0x0032: value.resize(17); value.assign(17, sirk1_infill); value[0] = 1; // Plain text SIRK break; case 0x0035: value.resize(1); value.assign(1, 2); break; case 0x0038: value.resize(1); break; case 0x0041: value.resize(1); value.assign(1, rank_1); break; case 0x0046: value.resize(17); value.assign(17, sirk2_infill); value[0] = 1; // Plain text SIRK break; case 0x0049: value.resize(1); value.assign(1, 2); break; case 0x0052: value.resize(1); break; case 0x0055: value.resize(1); value.assign(1, rank_2); break; default: log::error("Unknown handle? {}", handle); FAIL(); return; } cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); })); } protected: void SetUp(void) override { reset_mock_function_count_map(); bluetooth::manager::SetMockBtmInterface(&btm_interface); dm::SetMockBtaDmInterface(&dm_interface); gatt::SetMockBtaGattInterface(&gatt_interface); gatt::SetMockBtaGattQueue(&gatt_queue); SetMockCsisLockCallback(&csis_lock_cb); callbacks.reset(new MockCsisCallbacks()); ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(DoAll(Return(true))); ON_CALL(btm_interface, BTM_IsEncrypted(_, _)).WillByDefault(DoAll(Return(true))); 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); } })); } void TearDown(void) override { services_map.clear(); callbacks.reset(); CsisClient::CleanUp(); gatt::SetMockBtaGattInterface(nullptr); bluetooth::manager::SetMockBtmInterface(nullptr); } void TestAppRegister(void) { BtaAppRegisterCallback app_register_callback; EXPECT_CALL(gatt_interface, AppRegister(_, _, _)) .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback))); CsisClient::Initialize(callbacks.get(), Bind(&btif_storage_load_bonded_csis_devices)); ASSERT_TRUE(gatt_callback); ASSERT_TRUE(app_register_callback); app_register_callback.Run(gatt_if, GATT_SUCCESS); ASSERT_TRUE(CsisClient::IsCsisClientRunning()); } void TestAppUnregister(void) { EXPECT_CALL(gatt_interface, AppDeregister(gatt_if)); CsisClient::CleanUp(); ASSERT_FALSE(CsisClient::IsCsisClientRunning()); gatt_callback = nullptr; } void TestConnect(const RawAddress& address, bool encrypted = true) { // by default indicate link as encrypted ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _)) .WillByDefault(DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(encrypted))); EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, _)); CsisClient::Get()->Connect(address); Mock::VerifyAndClearExpectations(&gatt_interface); Mock::VerifyAndClearExpectations(&btm_interface); } void TestDisconnect(const RawAddress& address, uint16_t conn_id) { if (conn_id != GATT_INVALID_CONN_ID) { EXPECT_CALL(gatt_interface, Close(conn_id)); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::DISCONNECTED)); } else { EXPECT_CALL(gatt_interface, CancelOpen(_, address, _)); } CsisClient::Get()->Disconnect(address); } void TestAddFromStorage(const RawAddress& address, uint16_t conn_id, std::vector& storage_group_buf, std::vector& storage_buf) { EXPECT_CALL(*callbacks, OnConnectionState(address, ConnectionState::CONNECTED)).Times(1); EXPECT_CALL(*callbacks, OnDeviceAvailable(address, _, _, _, _)).Times(AtLeast(1)); EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, true)) .WillOnce(Invoke([this, conn_id](tGATT_IF /*client_if*/, const RawAddress& remote_bda, bool /*is_direct*/, bool /*opportunistic*/) { InjectConnectedEvent(remote_bda, conn_id); GetSearchCompleteEvent(conn_id); })); DeviceGroups::AddFromStorage(address, storage_group_buf); CsisClient::AddFromStorage(address, storage_buf); } void InjectEncryptionEvent(const RawAddress& test_address, uint16_t conn_id) { tBTA_GATTC_ENC_CMPL_CB event_data = { .client_if = static_cast(conn_id), .remote_bda = test_address, }; gatt_callback(BTA_GATTC_ENC_CMPL_CB_EVT, (tBTA_GATTC*)&event_data); } void InjectConnectedEvent(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, (tBTA_GATTC*)&event_data); } void InjectDisconnectedEvent(const RawAddress& address, uint16_t conn_id, tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_PEER_USER) { tBTA_GATTC_CLOSE event_data = { .conn_id = conn_id, .status = GATT_SUCCESS, .client_if = gatt_if, .remote_bda = address, .reason = reason, }; gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&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, (tBTA_GATTC*)&event_data); } void TestReadCharacteristic(const RawAddress& address, uint16_t conn_id, std::vector handles) { SetSampleDatabaseCsis(conn_id, 1); TestAppRegister(); TestConnect(address); InjectConnectedEvent(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); TestAppUnregister(); } void TestGattWriteCCC(uint16_t ccc_handle, GattStatus status, int deregister_times) { SetSampleDatabaseCsis(1, 1); TestAppRegister(); TestConnect(test_address); InjectConnectedEvent(test_address, 1); auto WriteDescriptorCbGenerator = [](tGATT_STATUS status, uint16_t ccc_handle) { return [status, ccc_handle](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) { if (ccc_handle) { handle = ccc_handle; } cb(conn_id, status, handle, value.size(), value.data(), cb_data); } }; }; // sirk, size, lock EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _)) .Times(3) .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0))) .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0))) .WillOnce(Invoke(WriteDescriptorCbGenerator(status, ccc_handle))); EXPECT_CALL(gatt_interface, DeregisterForNotifications(_, _, _)).Times(deregister_times); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&gatt_interface); } 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, (tBTA_GATTC*)&event_data); } void SetSampleCapIncludedDatabaseCsis(uint16_t conn_id, uint8_t rank, uint8_t sirk_msb = 1) { set_sample_cap_included_database(conn_id, true, false, rank, sirk_msb); } void SetSampleDatabaseCsis(uint16_t conn_id, uint8_t rank, uint8_t sirk_msb = 1) { set_sample_database(conn_id, true, false, rank, sirk_msb); } void SetSampleDatabaseNoCsis(uint16_t conn_id, uint8_t rank) { set_sample_database(conn_id, false, false, rank); } void SetSampleDatabaseCsisBroken(uint16_t conn_id, uint rank) { set_sample_database(conn_id, false, true, rank); } void SetSampleDatabaseDoubleCsis(uint16_t conn_id, uint8_t rank_1, uint8_t rank_2) { set_sample_database_double_csis(conn_id, rank_1, rank_2, false); } void SetSampleDatabaseDoubleCsisBroken(uint16_t conn_id, uint8_t rank_1, uint8_t rank_2) { set_sample_database_double_csis(conn_id, rank_1, rank_2, true); } std::unique_ptr callbacks; std::unique_ptr lock_callback; bluetooth::manager::MockBtmInterface btm_interface; dm::MockBtaDmInterface dm_interface; gatt::MockBtaGattInterface gatt_interface; gatt::MockBtaGattQueue gatt_queue; MockCsisLockCallback csis_lock_cb; tBTA_GATTC_CBACK* gatt_callback; const uint8_t gatt_if = 0xff; std::map> services_map; const RawAddress test_address = GetTestAddress(0); const RawAddress test_address2 = GetTestAddress(1); }; TEST_F(CsisClientTest, test_get_uninitialized) { ASSERT_EQ(CsisClient::Get(), nullptr); } TEST_F(CsisClientTest, test_initialize) { CsisClient::Initialize(callbacks.get(), base::DoNothing()); ASSERT_TRUE(CsisClient::IsCsisClientRunning()); CsisClient::CleanUp(); } TEST_F(CsisClientTest, test_initialize_twice) { CsisClient::Initialize(callbacks.get(), base::DoNothing()); CsisClient* csis_p = CsisClient::Get(); CsisClient::Initialize(callbacks.get(), base::DoNothing()); ASSERT_EQ(csis_p, CsisClient::Get()); CsisClient::CleanUp(); } TEST_F(CsisClientTest, test_cleanup_initialized) { CsisClient::Initialize(callbacks.get(), base::DoNothing()); CsisClient::CleanUp(); ASSERT_FALSE(CsisClient::IsCsisClientRunning()); } TEST_F(CsisClientTest, test_cleanup_uninitialized) { CsisClient::CleanUp(); ASSERT_FALSE(CsisClient::IsCsisClientRunning()); } TEST_F(CsisClientTest, test_app_registration) { TestAppRegister(); TestAppUnregister(); } TEST_F(CsisClientTest, test_connect) { TestAppRegister(); TestConnect(GetTestAddress(0)); TestAppUnregister(); } TEST_F(CsisClientTest, test_verify_opportunistic_connect_active_after_connect_timeout) { TestAppRegister(); std::vector no_set_info; DeviceGroups::AddFromStorage(test_address, no_set_info); CsisClient::AddFromStorage(test_address, no_set_info); Mock::VerifyAndClearExpectations(&gatt_interface); Mock::VerifyAndClearExpectations(callbacks.get()); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::DISCONNECTED)).Times(1); TestConnect(test_address); EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, test_address, _)).Times(0); EXPECT_CALL(gatt_interface, Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, true)) .Times(1); InjectConnectedEvent(test_address, 0, GATT_ERROR); Mock::VerifyAndClearExpectations(&gatt_interface); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_disconnect_non_connected) { TestAppRegister(); TestConnect(test_address); TestDisconnect(test_address, GATT_INVALID_CONN_ID); TestAppUnregister(); } TEST_F(CsisClientTest, test_disconnect_connected) { TestAppRegister(); TestConnect(test_address); InjectConnectedEvent(test_address, 1); TestDisconnect(test_address, 1); InjectDisconnectedEvent(test_address, 1); TestAppUnregister(); } TEST_F(CsisClientTest, test_disconnected) { TestAppRegister(); TestConnect(test_address); InjectConnectedEvent(test_address, 1); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::DISCONNECTED)); InjectDisconnectedEvent(test_address, 1); TestAppUnregister(); } TEST_F(CsisClientTest, test_connect_after_remove) { TestAppRegister(); TestConnect(test_address); InjectConnectedEvent(test_address, 1); CsisClient::Get()->RemoveDevice(test_address); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::DISCONNECTED)); ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(Return(false)); CsisClient::Get()->Connect(test_address); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_discovery_csis_found) { SetSampleDatabaseCsis(1, 1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)); EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_discovery_csis_not_found) { SetSampleDatabaseNoCsis(1, 1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(gatt_interface, Close(1)); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_discovery_csis_broken) { SetSampleDatabaseCsisBroken(1, 1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(gatt_interface, Close(1)); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_ccc_reg_fail_handle_not_found) { // service handle range: 0x0001 ~ 0x0030 uint16_t not_existed_ccc_handle = 0x0031; TestGattWriteCCC(not_existed_ccc_handle, GATT_INVALID_HANDLE, 0); } TEST_F(CsisClientTest, test_ccc_reg_fail_handle_found) { // kCsisLockUuid ccc handle uint16_t existed_ccc_hande = 0x0028; TestGattWriteCCC(existed_ccc_hande, GATT_INVALID_HANDLE, 1); } TEST_F(CsisClientTest, test_ccc_reg_fail_out_of_sync) { // kCsisLockUuid ccc handle uint16_t ccc_handle = 0x0028; TestGattWriteCCC(ccc_handle, GATT_DATABASE_OUT_OF_SYNC, 0); } class CsisClientCallbackTest : public CsisClientTest { protected: const RawAddress test_address = GetTestAddress(0); uint16_t conn_id = 22; void SetUp(void) override { CsisClientTest::SetUp(); SetSampleDatabaseCsis(conn_id, 1); TestAppRegister(); TestConnect(test_address); InjectConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); } void TearDown(void) override { TestAppUnregister(); CsisClientTest::TearDown(); } void GetNotificationEvent(uint16_t handle, 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, (tBTA_GATTC*)&event_data); } }; TEST_F(CsisClientCallbackTest, test_on_group_lock_changed_group_not_found) { bool callback_called = false; EXPECT_CALL(*callbacks, OnGroupLockChanged(2, false, CsisGroupLockStatus::FAILED_INVALID_GROUP)); CsisClient::Get()->LockGroup( 2, true, base::BindOnce( [](bool* callback_called, int group_id, bool /*locked*/, CsisGroupLockStatus status) { if ((group_id == 2) && (status == CsisGroupLockStatus::FAILED_INVALID_GROUP)) { *callback_called = true; } }, &callback_called)); ASSERT_TRUE(callback_called); } TEST_F(CsisClientTest, test_get_group_id) { SetSampleDatabaseCsis(1, 1); TestAppRegister(); TestConnect(test_address); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)); EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); ASSERT_EQ(1, CsisClient::Get()->GetGroupId(test_address)); TestAppUnregister(); } TEST_F(CsisClientTest, test_search_complete_before_encryption) { SetSampleDatabaseCsis(1, 1); TestAppRegister(); TestConnect(test_address, false); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)).Times(0); EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)).Times(0); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(false))); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(callbacks.get()); /* Incject encryption and expect device connection */ EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)).Times(1); EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)).Times(1); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(true))); EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)).Times(1); InjectEncryptionEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&gatt_interface); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_disconnect_when_link_key_is_gone) { SetSampleDatabaseCsis(1, 1); TestAppRegister(); TestConnect(test_address, false); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)).Times(0); ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(DoAll(Return(false))); ON_CALL(btm_interface, SetEncryption(test_address, _, _, _, _)) .WillByDefault(Return(tBTM_STATUS::BTM_ERR_KEY_MISSING)); EXPECT_CALL(gatt_interface, Close(1)); InjectConnectedEvent(test_address, 1); Mock::VerifyAndClearExpectations(&gatt_interface); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_is_group_empty) { std::list> csis_groups_; auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); csis_groups_.push_back(g_1); ASSERT_TRUE(g_1->IsEmpty()); } TEST_F(CsisClientTest, test_add_device_to_group) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); auto d_1 = std::make_shared(); ASSERT_TRUE(g_1->IsEmpty()); g_1->AddDevice(d_1); ASSERT_FALSE(g_1->IsEmpty()); } TEST_F(CsisClientTest, test_get_set_desired_size) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetDesiredSize(10); ASSERT_EQ(g_1->GetDesiredSize(), 10); } TEST_F(CsisClientTest, test_is_device_in_the_group) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); auto d_1 = std::make_shared(); g_1->AddDevice(d_1); g_1->IsDeviceInTheGroup(d_1); } TEST_F(CsisClientTest, test_get_current_size) { const RawAddress test_address_1 = GetTestAddress(0); const RawAddress test_address_2 = GetTestAddress(1); const RawAddress test_address_3 = GetTestAddress(2); auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); auto d_1 = std::make_shared(test_address_1, true); auto d_2 = std::make_shared(test_address_2, true); auto d_3 = std::make_shared(test_address_3, true); g_1->AddDevice(d_1); g_1->AddDevice(d_2); g_1->AddDevice(d_3); ASSERT_EQ(3, g_1->GetCurrentSize()); } TEST_F(CsisClientTest, test_set_current_lock_state_unset) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_UNSET); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_UNSET); } TEST_F(CsisClientTest, test_set_current_lock_state_locked) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_LOCKED); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_LOCKED); } TEST_F(CsisClientTest, test_set_current_lock_state_unlocked) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_UNLOCKED); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_UNLOCKED); } TEST_F(CsisClientTest, test_set_various_lock_states) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_UNLOCKED); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_UNLOCKED); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_LOCKED); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_LOCKED); g_1->SetCurrentLockState(CsisLockState::CSIS_STATE_UNSET); ASSERT_EQ(g_1->GetCurrentLockState(), CsisLockState::CSIS_STATE_UNSET); } TEST_F(CsisClientTest, test_set_discovery_state_completed) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_COMPLETED); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_COMPLETED); } TEST_F(CsisClientTest, test_set_discovery_state_idle) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_IDLE); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_IDLE); } TEST_F(CsisClientTest, test_set_discovery_state_ongoing) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_ONGOING); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_ONGOING); } TEST_F(CsisClientTest, test_set_various_discovery_states) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_COMPLETED); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_COMPLETED); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_IDLE); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_IDLE); g_1->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_ONGOING); ASSERT_EQ(g_1->GetDiscoveryState(), CsisDiscoveryState::CSIS_DISCOVERY_ONGOING); } TEST_F(CsisClientTest, test_get_first_last_device) { const RawAddress test_address_3 = GetTestAddress(3); const RawAddress test_address_4 = GetTestAddress(4); const RawAddress test_address_5 = GetTestAddress(5); auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); auto d_1 = std::make_shared(test_address_3, true); auto d_2 = std::make_shared(test_address_4, true); auto d_3 = std::make_shared(test_address_5, true); g_1->AddDevice(d_1); g_1->AddDevice(d_2); g_1->AddDevice(d_3); ASSERT_EQ(g_1->GetLastDevice(), d_3); ASSERT_EQ(g_1->GetFirstDevice(), d_1); } TEST_F(CsisClientTest, test_get_set_sirk) { auto g_1 = std::make_shared(666, bluetooth::Uuid::kEmpty); Octet16 sirk = {1}; g_1->SetSirk(sirk); ASSERT_EQ(g_1->GetSirk(), sirk); } TEST_F(CsisClientTest, test_not_open_duplicate_active_scan_while_bonding_set_member) { uint16_t conn_id = 0x0001; EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)).Times(1); SetSampleDatabaseCsis(conn_id, 1, 1); TestAppRegister(); // Here we handle background scan request Mock::VerifyAndClearExpectations(&dm_interface); TestConnect(test_address); InjectConnectedEvent(test_address, 1); auto ReadCharacteristicCbGenerator = []() { return [](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: // plain sirk value.resize(17); value.assign(17, 1); break; case 0x0024: // size value.resize(1); value.assign(1, 2); break; case 0x0027: // lock value.resize(2); break; case 0x0030: // rank value.resize(1); value.assign(1, 1); break; default: FAIL(); return; } if (cb) { cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); } }; }; // We should read 4 times for sirk, rank, size, and lock characteristics EXPECT_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)) .Times(4) .WillOnce(Invoke(ReadCharacteristicCbGenerator())) .WillOnce(Invoke(ReadCharacteristicCbGenerator())) .WillOnce(Invoke(ReadCharacteristicCbGenerator())) .WillOnce(Invoke(ReadCharacteristicCbGenerator())); // Here is actual active scan request for the first device tBTA_DM_SEARCH_CBACK* p_results_cb = nullptr; EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)) .Times(1) .WillOnce(DoAll(SaveArg<1>(&p_results_cb))); GetSearchCompleteEvent(conn_id); Mock::VerifyAndClearExpectations(&dm_interface); // Simulate we find the set member tBTA_DM_SEARCH result; result.inq_res.include_rsi = true; std::vector rsi = {0x07, 0x2e, 0x00, 0xed, 0x1a, 0x00, 0x00, 0x00}; result.inq_res.p_eir = rsi.data(); result.inq_res.eir_len = 8; result.inq_res.bd_addr = test_address2; // CSIS client should process set member event to JNI EXPECT_CALL(*callbacks, OnSetMemberAvailable(test_address2, 1)); p_results_cb(BTA_DM_INQ_RES_EVT, &result); Mock::VerifyAndClearExpectations(&dm_interface); EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)).Times(0); // Simulate getting duplicate response from remote for the first device // At this momoment we should not open second active scan because the set // member is already cached and waiting for bonding GetSearchCompleteEvent(conn_id); Mock::VerifyAndClearExpectations(&dm_interface); } TEST_F(CsisClientTest, test_csis_member_not_found) { EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)).Times(1); SetSampleDatabaseDoubleCsis(0x001, 1, 2); TestAppRegister(); /* Here we handle Background Scan request */ Mock::VerifyAndClearExpectations(&dm_interface); tBTA_DM_SEARCH_CBACK* p_results_cb = nullptr; /* Here is actual Active Scan request */ EXPECT_CALL(dm_interface, BTA_DmBleCsisObserve(true, _)) .WillOnce(DoAll(SaveArg<1>(&p_results_cb))); TestConnect(test_address); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(&dm_interface); /* Verify that scanner has been called to start filtering */ ASSERT_EQ(1, get_func_call_count("set_empty_filter")); /* Check callback is not null and simulate no member found and scan * completed*/ ASSERT_NE(p_results_cb, nullptr); tBTA_DM_SEARCH result; result.observe_cmpl.num_resps = 80; p_results_cb(BTA_DM_OBSERVE_CMPL_EVT, &result); /* Verify that scanner has been called to stop filtering */ ASSERT_EQ(2, get_func_call_count("set_empty_filter")); } class CsisMultiClientTest : public CsisClientTest { protected: const RawAddress test_address_1 = GetTestAddress(1); const RawAddress test_address_2 = GetTestAddress(2); void SetUp(void) override { CsisClientTest::SetUp(); TestAppRegister(); SetSampleDatabaseDoubleCsis(0x001, 1, 2); } }; class CsisMultiClientTestBroken : public CsisClientTest { protected: const RawAddress test_address_1 = GetTestAddress(1); const RawAddress test_address_2 = GetTestAddress(2); void SetUp(void) override { CsisClientTest::SetUp(); TestAppRegister(); SetSampleDatabaseDoubleCsisBroken(0x001, 1, 2); } }; TEST_F(CsisMultiClientTest, test_add_multiple_instances) { TestAppUnregister(); CsisClientTest::TearDown(); } TEST_F(CsisMultiClientTest, test_cleanup_multiple_instances) { CsisClient::CleanUp(); CsisClient::IsCsisClientRunning(); } TEST_F(CsisMultiClientTest, test_connect_multiple_instances) { TestConnect(GetTestAddress(0)); TestAppUnregister(); } TEST_F(CsisMultiClientTest, test_disconnect_multiple_instances) { TestConnect(test_address); InjectConnectedEvent(test_address, 1); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::DISCONNECTED)); InjectDisconnectedEvent(test_address, 1); TestAppUnregister(); CsisClientTest::TearDown(); } TEST_F(CsisMultiClientTest, test_lock_multiple_instances) { TestConnect(test_address); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); EXPECT_CALL(*callbacks, OnGroupLockChanged(1, true, CsisGroupLockStatus::SUCCESS)); EXPECT_CALL(*csis_lock_callback_mock, CsisGroupLockCb(1, true, CsisGroupLockStatus::SUCCESS)); 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) -> void { if (cb) { cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); } })); CsisClient::Get()->LockGroup( 1, true, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); EXPECT_CALL(*callbacks, OnGroupLockChanged(2, true, CsisGroupLockStatus::SUCCESS)); EXPECT_CALL(*csis_lock_callback_mock, CsisGroupLockCb(2, true, CsisGroupLockStatus::SUCCESS)); CsisClient::Get()->LockGroup( 2, true, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); } TEST_F(CsisMultiClientTest, test_unlock_multiple_instances) { TestConnect(test_address); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(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) -> void { if (cb) { cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); } })); CsisClient::Get()->LockGroup( 1, true, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); EXPECT_CALL(*callbacks, OnGroupLockChanged(1, false, CsisGroupLockStatus::SUCCESS)); EXPECT_CALL(*csis_lock_callback_mock, CsisGroupLockCb(1, false, CsisGroupLockStatus::SUCCESS)); CsisClient::Get()->LockGroup( 1, false, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); } TEST_F(CsisMultiClientTest, test_disconnect_locked_multiple_instances) { TestConnect(test_address); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); TestConnect(test_address2); InjectConnectedEvent(test_address2, 2); GetSearchCompleteEvent(2); EXPECT_CALL(*callbacks, OnGroupLockChanged(1, true, CsisGroupLockStatus::SUCCESS)); EXPECT_CALL(*csis_lock_callback_mock, CsisGroupLockCb(1, true, CsisGroupLockStatus::SUCCESS)); 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) -> void { if (cb) { cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); } })); CsisClient::Get()->LockGroup( 1, true, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); EXPECT_CALL(*callbacks, OnGroupLockChanged(1, false, CsisGroupLockStatus::LOCKED_GROUP_MEMBER_LOST)); InjectDisconnectedEvent(test_address, 2, GATT_CONN_TIMEOUT); } TEST_F(CsisMultiClientTest, test_discover_multiple_instances) { TestConnect(test_address); EXPECT_CALL(*callbacks, OnConnectionState(test_address, ConnectionState::CONNECTED)).Times(1); EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)).Times(2); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); Mock::VerifyAndClearExpectations(callbacks.get()); TestAppUnregister(); } TEST_F(CsisClientTest, test_storage_calls) { SetSampleDatabaseCsis(1, 1); ASSERT_EQ(0, get_func_call_count("btif_storage_load_bonded_csis_devices")); TestAppRegister(); ASSERT_EQ(1, get_func_call_count("btif_storage_load_bonded_csis_devices")); ASSERT_EQ(0, get_func_call_count("btif_storage_update_csis_info")); TestConnect(test_address); InjectConnectedEvent(test_address, 1); GetSearchCompleteEvent(1); ASSERT_EQ(1, get_func_call_count("btif_storage_update_csis_info")); ASSERT_EQ(0, get_func_call_count("btif_storage_remove_csis_device")); CsisClient::Get()->RemoveDevice(test_address); /* It is 0 because btif_csis_client.cc calls that */ ASSERT_EQ(0, get_func_call_count("btif_storage_remove_csis_device")); TestAppUnregister(); } TEST_F(CsisClientTest, test_storage_content) { // Two devices in one set SetSampleCapIncludedDatabaseCsis(1, 1); SetSampleCapIncludedDatabaseCsis(2, 2); // Devices in the other set SetSampleCapIncludedDatabaseCsis(3, 1, 2); SetSampleCapIncludedDatabaseCsis(4, 1, 3); TestAppRegister(); TestConnect(GetTestAddress(1)); InjectConnectedEvent(GetTestAddress(1), 1); GetSearchCompleteEvent(1); ASSERT_EQ(1, CsisClient::Get()->GetGroupId( GetTestAddress(1), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); TestConnect(GetTestAddress(2)); InjectConnectedEvent(GetTestAddress(2), 2); GetSearchCompleteEvent(2); ASSERT_EQ(1, CsisClient::Get()->GetGroupId( GetTestAddress(2), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); TestConnect(GetTestAddress(3)); InjectConnectedEvent(GetTestAddress(3), 3); GetSearchCompleteEvent(3); ASSERT_EQ(2, CsisClient::Get()->GetGroupId( GetTestAddress(3), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); std::vector dev1_storage; std::vector dev2_storage; std::vector dev3_storage; // Store to byte buffer CsisClient::GetForStorage(GetTestAddress(1), dev1_storage); CsisClient::GetForStorage(GetTestAddress(2), dev2_storage); CsisClient::GetForStorage(GetTestAddress(3), dev3_storage); ASSERT_NE(0u, dev1_storage.size()); ASSERT_NE(0u, dev2_storage.size()); ASSERT_NE(0u, dev3_storage.size()); std::vector dev1_group_storage; std::vector dev2_group_storage; std::vector dev3_group_storage; DeviceGroups::GetForStorage(GetTestAddress(1), dev1_group_storage); DeviceGroups::GetForStorage(GetTestAddress(2), dev2_group_storage); DeviceGroups::GetForStorage(GetTestAddress(3), dev3_group_storage); ASSERT_NE(0u, dev1_group_storage.size()); ASSERT_NE(0u, dev2_group_storage.size()); ASSERT_NE(0u, dev3_group_storage.size()); // Clean it up TestAppUnregister(); // Reinitialize service TestAppRegister(); // Restore dev1 from the byte buffer TestAddFromStorage(GetTestAddress(1), 1, dev1_group_storage, dev1_storage); ASSERT_EQ(1, CsisClient::Get()->GetGroupId( GetTestAddress(1), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); // Restore dev2 from the byte buffer TestAddFromStorage(GetTestAddress(2), 2, dev2_group_storage, dev2_storage); ASSERT_EQ(1, CsisClient::Get()->GetGroupId( GetTestAddress(2), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); // Restore dev3 from the byte buffer TestAddFromStorage(GetTestAddress(3), 3, dev3_group_storage, dev3_storage); ASSERT_EQ(2, CsisClient::Get()->GetGroupId( GetTestAddress(3), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); // Restore not inerrogated dev4 - empty buffer but valid sirk for group 2 std::vector no_set_info; TestAddFromStorage(GetTestAddress(4), 4, no_set_info, no_set_info); ASSERT_EQ(3, CsisClient::Get()->GetGroupId( GetTestAddress(4), bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE))); TestAppUnregister(); } TEST_F(CsisClientTest, test_database_out_of_sync) { auto test_address = GetTestAddress(0); auto conn_id = 1; TestAppRegister(); SetSampleDatabaseCsis(conn_id, 1); TestConnect(test_address); InjectConnectedEvent(test_address, conn_id); GetSearchCompleteEvent(conn_id); ASSERT_EQ(1, CsisClient::Get()->GetGroupId(test_address, bluetooth::Uuid::From16Bit(0x0000))); // Simulated database changed 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(_, _)); CsisClient::Get()->LockGroup( 1, true, base::BindOnce([](int group_id, bool locked, CsisGroupLockStatus status) { csis_lock_callback_mock->CsisGroupLockCb(group_id, locked, status); })); TestAppUnregister(); } } // namespace } // namespace internal } // namespace csis } // namespace bluetooth