blob: f3aaefe0988f7689a78b18b6d760be9d484ce968 [file] [log] [blame]
/*
* Copyright 2021 HIMSA II K/S - www.himsa.com.
* Represented by EHIMA - www.ehima.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <base/bind.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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 "btif_storage.h"
#include "btm_api_mock.h"
#include "csis_types.h"
#include "gatt/database_builder.h"
#include "hardware/bt_gatt_types.h"
std::map<std::string, int> mock_function_count_map;
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::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) {
CHECK_LT(index, UINT8_MAX);
RawAddress result = {
{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
return result;
}
/* Csis lock callback */
class MockCsisLockCallback {
public:
MockCsisLockCallback() = default;
~MockCsisLockCallback() = default;
MOCK_METHOD((void), CsisGroupLockCb,
(int group_id, bool locked, CsisGroupLockStatus status));
private:
DISALLOW_COPY_AND_ASSIGN(MockCsisLockCallback);
};
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() 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,
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));
private:
DISALLOW_COPY_AND_ASSIGN(MockCsisCallbacks);
};
class CsisClientTest : public ::testing::Test {
private:
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<uint8_t> 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:
ASSERT_TRUE(false);
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<uint8_t> 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;
ASSERT_TRUE(false);
return;
}
cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(),
cb_data);
}));
}
protected:
void SetUp(void) override {
mock_function_count_map.clear();
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(gatt_interface, GetCharacteristic(_, _))
.WillByDefault(
Invoke([&](uint16_t conn_id,
uint16_t handle) -> const gatt::Characteristic* {
std::list<gatt::Service>& 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<gatt::Service>& 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<gatt::Service>* {
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<uint8_t> 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) {
// by default indicate link as encrypted
ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
.WillByDefault(
DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(true)));
EXPECT_CALL(gatt_interface, Open(gatt_if, address, true, _));
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<uint8_t>& storage_buf) {
EXPECT_CALL(*callbacks,
OnConnectionState(address, ConnectionState::CONNECTED))
.Times(1);
EXPECT_CALL(*callbacks, OnDeviceAvailable(address, _, _, _)).Times(1);
EXPECT_CALL(gatt_interface, Open(gatt_if, address, false, _))
.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);
}));
CsisClient::AddFromStorage(address, storage_buf, true);
}
void InjectConnectedEvent(const RawAddress& address, uint16_t conn_id) {
tBTA_GATTC_OPEN event_data = {
.status = GATT_SUCCESS,
.conn_id = conn_id,
.client_if = gatt_if,
.remote_bda = address,
.transport = GATT_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 = {
.status = GATT_SUCCESS,
.conn_id = conn_id,
.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 = {
.status = GATT_SUCCESS,
.conn_id = conn_id,
};
gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
}
void TestReadCharacteristic(const RawAddress& address, uint16_t conn_id,
std::vector<uint16_t> 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 GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) {
tBTA_GATTC_CLOSE event_data = {
.status = GATT_SUCCESS,
.conn_id = conn_id,
.client_if = gatt_if,
.remote_bda = address,
.reason = GATT_CONN_TERMINATE_PEER_USER,
};
gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data);
}
void SetEncryptionResult(const RawAddress& address, bool success) {
ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
.WillByDefault(DoAll(SetArgPointee<1>(0), Return(true)));
EXPECT_CALL(btm_interface,
SetEncryption(address, _, NotNull(), _, BTM_BLE_SEC_ENCRYPT))
.WillOnce(Invoke(
[&success](const RawAddress& bd_addr, tBT_TRANSPORT transport,
tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS {
p_callback(&bd_addr, transport, p_ref_data,
success ? BTM_SUCCESS : BTM_FAILED_ON_SECURITY);
return BTM_SUCCESS;
}));
}
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<MockCsisCallbacks> callbacks;
std::unique_ptr<MockCsisCallbacks> 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<uint16_t, std::list<gatt::Service>> services_map;
const RawAddress test_address = GetTestAddress(0);
const RawAddress test_address2 = GetTestAddress(1);
};
TEST_F(CsisClientTest, test_get_uninitialized) {
ASSERT_DEATH(CsisClient::Get(), "");
}
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_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_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();
}
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<uint8_t>& 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);
int group_id = CsisClient::Get()->GetGroupId(test_address);
ASSERT_TRUE(group_id == 1);
TestAppUnregister();
}
TEST_F(CsisClientTest, test_is_group_empty) {
std::list<std::shared_ptr<CsisGroup>> csis_groups_;
auto g_1 = std::make_shared<CsisGroup>(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<CsisGroup>(666, bluetooth::Uuid::kEmpty);
auto d_1 = std::make_shared<CsisDevice>();
ASSERT_TRUE(g_1->IsEmpty());
g_1->AddDevice(d_1);
ASSERT_FALSE(g_1->IsEmpty());
}
TEST_F(CsisClientTest, test_set_desired_size) {
auto g_1 = std::make_shared<CsisGroup>(666, bluetooth::Uuid::kEmpty);
g_1->SetDesiredSize(10);
ASSERT_EQ((int)sizeof(g_1), 16);
}
TEST_F(CsisClientTest, test_get_desired_size) {
auto g_1 = std::make_shared<CsisGroup>(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<CsisGroup>(666, bluetooth::Uuid::kEmpty);
auto d_1 = std::make_shared<CsisDevice>();
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<CsisGroup>(666, bluetooth::Uuid::kEmpty);
auto d_1 = std::make_shared<CsisDevice>(test_address_1, true);
auto d_2 = std::make_shared<CsisDevice>(test_address_2, true);
auto d_3 = std::make_shared<CsisDevice>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(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<CsisGroup>(666, bluetooth::Uuid::kEmpty);
auto d_1 = std::make_shared<CsisDevice>(test_address_3, true);
auto d_2 = std::make_shared<CsisDevice>(test_address_4, true);
auto d_3 = std::make_shared<CsisDevice>(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<CsisGroup>(666, bluetooth::Uuid::kEmpty);
Octet16 sirk = {1};
g_1->SetSirk(sirk);
ASSERT_EQ(g_1->GetSirk(), sirk);
}
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<uint8_t> 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<uint8_t> 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<uint8_t> 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,
mock_function_count_map["btif_storage_load_bonded_csis_devices"]);
TestAppRegister();
ASSERT_EQ(1,
mock_function_count_map["btif_storage_load_bonded_csis_devices"]);
ASSERT_EQ(0, mock_function_count_map["btif_storage_update_csis_info"]);
ASSERT_EQ(0, mock_function_count_map["btif_storage_set_csis_autoconnect"]);
TestConnect(test_address);
InjectConnectedEvent(test_address, 1);
GetSearchCompleteEvent(1);
ASSERT_EQ(1, mock_function_count_map["btif_storage_set_csis_autoconnect"]);
ASSERT_EQ(1, mock_function_count_map["btif_storage_update_csis_info"]);
ASSERT_EQ(0, mock_function_count_map["btif_storage_remove_csis_device"]);
CsisClient::Get()->RemoveDevice(test_address);
ASSERT_EQ(1, mock_function_count_map["btif_storage_remove_csis_device"]);
TestAppUnregister();
}
TEST_F(CsisClientTest, test_storage_content) {
// Two devices in one set
SetSampleDatabaseCsis(1, 1);
SetSampleDatabaseCsis(2, 2);
// Devices in the other set
SetSampleDatabaseCsis(3, 1, 2);
SetSampleDatabaseCsis(4, 1, 2);
TestAppRegister();
TestConnect(GetTestAddress(1));
InjectConnectedEvent(GetTestAddress(1), 1);
GetSearchCompleteEvent(1);
ASSERT_EQ(1, CsisClient::Get()->GetGroupId(
GetTestAddress(1), bluetooth::Uuid::From16Bit(0x0000)));
TestConnect(GetTestAddress(2));
InjectConnectedEvent(GetTestAddress(2), 2);
GetSearchCompleteEvent(2);
ASSERT_EQ(1, CsisClient::Get()->GetGroupId(
GetTestAddress(2), bluetooth::Uuid::From16Bit(0x0000)));
TestConnect(GetTestAddress(3));
InjectConnectedEvent(GetTestAddress(3), 3);
GetSearchCompleteEvent(3);
ASSERT_EQ(2, CsisClient::Get()->GetGroupId(
GetTestAddress(3), bluetooth::Uuid::From16Bit(0x0000)));
std::vector<uint8_t> dev1_storage;
std::vector<uint8_t> dev2_storage;
std::vector<uint8_t> 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());
// Clean it up
TestAppUnregister();
// Reinitialize service
TestAppRegister();
// Restore dev1 from the byte buffer
TestAddFromStorage(GetTestAddress(1), 1, dev1_storage);
ASSERT_EQ(1, CsisClient::Get()->GetGroupId(
GetTestAddress(1), bluetooth::Uuid::From16Bit(0x0000)));
// Restore dev2 from the byte buffer
TestAddFromStorage(GetTestAddress(2), 2, dev2_storage);
ASSERT_EQ(1, CsisClient::Get()->GetGroupId(
GetTestAddress(2), bluetooth::Uuid::From16Bit(0x0000)));
// Restore dev3 from the byte buffer
TestAddFromStorage(GetTestAddress(3), 3, dev3_storage);
ASSERT_EQ(2, CsisClient::Get()->GetGroupId(
GetTestAddress(3), bluetooth::Uuid::From16Bit(0x0000)));
// Restore not inerrogated dev4 - empty buffer but valid sirk for group 2
std::vector<uint8_t> no_set_info;
TestAddFromStorage(GetTestAddress(4), 4, no_set_info);
ASSERT_EQ(2, CsisClient::Get()->GetGroupId(
GetTestAddress(4), bluetooth::Uuid::From16Bit(0x0000)));
TestAppUnregister();
}
} // namespace
} // namespace internal
} // namespace csis
} // namespace bluetooth