• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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