1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/gap/fake_adapter.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/transport/link_type.h"
18
19 namespace bt::gap::testing {
20
FakeAdapter(pw::async::Dispatcher & pw_dispatcher)21 FakeAdapter::FakeAdapter(pw::async::Dispatcher& pw_dispatcher)
22 : init_state_(InitState::kNotInitialized),
23 fake_le_(std::make_unique<FakeLowEnergy>(this)),
24 fake_bredr_(std::make_unique<FakeBrEdr>()),
25 heap_dispatcher_(pw_dispatcher),
26 peer_cache_(pw_dispatcher),
27 weak_self_(this) {}
28
Initialize(InitializeCallback callback,fit::closure transport_closed_callback)29 bool FakeAdapter::Initialize(InitializeCallback callback,
30 fit::closure transport_closed_callback) {
31 init_state_ = InitState::kInitializing;
32 (void)heap_dispatcher_.Post(
33 [this, cb = std::move(callback)](pw::async::Context /*ctx*/,
34 pw::Status status) mutable {
35 if (status.ok()) {
36 init_state_ = InitState::kInitialized;
37 cb(/*success=*/true);
38 }
39 });
40 return true;
41 }
42
ShutDown()43 void FakeAdapter::ShutDown() { init_state_ = InitState::kNotInitialized; }
44
~FakeBrEdr()45 FakeAdapter::FakeBrEdr::~FakeBrEdr() {
46 for (auto& chan : channels_) {
47 chan.second->Close();
48 }
49 }
50
OpenL2capChannel(PeerId peer_id,l2cap::Psm psm,BrEdrSecurityRequirements security_requirements,l2cap::ChannelParameters params,l2cap::ChannelCallback cb)51 void FakeAdapter::FakeBrEdr::OpenL2capChannel(
52 PeerId peer_id,
53 l2cap::Psm psm,
54 BrEdrSecurityRequirements security_requirements,
55 l2cap::ChannelParameters params,
56 l2cap::ChannelCallback cb) {
57 l2cap::ChannelInfo info(
58 params.mode.value_or(l2cap::RetransmissionAndFlowControlMode::kBasic),
59 params.max_rx_sdu_size.value_or(l2cap::kDefaultMTU),
60 /*max_tx_sdu_size=*/l2cap::kDefaultMTU,
61 /*n_frames_in_tx_window=*/0,
62 /*max_transmissions=*/0,
63 /*max_tx_pdu_payload_size=*/0,
64 psm,
65 params.flush_timeout);
66 l2cap::ChannelId local_id = next_channel_id_++;
67 auto channel = std::make_unique<l2cap::testing::FakeChannel>(
68 /*id=*/local_id,
69 /*remote_id=*/l2cap::kFirstDynamicChannelId,
70 /*handle=*/1,
71 bt::LinkType::kACL,
72 info);
73 l2cap::testing::FakeChannel::WeakPtr weak_fake_channel = channel->AsWeakPtr();
74 l2cap::Channel::WeakPtr weak_channel = channel->GetWeakPtr();
75 channels_.emplace(local_id, std::move(channel));
76 if (channel_cb_) {
77 channel_cb_(weak_fake_channel);
78 }
79 cb(weak_channel);
80 }
81
UpdateRandomAddress(DeviceAddress & address)82 void FakeAdapter::FakeLowEnergy::UpdateRandomAddress(DeviceAddress& address) {
83 random_ = address;
84 // Notify the callback about the change in address.
85 if (address_changed_callback_) {
86 address_changed_callback_();
87 }
88 }
89
Connect(PeerId peer_id,ConnectionResultCallback callback,LowEnergyConnectionOptions connection_options)90 void FakeAdapter::FakeLowEnergy::Connect(
91 PeerId peer_id,
92 ConnectionResultCallback callback,
93 LowEnergyConnectionOptions connection_options) {
94 connections_[peer_id] = Connection{peer_id, connection_options};
95
96 auto bondable_cb = [connection_options]() {
97 return connection_options.bondable_mode;
98 };
99 auto security_cb = []() { return sm::SecurityProperties(); };
100 auto handle = std::make_unique<LowEnergyConnectionHandle>(
101 peer_id,
102 /*handle=*/1,
103 /*release_cb=*/[](auto) {},
104 std::move(bondable_cb),
105 std::move(security_cb));
106 callback(fit::ok(std::move(handle)));
107 }
108
Disconnect(PeerId peer_id)109 bool FakeAdapter::FakeLowEnergy::Disconnect(PeerId peer_id) {
110 return connections_.erase(peer_id);
111 }
112
StartAdvertising(AdvertisingData data,AdvertisingData scan_rsp,AdvertisingInterval interval,bool anonymous,bool include_tx_power_level,std::optional<ConnectableAdvertisingParameters> connectable,AdvertisingStatusCallback status_callback)113 void FakeAdapter::FakeLowEnergy::StartAdvertising(
114 AdvertisingData data,
115 AdvertisingData scan_rsp,
116 AdvertisingInterval interval,
117 bool anonymous,
118 bool include_tx_power_level,
119 std::optional<ConnectableAdvertisingParameters> connectable,
120 AdvertisingStatusCallback status_callback) {
121 // status_callback is currently not called because its parameters can only be
122 // constructed by LowEnergyAdvertisingManager.
123
124 RegisteredAdvertisement adv{.data = std::move(data),
125 .scan_rsp = std::move(scan_rsp),
126 .connectable = std::move(connectable),
127 .interval = interval,
128 .anonymous = anonymous,
129 .include_tx_power_level = include_tx_power_level};
130 AdvertisementId adv_id = next_advertisement_id_;
131 next_advertisement_id_ = AdvertisementId(next_advertisement_id_.value() + 1);
132 advertisements_.emplace(adv_id, std::move(adv));
133 }
134
EnablePrivacy(bool enabled)135 void FakeAdapter::FakeLowEnergy::EnablePrivacy(bool enabled) {
136 privacy_enabled_ = enabled;
137 if (!enabled && random_.has_value()) {
138 random_.reset();
139 if (address_changed_callback_) {
140 address_changed_callback_();
141 }
142 }
143 }
144
145 FakeAdapter::FakeBrEdr::RegistrationHandle
RegisterService(std::vector<sdp::ServiceRecord> records,l2cap::ChannelParameters chan_params,ServiceConnectCallback conn_cb)146 FakeAdapter::FakeBrEdr::RegisterService(std::vector<sdp::ServiceRecord> records,
147 l2cap::ChannelParameters chan_params,
148 ServiceConnectCallback conn_cb) {
149 auto handle = next_service_handle_++;
150 registered_services_.emplace(
151 handle,
152 RegisteredService{std::move(records), chan_params, std::move(conn_cb)});
153 return handle;
154 }
155
SetLocalName(std::string name,hci::ResultFunction<> callback)156 void FakeAdapter::SetLocalName(std::string name,
157 hci::ResultFunction<> callback) {
158 local_name_ = name;
159 callback(fit::ok());
160 }
161
SetDeviceClass(DeviceClass dev_class,hci::ResultFunction<> callback)162 void FakeAdapter::SetDeviceClass(DeviceClass dev_class,
163 hci::ResultFunction<> callback) {
164 device_class_ = dev_class;
165 callback(fit::ok());
166 }
167
AddServiceSearch(const UUID & uuid,std::unordered_set<sdp::AttributeId> attributes,SearchCallback callback)168 BrEdrConnectionManager::SearchId FakeAdapter::FakeBrEdr::AddServiceSearch(
169 const UUID& uuid,
170 std::unordered_set<sdp::AttributeId> attributes,
171 SearchCallback callback) {
172 auto handle = next_search_handle_++;
173 registered_searches_.emplace(
174 handle,
175 RegisteredSearch{uuid, std::move(attributes), std::move(callback)});
176 return SearchId(handle);
177 }
178
TriggerServiceFound(PeerId peer_id,UUID uuid,std::map<sdp::AttributeId,sdp::DataElement> attributes)179 void FakeAdapter::FakeBrEdr::TriggerServiceFound(
180 PeerId peer_id,
181 UUID uuid,
182 std::map<sdp::AttributeId, sdp::DataElement> attributes) {
183 for (auto it = registered_searches_.begin(); it != registered_searches_.end();
184 it++) {
185 if (it->second.uuid == uuid) {
186 it->second.callback(peer_id, attributes);
187 }
188 }
189 }
190
191 } // namespace bt::gap::testing
192