| // Copyright 2017 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 "low_energy_peripheral_fidl_impl.h" |
| |
| #include "garnet/drivers/bluetooth/lib/gap/advertising_data.h" |
| #include "garnet/drivers/bluetooth/lib/gap/remote_device.h" |
| #include "garnet/drivers/bluetooth/lib/hci/hci_constants.h" |
| #include "garnet/drivers/bluetooth/lib/hci/util.h" |
| #include "lib/fxl/functional/make_copyable.h" |
| #include "lib/fxl/logging.h" |
| |
| #include "app.h" |
| #include "fidl_helpers.h" |
| |
| // Make the FIDL namespace explicit. |
| namespace btfidl = ::bluetooth; |
| |
| namespace bluetooth_service { |
| |
| namespace { |
| |
| std::string ErrorToString(btlib::hci::Status error) { |
| switch (error) { |
| case ::btlib::hci::kSuccess: |
| return "Success"; |
| case ::btlib::hci::kConnectionLimitExceeded: |
| return "Maximum advertisement amount reached"; |
| case ::btlib::hci::kMemoryCapacityExceeded: |
| return "Advertisement exceeds maximum allowed length"; |
| default: |
| return ::btlib::hci::StatusToString(error); |
| } |
| } |
| |
| } // namespace |
| |
| LowEnergyPeripheralFidlImpl::LowEnergyPeripheralFidlImpl( |
| AdapterManager* adapter_manager, |
| ::fidl::InterfaceRequest<::btfidl::low_energy::Peripheral> request, |
| const ConnectionErrorHandler& connection_error_handler) |
| : adapter_manager_(adapter_manager), |
| binding_(this, std::move(request)), |
| weak_ptr_factory_(this) { |
| adapter_manager_->AddObserver(this); |
| binding_.set_connection_error_handler( |
| [this, connection_error_handler] { connection_error_handler(this); }); |
| } |
| |
| LowEnergyPeripheralFidlImpl::~LowEnergyPeripheralFidlImpl() { |
| adapter_manager_->RemoveObserver(this); |
| // Stop all the advertisements that this client has started |
| auto advertising_manager = GetAdvertisingManager(); |
| if (!advertising_manager) |
| return; |
| for (const auto& it : delegates_) { |
| advertising_manager->StopAdvertising(it.first); |
| } |
| } |
| |
| void LowEnergyPeripheralFidlImpl::StartAdvertising( |
| ::btfidl::low_energy::AdvertisingDataPtr advertising_data, |
| ::btfidl::low_energy::AdvertisingDataPtr scan_result, |
| ::fidl::InterfaceHandle<::btfidl::low_energy::PeripheralDelegate> delegate, |
| uint32_t interval, |
| bool anonymous, |
| const StartAdvertisingCallback& callback) { |
| auto advertising_manager = GetAdvertisingManager(); |
| if (!advertising_manager) { |
| auto fidlerror = fidl_helpers::NewErrorStatus( |
| ::btfidl::ErrorCode::BLUETOOTH_NOT_AVAILABLE, "Not available"); |
| callback(std::move(fidlerror), ""); |
| return; |
| } |
| |
| ::btlib::gap::AdvertisingData ad_data, scan_data; |
| ::btlib::gap::AdvertisingData::FromFidl(advertising_data, &ad_data); |
| if (scan_result) { |
| ::btlib::gap::AdvertisingData::FromFidl(scan_result, &scan_data); |
| } |
| |
| ::btlib::gap::LowEnergyAdvertisingManager::ConnectionCallback connect_cb; |
| if (delegate) { |
| connect_cb = |
| fbl::BindMember(this, &LowEnergyPeripheralFidlImpl::OnConnected); |
| } |
| // |delegate| is temporarily held by the result callback, which will close the |
| // delegate channel if the advertising fails (after returning the status) |
| auto advertising_result_cb = fxl::MakeCopyable( |
| [self = weak_ptr_factory_.GetWeakPtr(), callback, |
| delegate = std::move(delegate)](std::string advertisement_id, |
| ::btlib::hci::Status status) mutable { |
| if (!self) |
| return; |
| |
| if (status != ::btlib::hci::kSuccess) { |
| auto fidlerror = fidl_helpers::NewErrorStatus( |
| ::btfidl::ErrorCode::PROTOCOL_ERROR, ErrorToString(status)); |
| fidlerror->error->protocol_error_code = status; |
| callback(std::move(fidlerror), ""); |
| return; |
| } |
| |
| // This will be an unbound interface pointer if there's no delegate, but |
| // we keep it to track the current advertisements. |
| auto delegate_ptr = ::btfidl::low_energy::PeripheralDelegatePtr::Create( |
| std::move(delegate)); |
| self->delegates_[advertisement_id] = std::move(delegate_ptr); |
| callback(::btfidl::Status::New(), advertisement_id); |
| }); |
| |
| advertising_manager->StartAdvertising(ad_data, scan_data, connect_cb, |
| interval, anonymous, |
| advertising_result_cb); |
| } |
| |
| void LowEnergyPeripheralFidlImpl::StopAdvertising( |
| const ::fidl::String& advertisement_id, |
| const StopAdvertisingCallback& callback) { |
| delegates_.erase(advertisement_id); |
| auto advertising_manager = GetAdvertisingManager(); |
| if (!advertising_manager) { |
| callback(fidl_helpers::NewErrorStatus( |
| ::btfidl::ErrorCode::BLUETOOTH_NOT_AVAILABLE, "Not available")); |
| return; |
| } |
| advertising_manager->StopAdvertising(advertisement_id); |
| callback(::btfidl::Status::New()); |
| } |
| |
| void LowEnergyPeripheralFidlImpl::OnActiveAdapterChanged( |
| ::btlib::gap::Adapter* adapter) { |
| // TODO(jamuraa): re-add the advertisements that have been started here? |
| FXL_NOTIMPLEMENTED(); |
| } |
| |
| void LowEnergyPeripheralFidlImpl::OnConnected( |
| std::string advertisement_id, |
| ::btlib::gap::LowEnergyConnectionRefPtr connection) { |
| auto it = delegates_.find(advertisement_id); |
| if (it == delegates_.end() || !it->second) |
| return; |
| |
| auto* device = |
| adapter_manager_->GetActiveAdapter()->device_cache().FindDeviceById( |
| connection->device_identifier()); |
| |
| auto fidl_device = fidl_helpers::NewLERemoteDevice(*device); |
| it->second->OnCentralConnected(advertisement_id, std::move(fidl_device)); |
| } |
| |
| ::btlib::gap::LowEnergyAdvertisingManager* |
| LowEnergyPeripheralFidlImpl::GetAdvertisingManager() const { |
| auto adapter = adapter_manager_->GetActiveAdapter(); |
| if (!adapter) |
| return nullptr; |
| return adapter->le_advertising_manager(); |
| } |
| |
| } // namespace bluetooth_service |