blob: bfdcbb0fde6e82d3026e12e6e1d3c2dce1baba64 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gatt/fake_layer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gatt/remote_service.h"
namespace bt::gatt::testing {
FakeLayer::TestPeer::TestPeer(pw::async::Dispatcher& pw_dispatcher)
: fake_client(pw_dispatcher) {}
std::pair<RemoteService::WeakPtr, FakeClient::WeakPtr>
FakeLayer::AddPeerService(PeerId peer_id,
const ServiceData& info,
bool notify) {
auto [iter, _] = peers_.try_emplace(peer_id, pw_dispatcher_);
auto& peer = iter->second;
BT_ASSERT(info.range_start <= info.range_end);
auto service =
std::make_unique<RemoteService>(info, peer.fake_client.GetWeakPtr());
RemoteService::WeakPtr service_weak = service->GetWeakPtr();
std::vector<att::Handle> removed;
ServiceList added;
ServiceList modified;
auto svc_iter = peer.services.find(info.range_start);
if (svc_iter != peer.services.end()) {
if (svc_iter->second->uuid() == info.type) {
modified.push_back(service_weak);
} else {
removed.push_back(svc_iter->second->handle());
added.push_back(service_weak);
}
svc_iter->second->set_service_changed(true);
peer.services.erase(svc_iter);
} else {
added.push_back(service_weak);
}
bt_log(DEBUG,
"gatt",
"services changed (removed: %zu, added: %zu, modified: %zu)",
removed.size(),
added.size(),
modified.size());
peer.services.emplace(info.range_start, std::move(service));
if (notify && remote_service_watchers_.count(peer_id)) {
remote_service_watchers_[peer_id](removed, added, modified);
}
return {service_weak, peer.fake_client.AsFakeWeakPtr()};
}
void FakeLayer::RemovePeerService(PeerId peer_id, att::Handle handle) {
auto peer_iter = peers_.find(peer_id);
if (peer_iter == peers_.end()) {
return;
}
auto svc_iter = peer_iter->second.services.find(handle);
if (svc_iter == peer_iter->second.services.end()) {
return;
}
svc_iter->second->set_service_changed(true);
peer_iter->second.services.erase(svc_iter);
if (remote_service_watchers_.count(peer_id)) {
remote_service_watchers_[peer_id](
/*removed=*/{handle}, /*added=*/{}, /*modified=*/{});
}
}
void FakeLayer::AddConnection(PeerId peer_id,
std::unique_ptr<Client> client,
Server::FactoryFunction server_factory) {
peers_.try_emplace(peer_id, pw_dispatcher_);
}
void FakeLayer::RemoveConnection(PeerId peer_id) { peers_.erase(peer_id); }
GATT::PeerMtuListenerId FakeLayer::RegisterPeerMtuListener(
PeerMtuListener listener) {
BT_PANIC("implement fake behavior if needed");
}
bool FakeLayer::UnregisterPeerMtuListener(PeerMtuListenerId listener_id) {
BT_PANIC("implement fake behavior if needed");
}
void FakeLayer::RegisterService(ServicePtr service,
ServiceIdCallback callback,
ReadHandler read_handler,
WriteHandler write_handler,
ClientConfigCallback ccc_callback) {
if (register_service_fails_) {
callback(kInvalidId);
return;
}
IdType id = next_local_service_id_++;
local_services_.try_emplace(id,
LocalService{std::move(service),
std::move(read_handler),
std::move(write_handler),
std::move(ccc_callback),
{}});
callback(id);
}
void FakeLayer::UnregisterService(IdType service_id) {
local_services_.erase(service_id);
}
void FakeLayer::SendUpdate(IdType service_id,
IdType chrc_id,
PeerId peer_id,
::std::vector<uint8_t> value,
IndicationCallback indicate_cb) {
auto iter = local_services_.find(service_id);
if (iter == local_services_.end()) {
indicate_cb(fit::error(att::ErrorCode::kInvalidHandle));
return;
}
iter->second.updates.push_back(
Update{chrc_id, std::move(value), std::move(indicate_cb), peer_id});
}
void FakeLayer::UpdateConnectedPeers(IdType service_id,
IdType chrc_id,
::std::vector<uint8_t> value,
IndicationCallback indicate_cb) {
auto iter = local_services_.find(service_id);
if (iter == local_services_.end()) {
indicate_cb(fit::error(att::ErrorCode::kInvalidHandle));
return;
}
iter->second.updates.push_back(
Update{chrc_id, std::move(value), std::move(indicate_cb), std::nullopt});
}
void FakeLayer::SetPersistServiceChangedCCCCallback(
PersistServiceChangedCCCCallback callback) {
if (set_persist_service_changed_ccc_cb_cb_) {
set_persist_service_changed_ccc_cb_cb_();
}
persist_service_changed_ccc_cb_ = std::move(callback);
}
void FakeLayer::SetRetrieveServiceChangedCCCCallback(
RetrieveServiceChangedCCCCallback callback) {
if (set_retrieve_service_changed_ccc_cb_cb_) {
set_retrieve_service_changed_ccc_cb_cb_();
}
retrieve_service_changed_ccc_cb_ = std::move(callback);
}
void FakeLayer::InitializeClient(PeerId peer_id,
std::vector<UUID> services_to_discover) {
std::vector<UUID> uuids = std::move(services_to_discover);
if (initialize_client_cb_) {
initialize_client_cb_(peer_id, uuids);
}
auto iter = peers_.find(peer_id);
if (iter == peers_.end()) {
return;
}
std::vector<RemoteService::WeakPtr> added;
if (uuids.empty()) {
for (auto& svc_pair : iter->second.services) {
added.push_back(svc_pair.second->GetWeakPtr());
}
} else {
for (auto& svc_pair : iter->second.services) {
auto uuid_iter =
std::find_if(uuids.begin(), uuids.end(), [&svc_pair](auto uuid) {
return svc_pair.second->uuid() == uuid;
});
if (uuid_iter != uuids.end()) {
added.push_back(svc_pair.second->GetWeakPtr());
}
}
}
if (remote_service_watchers_.count(peer_id)) {
remote_service_watchers_[peer_id](
/*removed=*/{}, /*added=*/added, /*modified=*/{});
}
}
GATT::RemoteServiceWatcherId FakeLayer::RegisterRemoteServiceWatcherForPeer(
PeerId peer_id, RemoteServiceWatcher watcher) {
BT_ASSERT(remote_service_watchers_.count(peer_id) == 0);
remote_service_watchers_[peer_id] = std::move(watcher);
// Use the PeerId as the watcher ID because FakeLayer only needs to support 1
// watcher per peer.
return peer_id.value();
}
bool FakeLayer::UnregisterRemoteServiceWatcher(
RemoteServiceWatcherId watcher_id) {
bool result = remote_service_watchers_.count(PeerId(watcher_id));
remote_service_watchers_.erase(PeerId(watcher_id));
return result;
}
void FakeLayer::ListServices(PeerId peer_id,
std::vector<UUID> uuids,
ServiceListCallback callback) {
if (pause_list_services_) {
return;
}
ServiceList services;
auto iter = peers_.find(peer_id);
if (iter != peers_.end()) {
for (auto& svc_pair : iter->second.services) {
auto pred = [&](const UUID& uuid) {
return svc_pair.second->uuid() == uuid;
};
if (uuids.empty() ||
std::find_if(uuids.begin(), uuids.end(), pred) != uuids.end()) {
services.push_back(svc_pair.second->GetWeakPtr());
}
}
}
callback(list_services_status_, std::move(services));
}
RemoteService::WeakPtr FakeLayer::FindService(PeerId peer_id,
IdType service_id) {
auto peer_iter = peers_.find(peer_id);
if (peer_iter == peers_.end()) {
return RemoteService::WeakPtr();
}
auto svc_iter = peer_iter->second.services.find(service_id);
if (svc_iter == peer_iter->second.services.end()) {
return RemoteService::WeakPtr();
}
return svc_iter->second->GetWeakPtr();
}
void FakeLayer::SetInitializeClientCallback(InitializeClientCallback cb) {
initialize_client_cb_ = std::move(cb);
}
void FakeLayer::set_list_services_status(att::Result<> status) {
list_services_status_ = status;
}
void FakeLayer::SetSetPersistServiceChangedCCCCallbackCallback(
SetPersistServiceChangedCCCCallbackCallback cb) {
set_persist_service_changed_ccc_cb_cb_ = std::move(cb);
}
void FakeLayer::SetSetRetrieveServiceChangedCCCCallbackCallback(
SetRetrieveServiceChangedCCCCallbackCallback cb) {
set_retrieve_service_changed_ccc_cb_cb_ = std::move(cb);
}
void FakeLayer::CallPersistServiceChangedCCCCallback(PeerId peer_id,
bool notify,
bool indicate) {
persist_service_changed_ccc_cb_(peer_id,
{.notify = notify, .indicate = indicate});
}
std::optional<ServiceChangedCCCPersistedData>
FakeLayer::CallRetrieveServiceChangedCCCCallback(PeerId peer_id) {
return retrieve_service_changed_ccc_cb_(peer_id);
}
} // namespace bt::gatt::testing