You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
28 KiB
730 lines
28 KiB
// |
|
// Copyright (C) 2015 Google, Inc. |
|
// |
|
// 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 <gmock/gmock.h> |
|
#include <gtest/gtest.h> |
|
|
|
#include "service/gatt_server.h" |
|
#include "service/hal/fake_bluetooth_gatt_interface.h" |
|
|
|
using ::testing::_; |
|
using ::testing::Return; |
|
|
|
namespace bluetooth { |
|
namespace { |
|
|
|
class MockGattHandler |
|
: public hal::FakeBluetoothGattInterface::TestServerHandler { |
|
public: |
|
MockGattHandler() = default; |
|
~MockGattHandler() override = default; |
|
|
|
MOCK_METHOD1(RegisterServer, bt_status_t(const bt_uuid_t&)); |
|
MOCK_METHOD1(UnregisterServer, bt_status_t(int)); |
|
MOCK_METHOD2(AddService, bt_status_t(int, std::vector<btgatt_db_element_t>)); |
|
MOCK_METHOD5(AddCharacteristic, bt_status_t(int, int, bt_uuid_t*, int, int)); |
|
MOCK_METHOD4(AddDescriptor, bt_status_t(int, int, bt_uuid_t*, int)); |
|
MOCK_METHOD3(StartService, bt_status_t(int, int, int)); |
|
MOCK_METHOD2(DeleteService, bt_status_t(int, int)); |
|
MOCK_METHOD5(SendIndication, |
|
bt_status_t(int, int, int, int, std::vector<uint8_t>)); |
|
MOCK_METHOD4(SendResponse, |
|
bt_status_t(int, int, int, const btgatt_response_t&)); |
|
|
|
private: |
|
DISALLOW_COPY_AND_ASSIGN(MockGattHandler); |
|
}; |
|
|
|
class TestDelegate : public GattServer::Delegate { |
|
public: |
|
TestDelegate() = default; |
|
~TestDelegate() override = default; |
|
|
|
struct RequestData { |
|
RequestData() |
|
: id(-1), |
|
offset(-1), |
|
is_long(false), |
|
is_prep(false), |
|
need_rsp(false), |
|
is_exec(false), |
|
count(0), |
|
connected(false) {} |
|
~RequestData() = default; |
|
|
|
std::string device_address; |
|
int id; |
|
int offset; |
|
bool is_long; |
|
bool is_prep; |
|
bool need_rsp; |
|
bool is_exec; |
|
uint16_t handle; |
|
int count; |
|
std::vector<uint8_t> write_value; |
|
bool connected; |
|
}; |
|
|
|
void OnCharacteristicReadRequest(GattServer* gatt_server, |
|
const std::string& device_address, |
|
int request_id, int offset, bool is_long, |
|
uint16_t handle) override { |
|
ASSERT_TRUE(gatt_server); |
|
char_read_req_.device_address = device_address; |
|
char_read_req_.id = request_id; |
|
char_read_req_.offset = offset; |
|
char_read_req_.is_long = is_long; |
|
char_read_req_.handle = handle; |
|
char_read_req_.count++; |
|
} |
|
|
|
void OnDescriptorReadRequest(GattServer* gatt_server, |
|
const std::string& device_address, |
|
int request_id, int offset, bool is_long, |
|
uint16_t handle) override { |
|
ASSERT_TRUE(gatt_server); |
|
desc_read_req_.device_address = device_address; |
|
desc_read_req_.id = request_id; |
|
desc_read_req_.offset = offset; |
|
desc_read_req_.is_long = is_long; |
|
desc_read_req_.handle = handle; |
|
desc_read_req_.count++; |
|
} |
|
|
|
void OnCharacteristicWriteRequest(GattServer* gatt_server, |
|
const std::string& device_address, |
|
int request_id, int offset, |
|
bool is_prepare_write, bool need_response, |
|
const std::vector<uint8_t>& value, |
|
uint16_t handle) override { |
|
ASSERT_TRUE(gatt_server); |
|
char_write_req_.device_address = device_address; |
|
char_write_req_.id = request_id; |
|
char_write_req_.offset = offset; |
|
char_write_req_.is_prep = is_prepare_write; |
|
char_write_req_.need_rsp = need_response; |
|
char_write_req_.handle = handle; |
|
char_write_req_.count++; |
|
char_write_req_.write_value = value; |
|
} |
|
|
|
void OnDescriptorWriteRequest(GattServer* gatt_server, |
|
const std::string& device_address, |
|
int request_id, int offset, |
|
bool is_prepare_write, bool need_response, |
|
const std::vector<uint8_t>& value, |
|
uint16_t handle) override { |
|
ASSERT_TRUE(gatt_server); |
|
desc_write_req_.device_address = device_address; |
|
desc_write_req_.id = request_id; |
|
desc_write_req_.offset = offset; |
|
desc_write_req_.is_prep = is_prepare_write; |
|
desc_write_req_.need_rsp = need_response; |
|
desc_write_req_.handle = handle; |
|
desc_write_req_.count++; |
|
desc_write_req_.write_value = value; |
|
} |
|
|
|
void OnExecuteWriteRequest(GattServer* gatt_server, |
|
const std::string& device_address, int request_id, |
|
bool is_execute) override { |
|
ASSERT_TRUE(gatt_server); |
|
exec_req_.device_address = device_address; |
|
exec_req_.id = request_id; |
|
exec_req_.is_exec = is_execute; |
|
exec_req_.count++; |
|
} |
|
|
|
void OnConnectionStateChanged(GattServer* gatt_server, |
|
const std::string& device_address, |
|
bool connected) override { |
|
ASSERT_TRUE(gatt_server); |
|
conn_state_changed_.device_address = device_address; |
|
conn_state_changed_.connected = connected; |
|
conn_state_changed_.count++; |
|
} |
|
|
|
const RequestData& char_read_req() const { return char_read_req_; } |
|
const RequestData& desc_read_req() const { return desc_read_req_; } |
|
const RequestData& char_write_req() const { return char_write_req_; } |
|
const RequestData& desc_write_req() const { return desc_write_req_; } |
|
const RequestData& conn_state_changed() const { return conn_state_changed_; } |
|
|
|
private: |
|
RequestData char_read_req_; |
|
RequestData desc_read_req_; |
|
RequestData char_write_req_; |
|
RequestData desc_write_req_; |
|
RequestData exec_req_; |
|
RequestData conn_state_changed_; |
|
}; |
|
|
|
class GattServerTest : public ::testing::Test { |
|
public: |
|
GattServerTest() = default; |
|
~GattServerTest() override = default; |
|
|
|
void SetUp() override { |
|
mock_handler_.reset(new MockGattHandler()); |
|
fake_hal_gatt_iface_ = new hal::FakeBluetoothGattInterface( |
|
nullptr, nullptr, nullptr, |
|
std::static_pointer_cast< |
|
hal::FakeBluetoothGattInterface::TestServerHandler>(mock_handler_)); |
|
|
|
hal::BluetoothGattInterface::InitializeForTesting(fake_hal_gatt_iface_); |
|
factory_.reset(new GattServerFactory()); |
|
} |
|
|
|
void TearDown() override { |
|
factory_.reset(); |
|
hal::BluetoothGattInterface::CleanUp(); |
|
} |
|
|
|
protected: |
|
hal::FakeBluetoothGattInterface* fake_hal_gatt_iface_; |
|
std::shared_ptr<MockGattHandler> mock_handler_; |
|
std::unique_ptr<GattServerFactory> factory_; |
|
|
|
private: |
|
DISALLOW_COPY_AND_ASSIGN(GattServerTest); |
|
}; |
|
|
|
const int kDefaultServerId = 4; |
|
|
|
class GattServerPostRegisterTest : public GattServerTest { |
|
public: |
|
GattServerPostRegisterTest() = default; |
|
~GattServerPostRegisterTest() override = default; |
|
|
|
void SetUp() override { |
|
GattServerTest::SetUp(); |
|
UUID uuid = UUID::GetRandom(); |
|
auto callback = [&](BLEStatus status, const UUID& in_uuid, |
|
std::unique_ptr<BluetoothInstance> in_client) { |
|
CHECK(in_uuid == uuid); |
|
CHECK(in_client.get()); |
|
CHECK(status == BLE_STATUS_SUCCESS); |
|
|
|
gatt_server_ = std::unique_ptr<GattServer>( |
|
static_cast<GattServer*>(in_client.release())); |
|
}; |
|
|
|
EXPECT_CALL(*mock_handler_, RegisterServer(_)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
factory_->RegisterInstance(uuid, callback); |
|
|
|
bt_uuid_t hal_uuid = uuid.GetBlueDroid(); |
|
fake_hal_gatt_iface_->NotifyRegisterServerCallback( |
|
BT_STATUS_SUCCESS, kDefaultServerId, hal_uuid); |
|
} |
|
|
|
void TearDown() override { |
|
EXPECT_CALL(*mock_handler_, UnregisterServer(_)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
gatt_server_ = nullptr; |
|
GattServerTest::TearDown(); |
|
} |
|
|
|
void SetUpTestService() { |
|
EXPECT_CALL(*mock_handler_, AddService(_, _)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
UUID uuid0 = UUID::GetRandom(); |
|
UUID uuid1 = UUID::GetRandom(); |
|
UUID uuid2 = UUID::GetRandom(); |
|
|
|
bool register_success = false; |
|
|
|
Service service(0, true, uuid0, {}, {}); |
|
|
|
ASSERT_TRUE(gatt_server_->AddService( |
|
service, [&](BLEStatus status, const Service& added_service) { |
|
ASSERT_EQ(BLE_STATUS_SUCCESS, status); |
|
ASSERT_TRUE(UUID(added_service.uuid()) == UUID(service.uuid())); |
|
ASSERT_TRUE(added_service.handle() == 0x0001); |
|
register_success = true; |
|
})); |
|
|
|
srvc_handle_ = 0x0001; |
|
char_handle_ = 0x0002; |
|
desc_handle_ = 0x0004; |
|
|
|
std::vector<btgatt_db_element_t> service_with_handles = { |
|
{.type = BTGATT_DB_PRIMARY_SERVICE, |
|
.uuid = uuid0.GetBlueDroid(), |
|
.attribute_handle = srvc_handle_}, |
|
{.type = BTGATT_DB_CHARACTERISTIC, |
|
.uuid = uuid1.GetBlueDroid(), |
|
.attribute_handle = char_handle_}, |
|
{.type = BTGATT_DB_DESCRIPTOR, |
|
.uuid = uuid2.GetBlueDroid(), |
|
.attribute_handle = desc_handle_}, |
|
}; |
|
|
|
fake_hal_gatt_iface_->NotifyServiceAddedCallback( |
|
BT_STATUS_SUCCESS, kDefaultServerId, service_with_handles); |
|
|
|
testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
|
|
|
ASSERT_TRUE(register_success); |
|
} |
|
|
|
protected: |
|
std::unique_ptr<GattServer> gatt_server_; |
|
|
|
uint16_t srvc_handle_; |
|
uint16_t char_handle_; |
|
uint16_t desc_handle_; |
|
|
|
private: |
|
DISALLOW_COPY_AND_ASSIGN(GattServerPostRegisterTest); |
|
}; |
|
|
|
TEST_F(GattServerTest, RegisterServer) { |
|
EXPECT_CALL(*mock_handler_, RegisterServer(_)) |
|
.Times(2) |
|
.WillOnce(Return(BT_STATUS_FAIL)) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
// These will be asynchronously populate with a result when the callback |
|
// executes. |
|
BLEStatus status = BLE_STATUS_SUCCESS; |
|
UUID cb_uuid; |
|
std::unique_ptr<GattServer> server; |
|
int callback_count = 0; |
|
|
|
auto callback = [&](BLEStatus in_status, const UUID& uuid, |
|
std::unique_ptr<BluetoothInstance> in_server) { |
|
status = in_status; |
|
cb_uuid = uuid; |
|
server = std::unique_ptr<GattServer>( |
|
static_cast<GattServer*>(in_server.release())); |
|
callback_count++; |
|
}; |
|
|
|
UUID uuid0 = UUID::GetRandom(); |
|
|
|
// HAL returns failure. |
|
EXPECT_FALSE(factory_->RegisterInstance(uuid0, callback)); |
|
EXPECT_EQ(0, callback_count); |
|
|
|
// HAL returns success. |
|
EXPECT_TRUE(factory_->RegisterInstance(uuid0, callback)); |
|
EXPECT_EQ(0, callback_count); |
|
|
|
// Calling twice with the same UUID should fail with no additional calls into |
|
// the stack. |
|
EXPECT_FALSE(factory_->RegisterInstance(uuid0, callback)); |
|
|
|
testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
|
|
|
// Call with a different UUID while one is pending. |
|
UUID uuid1 = UUID::GetRandom(); |
|
EXPECT_CALL(*mock_handler_, RegisterServer(_)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
EXPECT_TRUE(factory_->RegisterInstance(uuid1, callback)); |
|
|
|
// Trigger callback with an unknown UUID. This should get ignored. |
|
UUID uuid2 = UUID::GetRandom(); |
|
bt_uuid_t hal_uuid = uuid2.GetBlueDroid(); |
|
fake_hal_gatt_iface_->NotifyRegisterServerCallback(0, 0, hal_uuid); |
|
EXPECT_EQ(0, callback_count); |
|
|
|
// |uuid0| succeeds. |
|
int server_if0 = 2; // Pick something that's not 0. |
|
hal_uuid = uuid0.GetBlueDroid(); |
|
fake_hal_gatt_iface_->NotifyRegisterServerCallback(BT_STATUS_SUCCESS, |
|
server_if0, hal_uuid); |
|
|
|
EXPECT_EQ(1, callback_count); |
|
ASSERT_TRUE(server.get() != nullptr); // Assert to terminate in case of error |
|
EXPECT_EQ(BLE_STATUS_SUCCESS, status); |
|
EXPECT_EQ(server_if0, server->GetInstanceId()); |
|
EXPECT_EQ(uuid0, server->GetAppIdentifier()); |
|
EXPECT_EQ(uuid0, cb_uuid); |
|
|
|
// The server should unregister itself when deleted. |
|
EXPECT_CALL(*mock_handler_, UnregisterServer(server_if0)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
server.reset(); |
|
|
|
testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
|
|
|
// |uuid1| fails. |
|
int server_if1 = 3; |
|
hal_uuid = uuid1.GetBlueDroid(); |
|
fake_hal_gatt_iface_->NotifyRegisterServerCallback(BT_STATUS_FAIL, server_if1, |
|
hal_uuid); |
|
|
|
EXPECT_EQ(2, callback_count); |
|
ASSERT_TRUE(server.get() == nullptr); // Assert to terminate in case of error |
|
EXPECT_EQ(BLE_STATUS_FAILURE, status); |
|
EXPECT_EQ(uuid1, cb_uuid); |
|
} |
|
|
|
TEST_F(GattServerPostRegisterTest, RequestRead) { |
|
SetUpTestService(); |
|
|
|
TestDelegate test_delegate; |
|
gatt_server_->SetDelegate(&test_delegate); |
|
|
|
const std::vector<uint8_t> kTestValue = {0x01, 0x02, 0x03}; |
|
const std::vector<uint8_t> kTestValueTooLarge(BTGATT_MAX_ATTR_LEN + 1, 0); |
|
const std::string kTestAddress0 = "01:23:45:67:89:AB"; |
|
const std::string kTestAddress1 = "CD:EF:01:23:45:67"; |
|
const int kReqId0 = 0; |
|
const int kReqId1 = 1; |
|
const int kConnId0 = 1; |
|
|
|
// No pending request. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
RawAddress hal_addr0, hal_addr1; |
|
ASSERT_TRUE(RawAddress::FromString(kTestAddress0, hal_addr0)); |
|
ASSERT_TRUE(RawAddress::FromString(kTestAddress1, hal_addr1)); |
|
|
|
// Send a connection callback. The GattServer should store the connection |
|
// information and be able to process the incoming read requests for this |
|
// connection. |
|
fake_hal_gatt_iface_->NotifyServerConnectionCallback( |
|
kConnId0, kDefaultServerId, true, hal_addr0); |
|
|
|
// Unknown connection ID shouldn't trigger anything. |
|
fake_hal_gatt_iface_->NotifyRequestReadCharacteristicCallback( |
|
kConnId0 + 1, kReqId0, hal_addr0, char_handle_, 0, false); |
|
EXPECT_EQ(0, test_delegate.char_read_req().count); |
|
EXPECT_EQ(0, test_delegate.desc_read_req().count); |
|
|
|
// Unknown device address shouldn't trigger anything. |
|
fake_hal_gatt_iface_->NotifyRequestReadCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr1, char_handle_, 0, false); |
|
EXPECT_EQ(0, test_delegate.char_read_req().count); |
|
EXPECT_EQ(0, test_delegate.desc_read_req().count); |
|
|
|
// Characteristic and descriptor handles should trigger correct callbacks. |
|
fake_hal_gatt_iface_->NotifyRequestReadCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr0, char_handle_, 0, false); |
|
EXPECT_EQ(1, test_delegate.char_read_req().count); |
|
EXPECT_EQ(kTestAddress0, test_delegate.char_read_req().device_address); |
|
EXPECT_EQ(kReqId0, test_delegate.char_read_req().id); |
|
EXPECT_EQ(0, test_delegate.char_read_req().offset); |
|
EXPECT_FALSE(test_delegate.char_read_req().is_long); |
|
EXPECT_TRUE(char_handle_ == test_delegate.char_read_req().handle); |
|
EXPECT_EQ(0, test_delegate.desc_read_req().count); |
|
|
|
fake_hal_gatt_iface_->NotifyRequestReadDescriptorCallback( |
|
kConnId0, kReqId1, hal_addr0, desc_handle_, 2, true); |
|
EXPECT_EQ(1, test_delegate.char_read_req().count); |
|
EXPECT_EQ(1, test_delegate.desc_read_req().count); |
|
EXPECT_EQ(kTestAddress0, test_delegate.desc_read_req().device_address); |
|
EXPECT_EQ(kReqId1, test_delegate.desc_read_req().id); |
|
EXPECT_EQ(2, test_delegate.desc_read_req().offset); |
|
EXPECT_TRUE(test_delegate.desc_read_req().is_long); |
|
EXPECT_TRUE(desc_handle_ == test_delegate.desc_read_req().handle); |
|
|
|
// Callback with a pending request ID will be ignored. |
|
fake_hal_gatt_iface_->NotifyRequestReadCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr0, char_handle_, 0, false); |
|
fake_hal_gatt_iface_->NotifyRequestReadCharacteristicCallback( |
|
kConnId0, kReqId1, hal_addr0, char_handle_, 0, false); |
|
EXPECT_EQ(1, test_delegate.char_read_req().count); |
|
EXPECT_EQ(1, test_delegate.desc_read_req().count); |
|
|
|
// Send response for wrong device address. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress1, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Send response for a value that's too large. |
|
EXPECT_FALSE(gatt_server_->SendResponse( |
|
kTestAddress0, kReqId0, GATT_ERROR_NONE, 0, kTestValueTooLarge)); |
|
|
|
EXPECT_CALL(*mock_handler_, |
|
SendResponse(kConnId0, kReqId0, BT_STATUS_SUCCESS, _)) |
|
.Times(2) |
|
.WillOnce(Return(BT_STATUS_FAIL)) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
// Stack call fails. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Successful send response for characteristic. |
|
EXPECT_TRUE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Characteristic request ID no longer pending. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
EXPECT_CALL(*mock_handler_, |
|
SendResponse(kConnId0, kReqId1, BT_STATUS_SUCCESS, _)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
// Successful send response for descriptor. |
|
EXPECT_TRUE(gatt_server_->SendResponse(kTestAddress0, kReqId1, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Descriptor request ID no longer pending. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId1, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
gatt_server_->SetDelegate(nullptr); |
|
} |
|
|
|
TEST_F(GattServerPostRegisterTest, RequestWrite) { |
|
SetUpTestService(); |
|
|
|
TestDelegate test_delegate; |
|
gatt_server_->SetDelegate(&test_delegate); |
|
|
|
const std::vector<uint8_t> kTestValue = {0x01, 0x02, 0x03}; |
|
const std::string kTestAddress0 = "01:23:45:67:89:AB"; |
|
const std::string kTestAddress1 = "CD:EF:01:23:45:67"; |
|
const int kReqId0 = 0; |
|
const int kReqId1 = 1; |
|
const int kConnId0 = 1; |
|
|
|
// No pending request. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
RawAddress hal_addr0, hal_addr1; |
|
ASSERT_TRUE(RawAddress::FromString(kTestAddress0, hal_addr0)); |
|
ASSERT_TRUE(RawAddress::FromString(kTestAddress1, hal_addr1)); |
|
|
|
// Send a connection callback. The GattServer should store the connection |
|
// information and be able to process the incoming read requests for this |
|
// connection. |
|
fake_hal_gatt_iface_->NotifyServerConnectionCallback( |
|
kConnId0, kDefaultServerId, true, hal_addr0); |
|
|
|
// Unknown connection ID shouldn't trigger anything. |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0 + 1, kReqId0, hal_addr0, char_handle_, 0, true, false, |
|
kTestValue); |
|
EXPECT_EQ(0, test_delegate.char_write_req().count); |
|
EXPECT_EQ(0, test_delegate.desc_write_req().count); |
|
|
|
// Unknown device address shouldn't trigger anything. |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr1, char_handle_, 0, true, false, kTestValue); |
|
EXPECT_EQ(0, test_delegate.char_write_req().count); |
|
EXPECT_EQ(0, test_delegate.desc_write_req().count); |
|
|
|
// Characteristic and descriptor handles should trigger correct callbacks. |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr0, char_handle_, 0, true, false, kTestValue); |
|
EXPECT_EQ(1, test_delegate.char_write_req().count); |
|
EXPECT_EQ(kTestAddress0, test_delegate.char_write_req().device_address); |
|
EXPECT_EQ(kReqId0, test_delegate.char_write_req().id); |
|
EXPECT_EQ(0, test_delegate.char_write_req().offset); |
|
EXPECT_EQ(true, test_delegate.char_write_req().need_rsp); |
|
EXPECT_EQ(false, test_delegate.char_write_req().is_exec); |
|
EXPECT_EQ(kTestValue, test_delegate.char_write_req().write_value); |
|
EXPECT_TRUE(char_handle_ == test_delegate.char_write_req().handle); |
|
EXPECT_EQ(0, test_delegate.desc_write_req().count); |
|
|
|
fake_hal_gatt_iface_->NotifyRequestWriteDescriptorCallback( |
|
kConnId0, kReqId1, hal_addr0, desc_handle_, 2, true, false, kTestValue); |
|
EXPECT_EQ(1, test_delegate.char_write_req().count); |
|
EXPECT_EQ(1, test_delegate.desc_write_req().count); |
|
EXPECT_EQ(kTestAddress0, test_delegate.desc_write_req().device_address); |
|
EXPECT_EQ(kReqId1, test_delegate.desc_write_req().id); |
|
EXPECT_EQ(2, test_delegate.desc_write_req().offset); |
|
EXPECT_EQ(true, test_delegate.desc_write_req().need_rsp); |
|
EXPECT_EQ(false, test_delegate.desc_write_req().is_exec); |
|
EXPECT_EQ(kTestValue, test_delegate.desc_write_req().write_value); |
|
EXPECT_TRUE(desc_handle_ == test_delegate.desc_write_req().handle); |
|
|
|
// Callback with a pending request ID will be ignored. |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr0, char_handle_, 0, true, false, kTestValue); |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0, kReqId1, hal_addr0, char_handle_, 0, true, false, kTestValue); |
|
EXPECT_EQ(1, test_delegate.char_write_req().count); |
|
EXPECT_EQ(1, test_delegate.desc_write_req().count); |
|
|
|
// Send response for wrong device address. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress1, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
EXPECT_CALL(*mock_handler_, |
|
SendResponse(kConnId0, kReqId0, BT_STATUS_SUCCESS, _)) |
|
.Times(2) |
|
.WillOnce(Return(BT_STATUS_FAIL)) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
// Stack call fails. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Successful send response for characteristic. |
|
EXPECT_TRUE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Characteristic request ID no longer pending. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
EXPECT_CALL(*mock_handler_, |
|
SendResponse(kConnId0, kReqId1, BT_STATUS_SUCCESS, _)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
|
|
// Successful send response for descriptor. |
|
EXPECT_TRUE(gatt_server_->SendResponse(kTestAddress0, kReqId1, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// Descriptor request ID no longer pending. |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId1, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
// SendResponse should fail for a "Write Without Response". |
|
fake_hal_gatt_iface_->NotifyRequestWriteCharacteristicCallback( |
|
kConnId0, kReqId0, hal_addr0, char_handle_, 0, false, false, kTestValue); |
|
EXPECT_EQ(false, test_delegate.char_write_req().need_rsp); |
|
EXPECT_FALSE(gatt_server_->SendResponse(kTestAddress0, kReqId0, |
|
GATT_ERROR_NONE, 0, kTestValue)); |
|
|
|
gatt_server_->SetDelegate(nullptr); |
|
} |
|
|
|
TEST_F(GattServerPostRegisterTest, SendNotification) { |
|
SetUpTestService(); |
|
|
|
const std::string kTestAddress0 = "01:23:45:67:89:AB"; |
|
const std::string kTestAddress1 = "cd:ef:01:23:45:67"; |
|
const std::string kInvalidAddress = "thingamajig blabbidyboop"; |
|
const int kConnId0 = 0; |
|
const int kConnId1 = 1; |
|
std::vector<uint8_t> value; |
|
RawAddress hal_addr0; |
|
ASSERT_TRUE(RawAddress::FromString(kTestAddress0, hal_addr0)); |
|
|
|
// Set up two connections with the same address. |
|
fake_hal_gatt_iface_->NotifyServerConnectionCallback( |
|
kConnId0, kDefaultServerId, true, hal_addr0); |
|
fake_hal_gatt_iface_->NotifyServerConnectionCallback( |
|
kConnId1, kDefaultServerId, true, hal_addr0); |
|
|
|
// Set up a test callback. |
|
GATTError gatt_error; |
|
int callback_count = 0; |
|
auto callback = [&](GATTError in_error) { |
|
gatt_error = in_error; |
|
callback_count++; |
|
}; |
|
|
|
// Bad device address. |
|
EXPECT_FALSE(gatt_server_->SendNotification(kInvalidAddress, char_handle_, |
|
false, value, callback)); |
|
|
|
// Bad connection. |
|
EXPECT_FALSE(gatt_server_->SendNotification(kTestAddress1, char_handle_, |
|
false, value, callback)); |
|
|
|
// We should get a HAL call for each connection for this address. The calls |
|
// fail. |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId0, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_FAIL)); |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId1, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_FAIL)); |
|
EXPECT_FALSE(gatt_server_->SendNotification(kTestAddress0, char_handle_, |
|
false, value, callback)); |
|
|
|
// One of the calls succeeds. |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId0, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId1, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_FAIL)); |
|
EXPECT_TRUE(gatt_server_->SendNotification(kTestAddress0, char_handle_, false, |
|
value, callback)); |
|
|
|
// One of the connections is already pending so there should be only one call. |
|
// This one we send with confirm=true. |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId1, 1, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
EXPECT_TRUE(gatt_server_->SendNotification(kTestAddress0, char_handle_, true, |
|
value, callback)); |
|
|
|
// Calls are already pending. |
|
EXPECT_FALSE(gatt_server_->SendNotification(kTestAddress0, char_handle_, true, |
|
value, callback)); |
|
|
|
// Trigger one confirmation callback. We should get calls for two callbacks |
|
// since we have two separate calls pending. |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId0, |
|
BT_STATUS_SUCCESS); |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId1, |
|
BT_STATUS_SUCCESS); |
|
EXPECT_EQ(2, callback_count); |
|
EXPECT_EQ(GATT_ERROR_NONE, gatt_error); |
|
|
|
callback_count = 0; |
|
|
|
// Restart. Both calls succeed now. |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId0, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
EXPECT_CALL(*mock_handler_, SendIndication(kDefaultServerId, char_handle_, |
|
kConnId1, 0, value)) |
|
.Times(1) |
|
.WillOnce(Return(BT_STATUS_SUCCESS)); |
|
EXPECT_TRUE(gatt_server_->SendNotification(kTestAddress0, char_handle_, false, |
|
value, callback)); |
|
|
|
// Trigger one confirmation callback. The callback we passed should still be |
|
// pending. The first callback is for the wrong connection ID. |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId0 + 50, |
|
BT_STATUS_FAIL); |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId0, |
|
BT_STATUS_SUCCESS); |
|
EXPECT_EQ(0, callback_count); |
|
|
|
// This should be ignored since |kConnId0| was already processed. |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId0, |
|
BT_STATUS_SUCCESS); |
|
EXPECT_EQ(0, callback_count); |
|
|
|
// Run the callback with failure. Since the previous callback reported |
|
// success, we should report success. |
|
fake_hal_gatt_iface_->NotifyIndicationSentCallback(kConnId1, |
|
BT_STATUS_SUCCESS); |
|
EXPECT_EQ(1, callback_count); |
|
EXPECT_EQ(GATT_ERROR_NONE, gatt_error); |
|
} |
|
|
|
} // namespace |
|
} // namespace bluetooth
|
|
|