| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #include "device/bluetooth/floss/floss_advertiser_client.h" |
| |
| #include "components/device_event_log/device_event_log.h" |
| #include "device/bluetooth/floss/floss_gatt_manager_client.h" |
| |
| namespace floss { |
| namespace { |
| constexpr char kConnectable[] = "connectable"; |
| constexpr char kScannable[] = "scannable"; |
| constexpr char kIsLegacy[] = "is_legacy"; |
| constexpr char kIsAnonymous[] = "is_anonymous"; |
| constexpr char kIncludeTxPower[] = "include_tx_power"; |
| constexpr char kPrimaryPhy[] = "primary_phy"; |
| constexpr char kSecondaryPhy[] = "secondary_phy"; |
| constexpr char kInterval[] = "interval"; |
| constexpr char kTxPowerLevel[] = "tx_power_level"; |
| constexpr char kOwnAddressType[] = "own_address_type"; |
| |
| constexpr char kServiceUuids[] = "service_uuids"; |
| constexpr char kSolicitUuids[] = "solicit_uuids"; |
| constexpr char kTransportDiscoveryData[] = "transport_discovery_data"; |
| constexpr char kManufacturerData[] = "manufacturer_data"; |
| constexpr char kServiceData[] = "service_data"; |
| constexpr char kIncludeTxPowerLevel[] = "include_tx_power_level"; |
| constexpr char kIncludeDeviceName[] = "include_device_name"; |
| } // namespace |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const OwnAddressType& type) { |
| int32_t value = static_cast<int32_t>(type); |
| WriteDBusParam(writer, value); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const AdvertisingSetParameters& params) { |
| dbus::MessageWriter array(nullptr); |
| |
| writer->OpenArray("{sv}", &array); |
| WriteDictEntry(&array, kConnectable, params.connectable); |
| WriteDictEntry(&array, kScannable, params.scannable); |
| WriteDictEntry(&array, kIsLegacy, params.is_legacy); |
| WriteDictEntry(&array, kIsAnonymous, params.is_anonymous); |
| WriteDictEntry(&array, kIncludeTxPower, params.include_tx_power); |
| WriteDictEntry(&array, kPrimaryPhy, params.primary_phy); |
| WriteDictEntry(&array, kSecondaryPhy, params.secondary_phy); |
| WriteDictEntry(&array, kInterval, params.interval); |
| WriteDictEntry(&array, kTxPowerLevel, params.tx_power_level); |
| WriteDictEntry(&array, kOwnAddressType, params.own_address_type); |
| writer->CloseContainer(&array); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam(dbus::MessageWriter* writer, |
| const AdvertiseData& data) { |
| dbus::MessageWriter array(nullptr); |
| |
| writer->OpenArray("{sv}", &array); |
| WriteDictEntry(&array, kServiceUuids, data.service_uuids); |
| WriteDictEntry(&array, kSolicitUuids, data.solicit_uuids); |
| WriteDictEntry(&array, kTransportDiscoveryData, |
| data.transport_discovery_data); |
| WriteDictEntry(&array, kManufacturerData, data.manufacturer_data); |
| WriteDictEntry(&array, kServiceData, data.service_data); |
| WriteDictEntry(&array, kIncludeTxPowerLevel, data.include_tx_power_level); |
| WriteDictEntry(&array, kIncludeDeviceName, data.include_device_name); |
| writer->CloseContainer(&array); |
| } |
| |
| template <> |
| void FlossDBusClient::WriteDBusParam( |
| dbus::MessageWriter* writer, |
| const PeriodicAdvertisingParameters& params) { |
| dbus::MessageWriter array(nullptr); |
| |
| WriteDictEntry(&array, kIncludeTxPowerLevel, params.include_tx_power_level); |
| WriteDictEntry(&array, kInterval, params.interval); |
| writer->CloseContainer(&array); |
| } |
| |
| template <> |
| bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader, |
| AdvertisingStatus* status) { |
| uint32_t value; |
| if (FlossDBusClient::ReadDBusParam(reader, &value)) { |
| *status = static_cast<AdvertisingStatus>(value); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const OwnAddressType*) { |
| static DBusTypeInfo info{"i", "OwnAddressType"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const AdvertisingSetParameters*) { |
| static DBusTypeInfo info{"a{sv}", "AdvertisingSetParameters"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const AdvertiseData*) { |
| static DBusTypeInfo info{"a{sv}", "AdvertiseData"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const PeriodicAdvertisingParameters*) { |
| static DBusTypeInfo info{"a{sv}", "PeriodicAdvertisingParameters"}; |
| return info; |
| } |
| |
| template <> |
| const DBusTypeInfo& GetDBusTypeInfo(const AdvertisingStatus*) { |
| static DBusTypeInfo info{"u", "AdvertisingStatus*"}; |
| return info; |
| } |
| |
| // static |
| std::unique_ptr<FlossAdvertiserClient> FlossAdvertiserClient::Create() { |
| return std::make_unique<FlossAdvertiserClient>(); |
| } |
| |
| AdvertiseData::AdvertiseData() = default; |
| AdvertiseData::AdvertiseData(const AdvertiseData&) = default; |
| AdvertiseData::~AdvertiseData() = default; |
| |
| FlossAdvertiserClient::FlossAdvertiserClient() = default; |
| |
| FlossAdvertiserClient::~FlossAdvertiserClient() { |
| if (bus_) { |
| exported_callback_manager_.UnexportCallback( |
| dbus::ObjectPath(kAdvertisingSetCallbackPath)); |
| } |
| } |
| |
| void FlossAdvertiserClient::Init(dbus::Bus* bus, |
| const std::string& service_name, |
| const int adapter_index) { |
| bus_ = bus; |
| service_name_ = service_name; |
| gatt_adapter_path_ = GenerateGattPath(adapter_index); |
| |
| dbus::ObjectProxy* object_proxy = |
| bus_->GetObjectProxy(service_name_, gatt_adapter_path_); |
| if (!object_proxy) { |
| LOG(ERROR) << "FlossAdvertiserClient couldn't init. Object proxy was null."; |
| return; |
| } |
| |
| exported_callback_manager_.Init(bus_.get()); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnAdvertisingSetStarted, |
| &FlossAdvertiserClientObserver::OnAdvertisingSetStarted); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnOwnAddressRead, |
| &FlossAdvertiserClientObserver::OnOwnAddressRead); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnAdvertisingSetStopped, |
| &FlossAdvertiserClientObserver::OnAdvertisingSetStopped); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnAdvertisingEnabled, |
| &FlossAdvertiserClientObserver::OnAdvertisingEnabled); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnAdvertisingDataSet, |
| &FlossAdvertiserClientObserver::OnAdvertisingDataSet); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnScanResponseDataSet, |
| &FlossAdvertiserClientObserver::OnScanResponseDataSet); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnAdvertisingParametersUpdated, |
| &FlossAdvertiserClientObserver::OnAdvertisingParametersUpdated); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnPeriodicAdvertisingParametersUpdated, |
| &FlossAdvertiserClientObserver::OnPeriodicAdvertisingParametersUpdated); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnPeriodicAdvertisingDataSet, |
| &FlossAdvertiserClientObserver::OnPeriodicAdvertisingDataSet); |
| exported_callback_manager_.AddMethod( |
| advertiser::kOnPeriodicAdvertisingEnabled, |
| &FlossAdvertiserClientObserver::OnPeriodicAdvertisingEnabled); |
| |
| if (!exported_callback_manager_.ExportCallback( |
| dbus::ObjectPath(kAdvertisingSetCallbackPath), |
| weak_ptr_factory_.GetWeakPtr())) { |
| LOG(ERROR) |
| << "Unable to successfully export FlossAdvertiserClientObserver."; |
| return; |
| } |
| |
| // Registering callbacks. We will get the callback id in |
| // |CompleteRegisterCallback| for later use. |
| dbus::MethodCall register_callback(kGattInterface, |
| advertiser::kRegisterCallback); |
| dbus::MessageWriter writer(®ister_callback); |
| writer.AppendObjectPath(dbus::ObjectPath(kAdvertisingSetCallbackPath)); |
| object_proxy->CallMethodWithErrorResponse( |
| ®ister_callback, kDBusTimeoutMs, |
| base::BindOnce(&FlossAdvertiserClient::CompleteRegisterCallback, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void FlossAdvertiserClient::AddObserver( |
| FlossAdvertiserClientObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void FlossAdvertiserClient::RemoveObserver( |
| FlossAdvertiserClientObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void FlossAdvertiserClient::StartAdvertisingSet( |
| const AdvertisingSetParameters& params, |
| const AdvertiseData& adv_data, |
| const absl::optional<AdvertiseData> scan_rsp, |
| const absl::optional<PeriodicAdvertisingParameters> periodic_params, |
| const absl::optional<AdvertiseData> periodic_data, |
| const int32_t duration, |
| const int32_t max_ext_adv_events, |
| StartSuccessCallback success_callback, |
| ErrorCallback error_callback) { |
| CallAdvertisingMethod( |
| base::BindOnce( |
| &FlossAdvertiserClient::CompleteStartAdvertisingSetCallback, |
| weak_ptr_factory_.GetWeakPtr(), std::move(success_callback), |
| std::move(error_callback)), |
| advertiser::kStartAdvertisingSet, params, adv_data, scan_rsp, |
| periodic_params, periodic_data, duration, max_ext_adv_events, |
| callback_id_); |
| } |
| |
| void FlossAdvertiserClient::StopAdvertisingSet( |
| const AdvertiserId adv_id, |
| StopSuccessCallback success_callback, |
| ErrorCallback error_callback) { |
| CallAdvertisingMethod( |
| base::BindOnce(&FlossAdvertiserClient::CompleteStopAdvertisingSetCallback, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(success_callback), std::move(error_callback), |
| adv_id), |
| advertiser::kStopAdvertisingSet, adv_id); |
| } |
| |
| void FlossAdvertiserClient::SetAdvertisingParameters( |
| const AdvertiserId adv_id, |
| const AdvertisingSetParameters& params, |
| SetAdvParamsSuccessCallback success_callback, |
| ErrorCallback error_callback) { |
| CallAdvertisingMethod( |
| base::BindOnce( |
| &FlossAdvertiserClient::CompleteSetAdvertisingParametersCallback, |
| weak_ptr_factory_.GetWeakPtr(), std::move(success_callback), |
| std::move(error_callback), adv_id), |
| advertiser::kSetAdvertisingParameters, adv_id, params); |
| } |
| |
| void FlossAdvertiserClient::CompleteRegisterCallback( |
| dbus::Response* response, |
| dbus::ErrorResponse* error_response) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": error_response=" << error_response; |
| if (error_response) { |
| FlossDBusClient::LogErrorResponse( |
| "AdvertisingManager::RegisterAdvertiserCallback", error_response); |
| } else { |
| dbus::MessageReader reader(response); |
| uint32_t result; |
| if (!reader.PopUint32(&result)) { |
| LOG(ERROR) << "No callback id provided for " |
| "AdvertisingManager::RegisterAdvertiserCallback"; |
| return; |
| } |
| |
| callback_id_ = result; |
| BLUETOOTH_LOG(EVENT) << __func__ << ": callback_id_ = " << callback_id_; |
| } |
| } |
| |
| void FlossAdvertiserClient::CompleteStartAdvertisingSetCallback( |
| StartSuccessCallback success_callback, |
| ErrorCallback error_callback, |
| DBusResult<RegId> ret) { |
| if (!ret.has_value()) { |
| LOG(ERROR) << "Error on StartAdvertisingSet: " << ret.error(); |
| std::move(error_callback) |
| .Run(device::BluetoothAdvertisement::ERROR_STARTING_ADVERTISEMENT); |
| return; |
| } |
| |
| RegId reg_id = *ret; |
| start_advertising_set_callbacks_.insert( |
| {reg_id, |
| std::make_pair(std::move(success_callback), std::move(error_callback))}); |
| } |
| |
| void FlossAdvertiserClient::CompleteStopAdvertisingSetCallback( |
| StopSuccessCallback success_callback, |
| ErrorCallback error_callback, |
| const AdvertiserId adv_id, |
| DBusResult<Void> ret) { |
| stop_advertising_set_callbacks_.insert( |
| {adv_id, |
| std::make_pair(std::move(success_callback), std::move(error_callback))}); |
| } |
| |
| void FlossAdvertiserClient::CompleteSetAdvertisingParametersCallback( |
| SetAdvParamsSuccessCallback success_callback, |
| ErrorCallback error_callback, |
| const AdvertiserId adv_id, |
| DBusResult<Void> ret) { |
| set_advertising_params_callbacks_.insert( |
| {adv_id, |
| std::make_pair(std::move(success_callback), std::move(error_callback))}); |
| } |
| |
| void FlossAdvertiserClient::OnAdvertisingSetStarted(RegId reg_id, |
| AdvertiserId adv_id, |
| int32_t tx_power, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": reg_id=" << reg_id |
| << ", adv_id=" << adv_id << ", tx_power=" << tx_power |
| << ", status=" << static_cast<uint32_t>(status); |
| |
| auto found = start_advertising_set_callbacks_.find(reg_id); |
| if (found != start_advertising_set_callbacks_.end()) { |
| auto& [success_callback, error_callback] = found->second; |
| if (status == AdvertisingStatus::kSuccess) { |
| std::move(success_callback).Run(adv_id); |
| } else { |
| std::move(error_callback).Run(GetErrorCode(status)); |
| } |
| start_advertising_set_callbacks_.erase(found); |
| } |
| } |
| |
| void FlossAdvertiserClient::OnOwnAddressRead(AdvertiserId adv_id, |
| int32_t address_type, |
| std::string address) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", address_type=" << address_type |
| << ", address=" << address; |
| } |
| |
| void FlossAdvertiserClient::OnAdvertisingSetStopped(AdvertiserId adv_id) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id; |
| |
| auto found = stop_advertising_set_callbacks_.find(adv_id); |
| if (found != stop_advertising_set_callbacks_.end()) { |
| auto& [success_callback, error_callback] = found->second; |
| std::move(success_callback).Run(); |
| stop_advertising_set_callbacks_.erase(found); |
| } |
| } |
| |
| void FlossAdvertiserClient::OnAdvertisingEnabled(AdvertiserId adv_id, |
| bool enable, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", enable=" << enable |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| void FlossAdvertiserClient::OnAdvertisingDataSet(AdvertiserId adv_id, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| void FlossAdvertiserClient::OnScanResponseDataSet(AdvertiserId adv_id, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| void FlossAdvertiserClient::OnAdvertisingParametersUpdated( |
| AdvertiserId adv_id, |
| int32_t tx_power, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", tx_power=" << tx_power |
| << ", status=" << static_cast<uint32_t>(status); |
| |
| auto found = set_advertising_params_callbacks_.find(adv_id); |
| if (found != set_advertising_params_callbacks_.end()) { |
| auto& [success_callback, error_callback] = found->second; |
| |
| if (status == AdvertisingStatus::kSuccess) { |
| std::move(success_callback).Run(); |
| } else { |
| std::move(error_callback).Run(GetErrorCode(status)); |
| } |
| set_advertising_params_callbacks_.erase(found); |
| } |
| } |
| |
| void FlossAdvertiserClient::OnPeriodicAdvertisingParametersUpdated( |
| AdvertiserId adv_id, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| void FlossAdvertiserClient::OnPeriodicAdvertisingDataSet( |
| AdvertiserId adv_id, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| void FlossAdvertiserClient::OnPeriodicAdvertisingEnabled( |
| AdvertiserId adv_id, |
| bool enable, |
| AdvertisingStatus status) { |
| BLUETOOTH_LOG(EVENT) << __func__ << ": adv_id=" << adv_id |
| << ", enable=" << enable |
| << ", status=" << static_cast<uint32_t>(status); |
| } |
| |
| device::BluetoothAdvertisement::ErrorCode FlossAdvertiserClient::GetErrorCode( |
| AdvertisingStatus status) { |
| switch (status) { |
| case AdvertisingStatus::kSuccess: |
| return device::BluetoothAdvertisement::INVALID_ADVERTISEMENT_ERROR_CODE; |
| case AdvertisingStatus::kDataTooLarge: |
| return device::BluetoothAdvertisement::ERROR_ADVERTISEMENT_INVALID_LENGTH; |
| case AdvertisingStatus::kAlreadyStarted: |
| return device::BluetoothAdvertisement::ERROR_ADVERTISEMENT_ALREADY_EXISTS; |
| default: |
| return device::BluetoothAdvertisement:: |
| ERROR_INVALID_ADVERTISEMENT_INTERVAL; |
| } |
| } |
| |
| } // namespace floss |