1 // Copyright 2024 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/fuchsia/host/fidl/low_energy_peripheral_server.h"
16 
17 #include <lib/async/default.h>
18 #include <zircon/status.h>
19 
20 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_advertising_manager.h"
26 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h"
27 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
28 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
29 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
30 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
31 
32 #define LOG_TAG "fidl"
33 
34 using bt::sm::BondableMode;
35 namespace fble = fuchsia::bluetooth::le;
36 
37 namespace bthost {
38 namespace {
39 
FidlErrorFromStatus(bt::hci::Result<> status)40 fble::PeripheralError FidlErrorFromStatus(bt::hci::Result<> status) {
41   PW_CHECK(status.is_error(), "FidlErrorFromStatus called on success status");
42   return status.error_value().Visit(
43       [](bt::HostError host_error) {
44         switch (host_error) {
45           case bt::HostError::kNotSupported:
46             return fble::PeripheralError::NOT_SUPPORTED;
47           case bt::HostError::kInvalidParameters:
48             return fble::PeripheralError::INVALID_PARAMETERS;
49           case bt::HostError::kAdvertisingDataTooLong:
50             return fble::PeripheralError::ADVERTISING_DATA_TOO_LONG;
51           case bt::HostError::kScanResponseTooLong:
52             return fble::PeripheralError::SCAN_RESPONSE_DATA_TOO_LONG;
53           case bt::HostError::kCanceled:
54             return fble::PeripheralError::ABORTED;
55           default:
56             break;
57         }
58         return fble::PeripheralError::FAILED;
59       },
60       [](auto /*hci_error*/) { return fble::PeripheralError::FAILED; });
61 }
62 
63 }  // namespace
64 
AdvertisementInstance(LowEnergyPeripheralServer * peripheral_server,AdvertisementInstanceId id,fuchsia::bluetooth::le::AdvertisingParameters parameters,fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> handle,AdvertiseCompleteCallback complete_cb)65 LowEnergyPeripheralServer::AdvertisementInstance::AdvertisementInstance(
66     LowEnergyPeripheralServer* peripheral_server,
67     AdvertisementInstanceId id,
68     fuchsia::bluetooth::le::AdvertisingParameters parameters,
69     fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> handle,
70     AdvertiseCompleteCallback complete_cb)
71     : peripheral_server_(peripheral_server),
72       id_(id),
73       parameters_(std::move(parameters)),
74       advertise_complete_cb_(std::move(complete_cb)),
75       weak_self_(this) {
76   PW_CHECK(advertise_complete_cb_);
77   advertised_peripheral_.Bind(std::move(handle));
78   advertised_peripheral_.set_error_handler(
79       [this, peripheral_server, id](zx_status_t /*status*/) {
80         CloseWith(fpromise::ok());
81         peripheral_server->RemoveAdvertisingInstance(id);
82       });
83 }
84 
~AdvertisementInstance()85 LowEnergyPeripheralServer::AdvertisementInstance::~AdvertisementInstance() {
86   if (advertise_complete_cb_) {
87     CloseWith(fpromise::error(fble::PeripheralError::ABORTED));
88   }
89 }
90 
StartAdvertising()91 void LowEnergyPeripheralServer::AdvertisementInstance::StartAdvertising() {
92   auto self = weak_self_.GetWeakPtr();
93   auto status_cb = [self](auto adv_instance, bt::hci::Result<> status) {
94     if (!self.is_alive()) {
95       bt_log(
96           DEBUG, LOG_TAG, "advertisement canceled before advertising started");
97       // Destroying `adv_instance` will stop advertising.
98       return;
99     }
100 
101     if (bt_is_error(status,
102                     WARN,
103                     LOG_TAG,
104                     "failed to start advertising (status: %s)",
105                     bt_str(status))) {
106       self->CloseWith(fpromise::error(FidlErrorFromStatus(status)));
107       self->peripheral_server_->RemoveAdvertisingInstance(self->id_);
108       return;
109     }
110 
111     self->Register(std::move(adv_instance));
112   };
113 
114   peripheral_server_->StartAdvertisingInternal(
115       parameters_, std::move(status_cb), self->id_);
116 }
117 
ListenL2cap(fble::ChannelListenerRegistryListenL2capRequest request,ListenL2capCallback callback)118 void LowEnergyPeripheralServer::ListenL2cap(
119     fble::ChannelListenerRegistryListenL2capRequest request,
120     ListenL2capCallback callback) {
121   // TODO(fxbug.dev/42178956): Implement ListenL2cap.
122   fble::ChannelListenerRegistry_ListenL2cap_Result result;
123   callback(std::move(result.set_err(ZX_ERR_NOT_SUPPORTED)));
124 }
125 
Register(bt::gap::AdvertisementInstance instance)126 void LowEnergyPeripheralServer::AdvertisementInstance::Register(
127     bt::gap::AdvertisementInstance instance) {
128   PW_CHECK(!instance_);
129   instance_ = std::move(instance);
130 }
131 
OnConnected(bt::gap::AdvertisementId advertisement_id,bt::gap::Adapter::LowEnergy::ConnectionResult result)132 void LowEnergyPeripheralServer::AdvertisementInstance::OnConnected(
133     bt::gap::AdvertisementId advertisement_id,
134     bt::gap::Adapter::LowEnergy::ConnectionResult result) {
135   PW_CHECK(advertisement_id != bt::gap::kInvalidAdvertisementId);
136 
137   // HCI advertising ends when a connection is received (even for error
138   // results), so clear the stale advertisement handle.
139   instance_.reset();
140 
141   if (result.is_error()) {
142     bt_log(INFO,
143            LOG_TAG,
144            "incoming connection failed; restarting advertising (adv instance "
145            "id: %zu, prev adv "
146            "id: %s)",
147            id_,
148            bt_str(advertisement_id));
149     StartAdvertising();
150     return;
151   }
152 
153   std::unique_ptr<bt::gap::LowEnergyConnectionHandle> conn =
154       std::move(result).value();
155   bt::PeerId peer_id = conn->peer_identifier();
156   bt::gap::Peer* peer =
157       peripheral_server_->adapter()->peer_cache()->FindById(peer_id);
158   PW_CHECK(peer);
159 
160   bt_log(INFO,
161          LOG_TAG,
162          "peripheral received connection to advertisement (peer: %s, adv id: "
163          "%s, adv "
164          "instance id: %zu)",
165          bt_str(peer->identifier()),
166          bt_str(advertisement_id),
167          id_);
168 
169   fidl::InterfaceHandle<fble::Connection> conn_handle =
170       peripheral_server_->CreateConnectionServer(std::move(conn));
171 
172   // Restart advertising after the client acknowledges the connection.
173   auto self = weak_self_.GetWeakPtr();
174   auto on_connected_cb = [self] {
175     if (self.is_alive()) {
176       self->StartAdvertising();
177     }
178   };
179   advertised_peripheral_->OnConnected(fidl_helpers::PeerToFidlLe(*peer),
180                                       std::move(conn_handle),
181                                       std::move(on_connected_cb));
182 }
183 
CloseWith(fpromise::result<void,fuchsia::bluetooth::le::PeripheralError> result)184 void LowEnergyPeripheralServer::AdvertisementInstance::CloseWith(
185     fpromise::result<void, fuchsia::bluetooth::le::PeripheralError> result) {
186   if (advertise_complete_cb_) {
187     advertised_peripheral_.Unbind();
188     advertise_complete_cb_(std::move(result));
189   }
190 }
191 
192 LowEnergyPeripheralServer::AdvertisementInstanceDeprecated::
AdvertisementInstanceDeprecated(fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle> handle)193     AdvertisementInstanceDeprecated(
194         fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle>
195             handle)
196     : handle_(std::move(handle)) {
197   PW_DCHECK(handle_);
198 }
199 
200 LowEnergyPeripheralServer::AdvertisementInstanceDeprecated::
~AdvertisementInstanceDeprecated()201     ~AdvertisementInstanceDeprecated() {
202   handle_closed_wait_.Cancel();
203 }
204 
205 zx_status_t
Register(bt::gap::AdvertisementInstance instance)206 LowEnergyPeripheralServer::AdvertisementInstanceDeprecated::Register(
207     bt::gap::AdvertisementInstance instance) {
208   PW_DCHECK(!instance_);
209 
210   instance_ = std::move(instance);
211 
212   handle_closed_wait_.set_object(handle_.channel().get());
213   handle_closed_wait_.set_trigger(ZX_CHANNEL_PEER_CLOSED);
214   handle_closed_wait_.set_handler(
215       [this](auto*, auto*, zx_status_t status, const auto*) {
216         // Don't do anything if the wait was explicitly canceled by us.
217         if (status != ZX_ERR_CANCELED) {
218           bt_log(TRACE, LOG_TAG, "AdvertisingHandle closed");
219           instance_.reset();
220         }
221       });
222 
223   zx_status_t status =
224       handle_closed_wait_.Begin(async_get_default_dispatcher());
225   if (status != ZX_OK) {
226     bt_log(DEBUG,
227            LOG_TAG,
228            "failed to begin wait on AdvertisingHandle: %s",
229            zx_status_get_string(status));
230   }
231   return status;
232 }
233 
LowEnergyPeripheralServer(bt::gap::Adapter::WeakPtr adapter,bt::gatt::GATT::WeakPtr gatt,fidl::InterfaceRequest<Peripheral> request,bool privileged)234 LowEnergyPeripheralServer::LowEnergyPeripheralServer(
235     bt::gap::Adapter::WeakPtr adapter,
236     bt::gatt::GATT::WeakPtr gatt,
237     fidl::InterfaceRequest<Peripheral> request,
238     bool privileged)
239     : AdapterServerBase(std::move(adapter), this, std::move(request)),
240       gatt_(std::move(gatt)),
241       privileged_(privileged),
242       weak_self_(this) {}
243 
~LowEnergyPeripheralServer()244 LowEnergyPeripheralServer::~LowEnergyPeripheralServer() {
245   PW_CHECK(adapter()->bredr());
246 }
247 
Advertise(fble::AdvertisingParameters parameters,fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> advertised_peripheral,AdvertiseCallback callback)248 void LowEnergyPeripheralServer::Advertise(
249     fble::AdvertisingParameters parameters,
250     fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral>
251         advertised_peripheral,
252     AdvertiseCallback callback) {
253   // Advertise and StartAdvertising may not be used simultaneously.
254   if (advertisement_deprecated_.has_value()) {
255     callback(fpromise::error(fble::PeripheralError::FAILED));
256     return;
257   }
258 
259   // TODO: https://fxbug.dev/42156474 - As a temporary hack until multiple
260   // advertisements is supported, don't allow more than one advertisement. The
261   // current behavior of hci::LegacyLowEnergyAdvertiser is to replace the
262   // current advertisement, which is not the intended behavior of `Advertise`.
263   // NOTE: This is insufficient  when there are multiple Peripheral clients
264   // advertising, but that is the status quo with `StartAdvertising` anyway (the
265   // last advertiser wins).
266   if (!advertisements_.empty()) {
267     callback(fpromise::error(fble::PeripheralError::FAILED));
268     return;
269   }
270 
271   AdvertisementInstanceId instance_id = next_advertisement_instance_id_++;
272 
273   // Non-privileged clients should not be able to advertise with a public
274   // address, so we default to a random address type.
275   if (!privileged_ && parameters.has_address_type() &&
276       parameters.address_type() == fuchsia::bluetooth::AddressType::PUBLIC) {
277     bt_log(WARN,
278            LOG_TAG,
279            "Cannot advertise public address (instance id: %zu)",
280            instance_id);
281     callback(fpromise::error(fble::PeripheralError::INVALID_PARAMETERS));
282     return;
283   }
284 
285   auto [iter, inserted] =
286       advertisements_.try_emplace(instance_id,
287                                   this,
288                                   instance_id,
289                                   std::move(parameters),
290                                   std::move(advertised_peripheral),
291                                   std::move(callback));
292   PW_CHECK(inserted);
293   iter->second.StartAdvertising();
294 }
295 
StartAdvertising(fble::AdvertisingParameters parameters,::fidl::InterfaceRequest<fble::AdvertisingHandle> token,StartAdvertisingCallback callback)296 void LowEnergyPeripheralServer::StartAdvertising(
297     fble::AdvertisingParameters parameters,
298     ::fidl::InterfaceRequest<fble::AdvertisingHandle> token,
299     StartAdvertisingCallback callback) {
300   fble::Peripheral_StartAdvertising_Result result;
301 
302   // Advertise and StartAdvertising may not be used simultaneously.
303   if (!advertisements_.empty()) {
304     result.set_err(fble::PeripheralError::INVALID_PARAMETERS);
305     callback(std::move(result));
306     return;
307   }
308 
309   if (!token) {
310     result.set_err(fble::PeripheralError::INVALID_PARAMETERS);
311     callback(std::move(result));
312     return;
313   }
314 
315   if (advertisement_deprecated_) {
316     bt_log(DEBUG, LOG_TAG, "reconfigure existing advertising instance");
317     advertisement_deprecated_.reset();
318   }
319 
320   // Create an entry to mark that the request is in progress.
321   advertisement_deprecated_.emplace(std::move(token));
322 
323   auto self = weak_self_.GetWeakPtr();
324   auto status_cb = [self, callback = std::move(callback), func = __FUNCTION__](
325                        auto instance, bt::hci::Result<> status) {
326     // Advertising will be stopped when |instance| gets destroyed.
327     if (!self.is_alive()) {
328       return;
329     }
330 
331     PW_CHECK(self->advertisement_deprecated_);
332     PW_CHECK(self->advertisement_deprecated_->id() ==
333              bt::gap::kInvalidAdvertisementId);
334 
335     fble::Peripheral_StartAdvertising_Result result;
336     if (status.is_error()) {
337       bt_log(WARN,
338              LOG_TAG,
339              "%s: failed to start advertising (status: %s)",
340              func,
341              bt_str(status));
342 
343       result.set_err(FidlErrorFromStatus(status));
344 
345       // The only scenario in which it is valid to leave |advertisement_| intact
346       // in a failure scenario is if StartAdvertising was called while a
347       // previous call was in progress. This aborts the prior request causing it
348       // to end with the "kCanceled" status. This means that another request is
349       // currently progress.
350       if (!status.error_value().is(bt::HostError::kCanceled)) {
351         self->advertisement_deprecated_.reset();
352       }
353 
354       callback(std::move(result));
355       return;
356     }
357 
358     zx_status_t ecode =
359         self->advertisement_deprecated_->Register(std::move(instance));
360     if (ecode != ZX_OK) {
361       result.set_err(fble::PeripheralError::FAILED);
362       self->advertisement_deprecated_.reset();
363       callback(std::move(result));
364       return;
365     }
366 
367     result.set_response({});
368     callback(std::move(result));
369   };
370 
371   StartAdvertisingInternal(parameters, std::move(status_cb));
372 }
373 
374 const bt::gap::LowEnergyConnectionHandle*
FindConnectionForTesting(bt::PeerId id) const375 LowEnergyPeripheralServer::FindConnectionForTesting(bt::PeerId id) const {
376   auto connections_iter = std::find_if(
377       connections_.begin(), connections_.end(), [id](const auto& conn) {
378         return conn.second->conn()->peer_identifier() == id;
379       });
380   if (connections_iter != connections_.end()) {
381     return connections_iter->second->conn();
382   }
383   return nullptr;
384 }
385 
OnConnectedDeprecated(bt::gap::AdvertisementId advertisement_id,bt::gap::Adapter::LowEnergy::ConnectionResult result)386 void LowEnergyPeripheralServer::OnConnectedDeprecated(
387     bt::gap::AdvertisementId advertisement_id,
388     bt::gap::Adapter::LowEnergy::ConnectionResult result) {
389   PW_CHECK(advertisement_id != bt::gap::kInvalidAdvertisementId);
390 
391   // Abort connection procedure if advertisement was canceled by the client.
392   if (!advertisement_deprecated_ ||
393       advertisement_deprecated_->id() != advertisement_id) {
394     bt_log(
395         INFO,
396         LOG_TAG,
397         "dropping connection to canceled advertisement (advertisement id: %s)",
398         bt_str(advertisement_id));
399     return;
400   }
401 
402   zx::channel local, remote;
403   zx_status_t status = zx::channel::create(0, &local, &remote);
404   if (status != ZX_OK) {
405     bt_log(ERROR,
406            LOG_TAG,
407            "failed to create channel for Connection (status: %s)",
408            zx_status_get_string(status));
409     return;
410   }
411 
412   if (result.is_error()) {
413     bt_log(INFO,
414            LOG_TAG,
415            "incoming connection to advertisement failed (advertisement id: %s)",
416            bt_str(advertisement_id));
417     return;
418   }
419 
420   auto conn = std::move(result).value();
421   auto peer_id = conn->peer_identifier();
422   auto* peer = adapter()->peer_cache()->FindById(peer_id);
423   PW_CHECK(peer);
424 
425   bt_log(INFO,
426          LOG_TAG,
427          "central connected (peer: %s, advertisement id: %s)",
428          bt_str(peer->identifier()),
429          bt_str(advertisement_id));
430 
431   fidl::InterfaceHandle<fble::Connection> conn_handle =
432       CreateConnectionServer(std::move(conn));
433 
434   binding()->events().OnPeerConnected(fidl_helpers::PeerToFidlLe(*peer),
435                                       std::move(conn_handle));
436   advertisement_deprecated_.reset();
437 }
438 
439 fidl::InterfaceHandle<fuchsia::bluetooth::le::Connection>
CreateConnectionServer(std::unique_ptr<bt::gap::LowEnergyConnectionHandle> connection)440 LowEnergyPeripheralServer::CreateConnectionServer(
441     std::unique_ptr<bt::gap::LowEnergyConnectionHandle> connection) {
442   zx::channel local, remote;
443   zx_status_t status = zx::channel::create(0, &local, &remote);
444   PW_CHECK(status == ZX_OK);
445 
446   auto conn_server_id = next_connection_server_id_++;
447   auto conn_server = std::make_unique<LowEnergyConnectionServer>(
448       adapter(),
449       gatt_,
450       std::move(connection),
451       std::move(local),
452       [this, conn_server_id] {
453         bt_log(INFO, LOG_TAG, "connection closed");
454         connections_.erase(conn_server_id);
455       });
456   connections_[conn_server_id] = std::move(conn_server);
457 
458   return fidl::InterfaceHandle<fble::Connection>(std::move(remote));
459 }
460 
StartAdvertisingInternal(fuchsia::bluetooth::le::AdvertisingParameters & parameters,bt::gap::Adapter::LowEnergy::AdvertisingStatusCallback status_cb,std::optional<AdvertisementInstanceId> advertisement_instance)461 void LowEnergyPeripheralServer::StartAdvertisingInternal(
462     fuchsia::bluetooth::le::AdvertisingParameters& parameters,
463     bt::gap::Adapter::LowEnergy::AdvertisingStatusCallback status_cb,
464     std::optional<AdvertisementInstanceId> advertisement_instance) {
465   bt::AdvertisingData adv_data;
466   bool include_tx_power_level = false;
467   if (parameters.has_data()) {
468     auto maybe_adv_data =
469         fidl_helpers::AdvertisingDataFromFidl(parameters.data());
470     if (!maybe_adv_data) {
471       bt_log(WARN, LOG_TAG, "invalid advertising data");
472       status_cb({}, ToResult(bt::HostError::kInvalidParameters));
473       return;
474     }
475 
476     adv_data = std::move(*maybe_adv_data);
477     if (parameters.data().has_include_tx_power_level() &&
478         parameters.data().include_tx_power_level()) {
479       bt_log(TRACE,
480              LOG_TAG,
481              "Including TX Power level in advertising data at HCI layer");
482       include_tx_power_level = true;
483     }
484   }
485 
486   bt::AdvertisingData scan_rsp;
487   if (parameters.has_scan_response()) {
488     auto maybe_scan_rsp =
489         fidl_helpers::AdvertisingDataFromFidl(parameters.scan_response());
490     if (!maybe_scan_rsp) {
491       bt_log(WARN, LOG_TAG, "invalid scan response in advertising data");
492       status_cb({}, ToResult(bt::HostError::kInvalidParameters));
493       return;
494     }
495     scan_rsp = std::move(*maybe_scan_rsp);
496   }
497 
498   fble::AdvertisingModeHint mode_hint = fble::AdvertisingModeHint::SLOW;
499   if (parameters.has_mode_hint()) {
500     mode_hint = parameters.mode_hint();
501   }
502   bt::gap::AdvertisingInterval interval =
503       fidl_helpers::AdvertisingIntervalFromFidl(mode_hint);
504 
505   std::optional<bt::gap::Adapter::LowEnergy::ConnectableAdvertisingParameters>
506       connectable_params;
507 
508   // Per the API contract of `AdvertisingParameters` FIDL, if
509   // `connection_options` is present or the deprecated `connectable` parameter
510   // is true, advertisements will be connectable. `connectable_parameter` was
511   // the predecessor of `connection_options` and
512   // TODO: https://fxbug.dev/42121197 - will be removed once all consumers of it
513   // have migrated to `connection_options`.
514   bool connectable = parameters.has_connection_options() ||
515                      (parameters.has_connectable() && parameters.connectable());
516   if (connectable) {
517     connectable_params.emplace();
518 
519     auto self = weak_self_.GetWeakPtr();
520     connectable_params->connection_cb =
521         [self, advertisement_instance](
522             bt::gap::AdvertisementId advertisement_id,
523             bt::gap::Adapter::LowEnergy::ConnectionResult result) {
524           if (!self.is_alive()) {
525             return;
526           }
527 
528           // Handle connection for deprecated StartAdvertising method.
529           if (!advertisement_instance) {
530             self->OnConnectedDeprecated(advertisement_id, std::move(result));
531             return;
532           }
533 
534           auto advertisement_iter =
535               self->advertisements_.find(*advertisement_instance);
536           if (advertisement_iter == self->advertisements_.end()) {
537             if (result.is_ok()) {
538               bt_log(DEBUG,
539                      LOG_TAG,
540                      "releasing connection handle for canceled advertisement "
541                      "(peer: %s)",
542                      bt_str(result.value()->peer_identifier()));
543               result.value()->Release();
544             }
545             return;
546           }
547           advertisement_iter->second.OnConnected(advertisement_id,
548                                                  std::move(result));
549         };
550 
551     // Per the API contract of the `ConnectionOptions` FIDL, the bondable mode
552     // of the connection defaults to bondable mode unless the
553     // `connection_options` table exists and `bondable_mode` is explicitly set
554     // to false.
555     connectable_params->bondable_mode =
556         (!parameters.has_connection_options() ||
557          !parameters.connection_options().has_bondable_mode() ||
558          parameters.connection_options().bondable_mode())
559             ? BondableMode::Bondable
560             : BondableMode::NonBondable;
561   }
562 
563   bool extended_pdu = false;
564   if (parameters.has_advertising_procedure()) {
565     extended_pdu = parameters.advertising_procedure().is_extended();
566   }
567 
568   std::optional<bt::DeviceAddress::Type> address_type = std::nullopt;
569   if (parameters.has_address_type()) {
570     address_type =
571         fidl_helpers::FidlToDeviceAddressType(parameters.address_type());
572   }
573 
574   PW_CHECK(adapter()->le());
575   adapter()->le()->StartAdvertising(std::move(adv_data),
576                                     std::move(scan_rsp),
577                                     interval,
578                                     extended_pdu,
579                                     /*anonymous=*/false,
580                                     include_tx_power_level,
581                                     std::move(connectable_params),
582                                     address_type,
583                                     std::move(status_cb));
584 }
585 
LowEnergyPrivilegedPeripheralServer(const bt::gap::Adapter::WeakPtr & adapter,bt::gatt::GATT::WeakPtr gatt,fidl::InterfaceRequest<fuchsia::bluetooth::le::PrivilegedPeripheral> request)586 LowEnergyPrivilegedPeripheralServer::LowEnergyPrivilegedPeripheralServer(
587     const bt::gap::Adapter::WeakPtr& adapter,
588     bt::gatt::GATT::WeakPtr gatt,
589     fidl::InterfaceRequest<fuchsia::bluetooth::le::PrivilegedPeripheral>
590         request)
591     : AdapterServerBase(adapter, this, std::move(request)), weak_self_(this) {
592   fidl::InterfaceHandle<fuchsia::bluetooth::le::Peripheral> handle;
593   le_peripheral_server_ = std::make_unique<LowEnergyPeripheralServer>(
594       adapter, std::move(gatt), handle.NewRequest(), /*privileged=*/true);
595 }
596 
Advertise(fuchsia::bluetooth::le::AdvertisingParameters parameters,fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> advertised_peripheral,AdvertiseCallback callback)597 void LowEnergyPrivilegedPeripheralServer::Advertise(
598     fuchsia::bluetooth::le::AdvertisingParameters parameters,
599     fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral>
600         advertised_peripheral,
601     AdvertiseCallback callback) {
602   le_peripheral_server_->Advertise(std::move(parameters),
603                                    std::move(advertised_peripheral),
604                                    std::move(callback));
605 }
606 
StartAdvertising(fble::AdvertisingParameters parameters,::fidl::InterfaceRequest<fble::AdvertisingHandle> token,StartAdvertisingCallback callback)607 void LowEnergyPrivilegedPeripheralServer::StartAdvertising(
608     fble::AdvertisingParameters parameters,
609     ::fidl::InterfaceRequest<fble::AdvertisingHandle> token,
610     StartAdvertisingCallback callback) {
611   le_peripheral_server_->StartAdvertising(
612       std::move(parameters), std::move(token), std::move(callback));
613 }
614 
ListenL2cap(fble::ChannelListenerRegistryListenL2capRequest request,ListenL2capCallback callback)615 void LowEnergyPrivilegedPeripheralServer::ListenL2cap(
616     fble::ChannelListenerRegistryListenL2capRequest request,
617     ListenL2capCallback callback) {
618   le_peripheral_server_->ListenL2cap(std::move(request), std::move(callback));
619 }
620 
621 }  // namespace bthost
622