summaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
author Krzysztof Kopyściński <kopyscinski@google.com> 2025-01-10 13:14:32 +0000
committer Krzysztof Kopyściński <kopyscinski@google.com> 2025-03-17 14:02:16 +0000
commit9a65dd1ced9f33ce1f009d303ea595132ac9c55a (patch)
tree89c6c3285746e1269b8cca72e1854749b205b81f /system
parent9001e81c9c3bb1598e46d82c4a41f3bba7c88323 (diff)
ASHA: add basic test framework
Add test framework for ASHA in native. Bug: 388446070 Test: m . Flag: EXEMPT, test only Change-Id: I8a51d50855613c71d5a254d2a83ea2fb7255f919
Diffstat (limited to 'system')
-rw-r--r--system/bta/Android.bp68
-rw-r--r--system/bta/hearing_aid/hearing_aid_test.cc474
-rw-r--r--system/bta/test/common/btif_storage_mock.cc15
-rw-r--r--system/bta/test/common/btif_storage_mock.h12
-rw-r--r--system/test/Android.bp15
-rw-r--r--system/test/mock/mock_bta_hearing_aid_audio_source.cc43
-rw-r--r--system/test/mock/mock_stack_gap_conn_interface.cc (renamed from system/test/mock/mock_stack_gap_conn.cc)53
-rw-r--r--system/test/mock/mock_stack_gap_conn_interface.h63
8 files changed, 725 insertions, 18 deletions
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 5a8f721ebd..22074caf65 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -1347,6 +1347,74 @@ cc_test {
}
cc_test {
+ name: "bluetooth_hearing_aid_test",
+ test_suites: ["general-tests"],
+ defaults: [
+ "fluoride_bta_defaults",
+ "mts_defaults",
+ ],
+ host_supported: true,
+ include_dirs: [
+ "packages/modules/Bluetooth/system",
+ "packages/modules/Bluetooth/system/bta/include",
+ "packages/modules/Bluetooth/system/bta/test/common",
+ "packages/modules/Bluetooth/system/stack/include",
+ ],
+ srcs: [
+ ":TestCommonMockFunctions",
+ ":TestMockBtaHearingAidAudioSource",
+ ":TestMockDevice",
+ ":TestMockMainShim",
+ ":TestMockMainShimEntry",
+ ":TestMockStackBtmInterface",
+ ":TestMockStackBtmIso",
+ ":TestMockStackGapConnInterface",
+ ":TestMockStackL2cap",
+ ":TestStubOsi",
+ "gatt/database.cc",
+ "gatt/database_builder.cc",
+ "hearing_aid/hearing_aid.cc",
+ "hearing_aid/hearing_aid_test.cc",
+ "test/common/bta_gatt_api_mock.cc",
+ "test/common/bta_gatt_queue_mock.cc",
+ "test/common/btif_storage_mock.cc",
+ "test/common/btm_api_mock.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ ],
+ static_libs: [
+ "bluetooth_flags_c_lib_for_test",
+ "libaconfig_storage_read_api_cc",
+ "libbluetooth-types",
+ "libbluetooth_crypto_toolbox",
+ "libbluetooth_gd",
+ "libbluetooth_log",
+ "libbt-audio-asrc",
+ "libbt-btu-main-thread",
+ "libbt-common",
+ "libbt-platform-protos-lite",
+ "libchrome",
+ "libevent",
+ "libg722codec",
+ "libgmock",
+ "server_configurable_flags",
+ ],
+ sanitize: {
+ cfi: true,
+ scs: true,
+ address: true,
+ all_undefined: true,
+ integer_overflow: true,
+ diag: {
+ undefined: true,
+ },
+ },
+}
+
+cc_test {
name: "bluetooth_hh_test",
test_suites: ["general-tests"],
defaults: [
diff --git a/system/bta/hearing_aid/hearing_aid_test.cc b/system/bta/hearing_aid/hearing_aid_test.cc
new file mode 100644
index 0000000000..2090df9d81
--- /dev/null
+++ b/system/bta/hearing_aid/hearing_aid_test.cc
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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_helpers.h>
+#include <base/functional/bind.h>
+#include <base/strings/string_number_conversions.h>
+#include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <osi/include/alarm.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <variant>
+
+#include "bta/le_audio/le_audio_types.h"
+#include "bta_gatt_api_mock.h"
+#include "bta_gatt_queue_mock.h"
+#include "bta_hearing_aid_api.h"
+#include "btif_storage_mock.h"
+#include "btm_api_mock.h"
+#include "gatt/database_builder.h"
+#include "hardware/bt_gatt_types.h"
+#include "hci/controller_interface_mock.h"
+#include "stack/include/bt_uuid16.h"
+#include "stack/include/btm_status.h"
+#include "test/common/mock_functions.h"
+#include "test/mock/mock_main_shim_entry.h"
+#include "test/mock/mock_stack_gap_conn_interface.h"
+#include "test/mock/mock_stack_l2cap_interface.h"
+#include "types/bt_transport.h"
+
+static std::map<const char*, bool> fake_osi_bool_props;
+
+namespace bluetooth {
+namespace hearing_aid {
+namespace internal {
+namespace {
+
+using base::HexEncode;
+
+using namespace bluetooth::hearing_aid;
+
+using ::bluetooth::hearing_aid::ConnectionState;
+using ::bluetooth::hearing_aid::HearingAidCallbacks;
+using ::bluetooth::hearing_aid::HearingAidInterface;
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::Invoke;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::Sequence;
+using ::testing::SetArgPointee;
+using ::testing::WithArg;
+
+RawAddress GetTestAddress(int index) {
+ CHECK_LT(index, UINT8_MAX);
+ RawAddress result = {{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}};
+ return result;
+}
+
+static uint16_t GetTestConnId(const RawAddress& address) {
+ return address.address[RawAddress::kLength - 1];
+}
+
+class MockHearingAidCallbacks : public HearingAidCallbacks {
+public:
+ MockHearingAidCallbacks() = default;
+ MockHearingAidCallbacks(const MockHearingAidCallbacks&) = delete;
+ ~MockHearingAidCallbacks() override = default;
+
+ MOCK_METHOD((void), OnConnectionState, (ConnectionState state, const RawAddress& address),
+ (override));
+ MOCK_METHOD((void), OnDeviceAvailable,
+ (uint8_t capabilities, uint64_t hiSyncId, const RawAddress& address), (override));
+};
+
+class HearingAidTestBase : public ::testing::Test {
+protected:
+ Uuid HEARING_AID_UUID = Uuid::FromString("FDF0");
+ Uuid READ_ONLY_PROPERTIES_UUID = Uuid::FromString("6333651e-c481-4a3e-9169-7c902aad37bb");
+ Uuid AUDIO_CONTROL_POINT_UUID = Uuid::FromString("f0d4de7e-4a88-476c-9d9f-1937b0996cc0");
+ Uuid AUDIO_STATUS_UUID = Uuid::FromString("38663f1a-e711-4cac-b641-326b56404837");
+ Uuid VOLUME_UUID = Uuid::FromString("00e4ca9e-ab14-41e4-8823-f9e70c7e91df");
+ Uuid LE_PSM_UUID = Uuid::FromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
+
+ void set_sample_database(uint16_t conn_id) {
+ static constexpr uint16_t kGapSvcStartHdl = 0x0001;
+ static constexpr uint16_t kGapDeviceNameValHdl = 0x0003;
+ static constexpr uint16_t kGapSvcEndHdl = kGapDeviceNameValHdl;
+
+ static constexpr uint16_t kSvcStartHdl = 0x0010;
+ static constexpr uint16_t kReadOnlyProperties = 0x0012;
+ static constexpr uint16_t kAudioControlPoint = 0x0015;
+ static constexpr uint16_t kAudioStatusPoint = 0x0018;
+ static constexpr uint16_t kVolume = 0x001B;
+ static constexpr uint16_t kLePsm = 0x001E;
+ static constexpr uint16_t kSvcEndHdl = kLePsm;
+
+ gatt::DatabaseBuilder bob;
+
+ /* Generic Access Service */
+ bob.AddService(kGapSvcStartHdl, kGapDeviceNameValHdl, Uuid::From16Bit(0x1800), true);
+ /* Device Name Char. */
+ bob.AddCharacteristic(kGapDeviceNameValHdl - 1, kGapDeviceNameValHdl, Uuid::From16Bit(0x2a00),
+ GATT_CHAR_PROP_BIT_READ);
+
+ /* ASHA Service */
+ bob.AddService(kSvcStartHdl, kSvcEndHdl, HEARING_AID_UUID, true);
+ bob.AddCharacteristic(kReadOnlyProperties - 1, kReadOnlyProperties, READ_ONLY_PROPERTIES_UUID,
+ GATT_CHAR_PROP_BIT_READ);
+ bob.AddCharacteristic(kAudioControlPoint - 1, kAudioControlPoint, AUDIO_CONTROL_POINT_UUID,
+ GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR);
+ bob.AddCharacteristic(kAudioStatusPoint - 1, kAudioStatusPoint, AUDIO_STATUS_UUID,
+ GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
+ bob.AddDescriptor(kAudioStatusPoint + 1, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
+ bob.AddCharacteristic(kVolume - 1, kVolume, VOLUME_UUID, GATT_CHAR_PROP_BIT_WRITE_NR);
+ bob.AddCharacteristic(kLePsm - 1, kLePsm, LE_PSM_UUID, GATT_CHAR_PROP_BIT_READ);
+
+ services_map[conn_id] = bob.Build().Services();
+
+ ON_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _))
+ .WillByDefault(Invoke(
+ [this](uint16_t conn_id, uint16_t handle, GATT_READ_OP_CB cb, void* cb_data) {
+ auto* svc = gatt::FindService(services_map[conn_id], handle);
+ if (svc == nullptr) {
+ return;
+ }
+
+ std::vector<uint8_t> value;
+ tGATT_STATUS status = GATT_SUCCESS;
+
+ switch (handle) {
+ case kReadOnlyProperties:
+ value.resize(17);
+ value.assign(17, 0x01);
+ break;
+ case kAudioStatusPoint:
+ value.resize(1);
+ value.assign(1, 0);
+ break;
+ case kLePsm:
+ value.resize(2);
+ value.assign(2, 0x0080);
+ break;
+ /* passthrough */
+ default:
+ status = GATT_READ_NOT_PERMIT;
+ break;
+ }
+
+ if (cb) {
+ cb(conn_id, status, handle, value.size(), value.data(), cb_data);
+ }
+ }));
+
+ /* 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<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;
+ }));
+
+ ON_CALL(gatt_interface, ServiceSearchRequest(_, _))
+ .WillByDefault(WithArg<0>(
+ Invoke([&](uint16_t conn_id) { InjectSearchCompleteEvent(conn_id); })));
+
+ /* 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 SetUp(void) override {
+ fake_osi_bool_props.clear();
+ bluetooth::manager::SetMockBtmInterface(&btm_interface);
+ bluetooth::storage::SetMockBtifStorageInterface(&btif_storage_interface_);
+ gatt::SetMockBtaGattInterface(&gatt_interface);
+ gatt::SetMockBtaGattQueue(&gatt_queue);
+ callbacks.reset(new MockHearingAidCallbacks());
+ bluetooth::hci::testing::mock_controller_ =
+ std::make_unique<NiceMock<bluetooth::hci::testing::MockControllerInterface>>();
+ bluetooth::testing::stack::l2cap::set_interface(&mock_stack_l2cap_interface_);
+ bluetooth::testing::stack::gap_conn::set_interface(&mock_stack_gap_conn_interface_);
+
+ encryption_result = true;
+
+ ON_CALL(mock_stack_l2cap_interface_, L2CA_UpdateBleConnParams(_, _, _, _, _, _, _))
+ .WillByDefault(Invoke([&](const RawAddress& /*rem_bda*/, uint16_t min_int,
+ uint16_t /*max_int*/, uint16_t latency, uint16_t timeout,
+ uint16_t /*min_ce_len*/, uint16_t /*max_ce_len*/) {
+ req_int = min_int;
+ req_latency = latency;
+ req_timeout = timeout;
+ return true;
+ }));
+
+ ON_CALL(mock_stack_gap_conn_interface_, GAP_ConnOpen(_, _, _, _, _, _, _, _, _, _, _))
+ .WillByDefault(Invoke([&](const char* /* p_serv_name */, uint8_t /*service_id*/,
+ bool /*is_server*/, const RawAddress* p_rem_bda,
+ uint16_t /*psm*/, uint16_t /*le_mps*/,
+ tL2CAP_CFG_INFO* /*p_cfg*/, tL2CAP_ERTM_INFO* /*ertm_info*/,
+ uint16_t /*security*/, tGAP_CONN_CALLBACK* p_cb,
+ tBT_TRANSPORT /*transport*/) {
+ InjectConnUpdateEvent(p_rem_bda->address[5], req_int, req_latency, req_timeout);
+
+ gap_conn_cb = p_cb;
+ if (gap_conn_cb) {
+ gap_conn_cb(0xFFFF, GAP_EVT_CONN_OPENED, nullptr);
+ }
+ return 1;
+ }));
+
+ ON_CALL(mock_stack_gap_conn_interface_, GAP_ConnGetRemoteAddr(_))
+ .WillByDefault(Invoke([&](uint16_t /*gap_handle*/) { return &test_address; }));
+
+ /* by default connect only direct connection requests */
+ ON_CALL(gatt_interface, Open(_, _, _, _))
+ .WillByDefault(Invoke([&](tGATT_IF /*client_if*/, const RawAddress& remote_bda,
+ tBTM_BLE_CONN_TYPE connection_type, bool /*opportunistic*/) {
+ if (connection_type == BTM_BLE_DIRECT_CONNECTION) {
+ InjectConnectedEvent(remote_bda, GetTestConnId(remote_bda));
+ }
+ }));
+
+ ON_CALL(gatt_interface, Close(_)).WillByDefault(Invoke([&](uint16_t conn_id) {
+ /* We arrive here once, when we call Disconnect; and second time, after
+ * we send OnGattDisconnected - but device was already removed */
+ if (connected_devices.count(conn_id) > 0) {
+ InjectDisconnectedEvent(conn_id);
+ }
+ }));
+ }
+
+ void TearDown(void) override {
+ services_map.clear();
+ gatt::SetMockBtaGattQueue(nullptr);
+ gatt::SetMockBtaGattInterface(nullptr);
+ bluetooth::manager::SetMockBtmInterface(nullptr);
+ bluetooth::hci::testing::mock_controller_.reset();
+ bluetooth::testing::stack::l2cap::reset_interface();
+ bluetooth::testing::stack::gap_conn::reset_interface();
+ Mock::VerifyAndClearExpectations(&*callbacks);
+ Mock::VerifyAndClearExpectations(&gatt_queue);
+ Mock::VerifyAndClearExpectations(&gatt_interface);
+ Mock::VerifyAndClearExpectations(&btm_interface);
+ callbacks.reset();
+ }
+
+ 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,
+ };
+ connected_devices[conn_id] = address;
+ gatt_callback(BTA_GATTC_OPEN_EVT, (tBTA_GATTC*)&event_data);
+ }
+
+ void InjectConnUpdateEvent(uint16_t conn_id, uint16_t interval, uint16_t latency,
+ uint16_t timeout, tGATT_STATUS status = GATT_SUCCESS) {
+ tBTA_GATTC_CONN_UPDATE event_data = {
+ .conn_id = conn_id,
+ .interval = interval,
+ .latency = latency,
+ .timeout = timeout,
+ .status = status,
+ };
+
+ gatt_callback(BTA_GATTC_CONN_UPDATE_EVT, (tBTA_GATTC*)&event_data);
+ }
+
+ void InjectDisconnectedEvent(uint16_t conn_id,
+ tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST,
+ bool allow_fake_conn = false) {
+ if (!allow_fake_conn) {
+ ASSERT_NE(connected_devices.count(conn_id), 0u);
+ }
+
+ tBTA_GATTC_CLOSE event_data = {
+ .conn_id = conn_id,
+ .status = GATT_SUCCESS,
+ .client_if = gatt_if,
+ .remote_bda = connected_devices[conn_id],
+ .reason = reason,
+ };
+
+ connected_devices.erase(conn_id);
+ gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data);
+ }
+
+ void InjectSearchCompleteEvent(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 InjectNotificationEvent(const RawAddress& test_address, uint16_t conn_id, uint16_t handle,
+ std::vector<uint8_t> value, bool indicate = false) {
+ tBTA_GATTC_NOTIFY event_data = {
+ .conn_id = conn_id,
+ .bda = test_address,
+ .handle = handle,
+ .len = (uint8_t)value.size(),
+ .is_notify = !indicate,
+ };
+
+ ASSERT_TRUE(value.size() < GATT_MAX_ATTR_LEN);
+ std::copy(value.begin(), value.end(), event_data.value);
+ gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data);
+ }
+
+ void SetEncryptionResult(const RawAddress& address, bool success) {
+ encryption_result = success;
+
+ ON_CALL(btm_interface, BTM_IsEncrypted(address, _)).WillByDefault(Return(encryption_result));
+
+ ON_CALL(btm_interface, IsDeviceBonded(address, _)).WillByDefault(Return(true));
+ }
+
+ std::unique_ptr<MockHearingAidCallbacks> callbacks;
+ bluetooth::manager::MockBtmInterface btm_interface;
+ bluetooth::storage::MockBtifStorageInterface btif_storage_interface_;
+ gatt::MockBtaGattInterface gatt_interface;
+ gatt::MockBtaGattQueue gatt_queue;
+ tBTA_GATTC_CBACK* gatt_callback;
+ const uint8_t gatt_if = 0xfe;
+ std::map<uint8_t, RawAddress> connected_devices;
+ std::map<uint16_t, std::list<gatt::Service>> services_map;
+ bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_;
+ bluetooth::testing::stack::gap_conn::Mock mock_stack_gap_conn_interface_;
+ tGAP_CONN_CALLBACK* gap_conn_cb;
+ uint16_t req_int;
+ uint16_t req_latency;
+ uint16_t req_timeout;
+ bool encryption_result;
+ const RawAddress test_address = GetTestAddress(1);
+};
+
+class HearingAidTest : public HearingAidTestBase {
+ void SetUp(void) override {
+ HearingAidTestBase::SetUp();
+ BtaAppRegisterCallback app_register_callback;
+ EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback)));
+ HearingAid::Initialize(callbacks.get(), base::DoNothing());
+ ASSERT_TRUE(gatt_callback);
+ ASSERT_TRUE(app_register_callback);
+ app_register_callback.Run(gatt_if, GATT_SUCCESS);
+ ASSERT_TRUE(HearingAid::IsHearingAidRunning());
+ Mock::VerifyAndClearExpectations(&gatt_interface);
+ }
+ void TearDown(void) override {
+ EXPECT_CALL(gatt_interface, AppDeregister(gatt_if));
+ if (HearingAid::IsHearingAidRunning()) {
+ HearingAid::CleanUp();
+ }
+ ASSERT_FALSE(HearingAid::IsHearingAidRunning());
+ gatt_callback = nullptr;
+ HearingAidTestBase::TearDown();
+ }
+};
+
+/* Test that hearing aid is initialized and cleaned up */
+TEST_F(HearingAidTestBase, initialize) {
+ ASSERT_FALSE(HearingAid::IsHearingAidRunning());
+ HearingAid::Initialize(callbacks.get(), base::DoNothing());
+ ASSERT_TRUE(HearingAid::IsHearingAidRunning());
+ HearingAid::CleanUp();
+ ASSERT_FALSE(HearingAid::IsHearingAidRunning());
+}
+
+/* Test that connect cancellation works */
+TEST_F(HearingAidTest, disconnect_when_connecting) {
+ /* Override the default action to prevent us sending the connected event */
+ EXPECT_CALL(gatt_interface, Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, _))
+ .WillOnce(Return());
+ EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address)).Times(0);
+ HearingAid::Connect(test_address);
+
+ /* Single call from HearingAid:Disconnect*/
+ EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
+ EXPECT_CALL(gatt_interface, CancelOpen(_, test_address, _)).Times(AnyNumber());
+ EXPECT_CALL(gatt_interface, Close(_)).Times(0);
+ HearingAid::Disconnect(test_address);
+}
+
+/* Test that connect works and Connected state gets reported */
+TEST_F(HearingAidTest, connect) {
+ set_sample_database(1);
+
+ EXPECT_CALL(gatt_interface, Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, _));
+ EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+ EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address));
+ ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(Return(true));
+
+ HearingAid::Connect(test_address);
+}
+
+/* Test that connected device can be disconnected */
+TEST_F(HearingAidTest, disconnect_when_connected) {
+ set_sample_database(1);
+
+ ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(Return(true));
+ EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(1);
+ EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address));
+ HearingAid::Connect(test_address);
+
+ /* First call from HearingAid:Disconnect. Second call from OnGattDisconnected*/
+ EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(2);
+ EXPECT_CALL(gatt_interface, Close(_)).Times(2);
+ HearingAid::Disconnect(test_address);
+}
+
+} // namespace
+} // namespace internal
+} // namespace hearing_aid
+} // namespace bluetooth
diff --git a/system/bta/test/common/btif_storage_mock.cc b/system/bta/test/common/btif_storage_mock.cc
index 7e4a4e5118..f5eb38939e 100644
--- a/system/bta/test/common/btif_storage_mock.cc
+++ b/system/bta/test/common/btif_storage_mock.cc
@@ -19,6 +19,7 @@
#include <bluetooth/log.h>
+#include "bta_hearing_aid_api.h"
#include "btif/include/btif_profile_storage.h"
#include "btif/include/btif_storage.h"
@@ -134,3 +135,17 @@ bt_status_t btif_storage_get_remote_device_property(const RawAddress* address,
log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!");
return btif_storage_interface->GetRemoteDeviceProperty(address, property);
}
+
+/** Get the hearing aid device properties. */
+bool btif_storage_get_hearing_aid_prop(const RawAddress& address, uint8_t* capabilities,
+ uint64_t* hi_sync_id, uint16_t* render_delay,
+ uint16_t* preparation_delay, uint16_t* codecs) {
+ log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!");
+ return btif_storage_interface->GetHearingAidProp(address, capabilities, hi_sync_id, render_delay,
+ preparation_delay, codecs);
+}
+
+void btif_storage_add_hearing_aid(const HearingDevice& dev_info) {
+ log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!");
+ return btif_storage_interface->AddHearingAid(&dev_info);
+}
diff --git a/system/bta/test/common/btif_storage_mock.h b/system/bta/test/common/btif_storage_mock.h
index 8809643f70..25f9bbdde0 100644
--- a/system/bta/test/common/btif_storage_mock.h
+++ b/system/bta/test/common/btif_storage_mock.h
@@ -21,6 +21,8 @@
#include "include/hardware/bluetooth.h"
#include "types/raw_address.h"
+struct HearingDevice;
+
namespace bluetooth {
namespace storage {
@@ -50,6 +52,11 @@ public:
virtual bt_status_t GetRemoteDeviceProperty(const RawAddress* address,
bt_property_t* property) = 0;
+ virtual bool GetHearingAidProp(const RawAddress& address, uint8_t* capabilities,
+ uint64_t* hi_sync_id, uint16_t* render_delay,
+ uint16_t* preparation_delay, uint16_t* codecs) = 0;
+ virtual void AddHearingAid(const HearingDevice* dev_info) = 0;
+
virtual ~BtifStorageInterface() = default;
};
@@ -88,6 +95,11 @@ public:
MOCK_METHOD((void), RemoveLeaudioHas, (const RawAddress& address), (override));
MOCK_METHOD((bt_status_t), GetRemoteDeviceProperty,
(const RawAddress* address, bt_property_t* property), (override));
+ MOCK_METHOD((bool), GetHearingAidProp,
+ (const RawAddress& address, uint8_t* capabilities, uint64_t* hi_sync_id,
+ uint16_t* render_delay, uint16_t* preparation_delay, uint16_t* codecs),
+ (override));
+ MOCK_METHOD((void), AddHearingAid, (const HearingDevice* dev_info), (override));
};
/**
diff --git a/system/test/Android.bp b/system/test/Android.bp
index 106dedb7bd..9dab01352f 100644
--- a/system/test/Android.bp
+++ b/system/test/Android.bp
@@ -88,6 +88,13 @@ filegroup {
}
filegroup {
+ name: "TestMockBtaHearingAidAudioSource",
+ srcs: [
+ "mock/mock_bta_hearing_aid_audio_source.cc",
+ ],
+}
+
+filegroup {
name: "TestMockBtaHf",
srcs: [
"mock/mock_bta_hf*.cc",
@@ -184,6 +191,7 @@ filegroup {
":TestMockBtaHas",
":TestMockBtaHd",
":TestMockBtaHearingAid",
+ ":TestMockBtaHearingAidAudioSource",
":TestMockBtaHf",
":TestMockBtaHh",
":TestMockBtaJv",
@@ -457,6 +465,13 @@ filegroup {
}
filegroup {
+ name: "TestMockStackGapConnInterface",
+ srcs: [
+ "mock/mock_stack_gap_conn_interface.cc",
+ ],
+}
+
+filegroup {
name: "TestMockStackConnMgr",
srcs: [
"mock/mock_stack_connection_manager.cc",
diff --git a/system/test/mock/mock_bta_hearing_aid_audio_source.cc b/system/test/mock/mock_bta_hearing_aid_audio_source.cc
new file mode 100644
index 0000000000..c68fad3b8d
--- /dev/null
+++ b/system/test/mock/mock_bta_hearing_aid_audio_source.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * 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.
+ */
+
+/*
+ * Generated mock file from original source file
+ * Functions generated:8
+ */
+
+#include <base/functional/callback.h>
+
+#include <cstdint>
+
+#include "bta/include/bta_gatt_queue.h"
+#include "bta/include/bta_hearing_aid_api.h"
+#include "test/common/mock_functions.h"
+#include "types/raw_address.h"
+
+void HearingAidAudioSource::Start(const CodecConfiguration& /*codecConfiguration*/,
+ HearingAidAudioReceiver* /*audioReceiver*/,
+ uint16_t /*remote_delay_ms*/) {
+ inc_func_call_count(__func__);
+}
+
+void HearingAidAudioSource::Stop() { inc_func_call_count(__func__); }
+
+void HearingAidAudioSource::Initialize() { inc_func_call_count(__func__); }
+
+void HearingAidAudioSource::CleanUp() { inc_func_call_count(__func__); }
+
+void HearingAidAudioSource::DebugDump(int /*fd*/) { inc_func_call_count(__func__); }
diff --git a/system/test/mock/mock_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn_interface.cc
index 07193c9130..2ef1a3ef8f 100644
--- a/system/test/mock/mock_stack_gap_conn.cc
+++ b/system/test/mock/mock_stack_gap_conn_interface.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,39 @@
* limitations under the License.
*/
-/*
- * Generated mock file from original source file
- * Functions generated:13
- */
+#include "test/mock/mock_stack_gap_conn_interface.h"
-#include "gap_api.h"
-#include "stack/include/bt_hdr.h"
+#include "stack/include/gap_api.h"
#include "test/common/mock_functions.h"
-#include "types/raw_address.h"
-const RawAddress* GAP_ConnGetRemoteAddr(uint16_t /* gap_handle */) {
+namespace {
+bluetooth::testing::stack::gap_conn::Mock mock_gap_conn_interface;
+bluetooth::testing::stack::gap_conn::Interface* interface_ = &mock_gap_conn_interface;
+} // namespace
+
+void bluetooth::testing::stack::gap_conn::reset_interface() {
+ interface_ = &mock_gap_conn_interface;
+}
+
+void bluetooth::testing::stack::gap_conn::set_interface(
+ bluetooth::testing::stack::gap_conn::Interface* interface) {
+ interface_ = interface;
+}
+
+uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, bool is_server,
+ const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps,
+ tL2CAP_CFG_INFO* p_cfg, tL2CAP_ERTM_INFO* ertm_info, uint16_t security,
+ tGAP_CONN_CALLBACK* p_cb, tBT_TRANSPORT transport) {
inc_func_call_count(__func__);
- return nullptr;
+
+ return interface_->GAP_ConnOpen(p_serv_name, service_id, is_server, p_rem_bda, psm, le_mps, p_cfg,
+ ertm_info, security, p_cb, transport);
+}
+
+const RawAddress* GAP_ConnGetRemoteAddr(uint16_t gap_handle) {
+ inc_func_call_count(__func__);
+
+ return interface_->GAP_ConnGetRemoteAddr(gap_handle);
}
int GAP_GetRxQueueCnt(uint16_t /* handle */, uint32_t* /* p_rx_queue_count */) {
inc_func_call_count(__func__);
@@ -44,14 +64,6 @@ uint16_t GAP_ConnGetRemMtuSize(uint16_t /* gap_handle */) {
inc_func_call_count(__func__);
return 0;
}
-uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t /* service_id */, bool /* is_server */,
- const RawAddress* /* p_rem_bda */, uint16_t /* psm */, uint16_t /* le_mps */,
- tL2CAP_CFG_INFO* /* p_cfg */, tL2CAP_ERTM_INFO* /* ertm_info */,
- uint16_t /* security */, tGAP_CONN_CALLBACK* /* p_cb */,
- tBT_TRANSPORT /* transport */) {
- inc_func_call_count(__func__);
- return 0;
-}
uint16_t GAP_ConnReadData(uint16_t /* gap_handle */, uint8_t* /* p_data */, uint16_t /* max_len */,
uint16_t* /* p_len */) {
inc_func_call_count(__func__);
@@ -74,3 +86,8 @@ bool GAP_IsTransportLe(uint16_t /* gap_handle */) {
return false;
}
void GAP_Init(void) { inc_func_call_count(__func__); }
+
+bluetooth::testing::stack::gap_conn::Interface&
+bluetooth::testing::stack::gap_conn::get_interface() {
+ return *interface_;
+}
diff --git a/system/test/mock/mock_stack_gap_conn_interface.h b/system/test/mock/mock_stack_gap_conn_interface.h
new file mode 100644
index 0000000000..c1ed7caea3
--- /dev/null
+++ b/system/test/mock/mock_stack_gap_conn_interface.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <vector>
+
+#include "stack/include/gap_api.h"
+
+namespace bluetooth {
+namespace testing {
+namespace stack {
+namespace gap_conn {
+
+class Interface {
+public:
+ virtual ~Interface() = default;
+
+ virtual uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, bool is_server,
+ const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps,
+ tL2CAP_CFG_INFO* p_cfg, tL2CAP_ERTM_INFO* ertm_info,
+ uint16_t security, tGAP_CONN_CALLBACK* p_cb,
+ tBT_TRANSPORT transport) = 0;
+
+ virtual const RawAddress* GAP_ConnGetRemoteAddr(uint16_t gap_handle) = 0;
+};
+
+class Mock : public Interface {
+public:
+ ~Mock() = default;
+
+ MOCK_METHOD(uint16_t, GAP_ConnOpen,
+ (const char* p_serv_name, uint8_t service_id, bool is_server,
+ const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps, tL2CAP_CFG_INFO* p_cfg,
+ tL2CAP_ERTM_INFO* ertm_info, uint16_t security, tGAP_CONN_CALLBACK* p_cb,
+ tBT_TRANSPORT transport));
+
+ MOCK_METHOD(const RawAddress*, GAP_ConnGetRemoteAddr, (uint16_t gap_handle));
+};
+
+void reset_interface();
+void set_interface(Interface* interface_);
+Interface& get_interface();
+
+} // namespace gap_conn
+} // namespace stack
+} // namespace testing
+} // namespace bluetooth