blob: 56e275f923282857b4d9ff615ac6cc3929482fa9 [file] [log] [blame]
// Copyright 2021 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_manager_client.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/task/single_thread_task_runner.h"
#include "dbus/bus.h"
#include "dbus/exported_object.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "device/bluetooth/bluez/bluez_features.h"
#include "device/bluetooth/chromeos_platform_features.h"
#include "device/bluetooth/floss/floss_dbus_client.h"
#include "device/bluetooth/floss/floss_features.h"
namespace floss {
using internal::AdapterWithEnabled;
template <>
bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader,
AdapterWithEnabled* adapter) {
static FlossDBusClient::StructReader<AdapterWithEnabled> struct_reader({
{"hci_interface", CreateFieldReader(&AdapterWithEnabled::adapter)},
{"enabled", CreateFieldReader(&AdapterWithEnabled::enabled)},
});
return struct_reader.ReadDBusParam(reader, adapter);
}
template <>
const DBusTypeInfo& GetDBusTypeInfo<AdapterWithEnabled>(
const AdapterWithEnabled* unused) {
static DBusTypeInfo info{"a{sv}", "AdapterWithEnabled"};
return info;
}
// static
const char FlossManagerClient::kExportedCallbacksPath[] =
"/org/chromium/bluetooth/managerclient";
// static
const char FlossManagerClient::kObjectManagerPath[] = "/";
// static
const int FlossManagerClient::kSetFlossRetryCount = 3;
// static
const int FlossManagerClient::kSetFlossRetryDelayMs = 500;
// static
const int FlossManagerClient::kSetFlossEnabledDBusTimeoutMs = 10000;
FlossManagerClient::FlossManagerClient() = default;
FlossManagerClient::~FlossManagerClient() {
if (object_manager_) {
object_manager_->UnregisterInterface(kManagerInterface);
}
if (bus_) {
bus_->UnregisterExportedObject(dbus::ObjectPath(kExportedCallbacksPath));
}
}
void FlossManagerClient::AddObserver(FlossManagerClient::Observer* observer) {
observers_.AddObserver(observer);
}
void FlossManagerClient::RemoveObserver(
FlossManagerClient::Observer* observer) {
observers_.RemoveObserver(observer);
}
std::vector<int> FlossManagerClient::GetAdapters() const {
std::vector<int> adapters;
for (auto& [adapter, enabled] : adapter_to_enabled_) {
adapters.push_back(adapter);
}
return adapters;
}
int FlossManagerClient::GetDefaultAdapter() const {
return default_adapter_;
}
bool FlossManagerClient::GetAdapterPresent(int adapter) const {
return base::Contains(adapter_to_enabled_, adapter);
}
bool FlossManagerClient::GetAdapterEnabled(int adapter) const {
auto iter = adapter_to_enabled_.find(adapter);
if (iter != adapter_to_enabled_.end()) {
return iter->second;
}
return false;
}
void FlossManagerClient::GetFlossEnabledWithTarget(bool target,
int retry,
int retry_wait_ms) {
CallManagerMethod<bool>(
base::BindOnce(&FlossManagerClient::HandleGetFlossEnabled,
weak_ptr_factory_.GetWeakPtr(), target, retry,
retry_wait_ms),
manager::kGetFlossEnabled);
}
void FlossManagerClient::SetFlossEnabled(
bool enabled,
int retry,
int retry_wait_ms,
absl::optional<ResponseCallback<bool>> cb) {
if (cb) {
set_floss_enabled_callback_ =
WeaklyOwnedResponseCallback<bool>::Create(std::move(*cb));
}
CallManagerMethod<Void>(
base::BindOnce(&FlossManagerClient::HandleSetFlossEnabled,
weak_ptr_factory_.GetWeakPtr(), enabled, retry,
retry_wait_ms),
manager::kSetFlossEnabled, enabled);
}
void FlossManagerClient::SetAdapterEnabled(int adapter,
bool enabled,
ResponseCallback<Void> callback) {
if (adapter != GetDefaultAdapter()) {
return;
}
DVLOG(1) << __func__;
adapter_enabled_callback_ =
WeaklyOwnedResponseCallback<Void>::CreateWithTimeout(
std::move(callback), kAdapterEnabledTimeoutMs,
base::unexpected(Error(kErrorNoResponse, "")));
const char* command = enabled ? manager::kStart : manager::kStop;
CallManagerMethod<Void>(
base::BindOnce(&FlossManagerClient::OnSetAdapterEnabled,
weak_ptr_factory_.GetWeakPtr()),
command, adapter);
}
void FlossManagerClient::OnSetAdapterEnabled(DBusResult<Void> response) {
// Only handle error cases since non-error called in OnHciEnabledChange
if (adapter_enabled_callback_ && !response.has_value()) {
adapter_enabled_callback_->Run(
base::unexpected(Error(kErrorNoResponse, "")));
adapter_enabled_callback_.reset();
}
}
void FlossManagerClient::SetLLPrivacy(ResponseCallback<Void> callback,
const bool enable) {
CallExperimentalMethod<Void>(std::move(callback), experimental::kSetLLPrivacy,
enable);
}
void FlossManagerClient::SetDevCoredump(ResponseCallback<Void> callback,
const bool enable) {
CallExperimentalMethod<Void>(std::move(callback),
experimental::kSetDevCoredump, enable);
}
// Register manager client against manager.
void FlossManagerClient::RegisterWithManager() {
DCHECK(!manager_available_);
// Get the default adapter.
CallManagerMethod<int>(
base::BindOnce(&FlossManagerClient::HandleGetDefaultAdapter,
weak_ptr_factory_.GetWeakPtr()),
manager::kGetDefaultAdapter);
// Get the list of available adapters.
CallManagerMethod<std::vector<AdapterWithEnabled>>(
base::BindOnce(&FlossManagerClient::HandleGetAvailableAdapters,
weak_ptr_factory_.GetWeakPtr()),
manager::kGetAvailableAdapters);
// Register for callbacks.
CallManagerMethod<Void>(
base::BindOnce(&FlossManagerClient::HandleRegisterCallback,
weak_ptr_factory_.GetWeakPtr()),
manager::kRegisterCallback, dbus::ObjectPath(kExportedCallbacksPath));
manager_available_ = true;
for (auto& observer : observers_) {
observer.ManagerPresent(manager_available_);
}
}
// Remove manager client (no longer available).
void FlossManagerClient::RemoveManager() {
// Make copy of old adapters and clear existing ones.
auto previous_adapters = std::move(adapter_to_enabled_);
adapter_to_enabled_.clear();
// All old adapters need to be sent a `present = false` notification.
for (auto& [adapter, enabled] : previous_adapters) {
for (auto& observer : observers_) {
observer.AdapterPresent(adapter, false);
}
}
manager_available_ = false;
for (auto& observer : observers_) {
observer.ManagerPresent(manager_available_);
}
}
// The manager can manage multiple adapters so ignore the adapter index given
// here. It is unused.
void FlossManagerClient::Init(dbus::Bus* bus,
const std::string& service_name,
const int adapter_index,
base::OnceClosure on_ready) {
bus_ = bus;
service_name_ = service_name;
// We should always have object proxy since the client initialization is
// gated on ObjectManager marking the manager interface as available.
if (!bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject))) {
LOG(ERROR) << "FlossManagerClient couldn't init. Object proxy was null.";
return;
}
DVLOG(1) << __func__;
exported_callback_manager_.Init(bus_.get());
exported_callback_manager_.AddMethod(
manager::kOnHciDeviceChanged,
&FlossManagerClientCallbacks::OnHciDeviceChanged);
exported_callback_manager_.AddMethod(
manager::kOnHciEnabledChanged,
&FlossManagerClientCallbacks::OnHciEnabledChanged);
exported_callback_manager_.AddMethod(
manager::kOnDefaultAdapterChanged,
&FlossManagerClientCallbacks::OnDefaultAdapterChanged);
if (!exported_callback_manager_.ExportCallback(
dbus::ObjectPath(kExportedCallbacksPath),
weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&FlossManagerClient::RegisterWithManager,
weak_ptr_factory_.GetWeakPtr()))) {
LOG(ERROR) << "Unable to successfully export FlossManagerClientCallbacks.";
return;
}
// Register object manager for Manager.
object_manager_ = bus_->GetObjectManager(
service_name, dbus::ObjectPath(kObjectManagerPath));
object_manager_->RegisterInterface(kManagerInterface, this);
// Enable Floss and retry a few times until it is set.
SetFlossEnabled(floss::features::IsFlossEnabled(), kSetFlossRetryCount,
kSetFlossRetryDelayMs,
base::BindOnce(&FlossManagerClient::CompleteSetFlossEnabled,
weak_ptr_factory_.GetWeakPtr()));
#if BUILDFLAG(IS_CHROMEOS)
SetDevCoredump(base::BindOnce([](DBusResult<Void> ret) {
if (!ret.has_value()) {
LOG(ERROR) << "Fail to set devcoredump.\n";
}
}),
base::FeatureList::IsEnabled(
chromeos::bluetooth::features::kBluetoothFlossCoredump));
#endif // BUILDFLAG(IS_CHROMEOS)
SetLLPrivacy(
base::BindOnce([](DBusResult<Void> ret) {
if (!ret.has_value())
LOG(ERROR) << "Fail to set LL privacy.\n";
}),
base::FeatureList::IsEnabled(bluez::features::kLinkLayerPrivacy));
on_ready_ = std::move(on_ready);
}
void FlossManagerClient::HandleGetDefaultAdapter(DBusResult<int32_t> response) {
if (!response.has_value()) {
LOG(ERROR) << "GetDefaultAdapter responded with error: "
<< response.error();
return;
}
OnDefaultAdapterChanged(response.value());
}
void FlossManagerClient::HandleGetAvailableAdapters(
DBusResult<std::vector<AdapterWithEnabled>> adapters) {
if (!adapters.has_value()) {
LOG(WARNING) << "GetAvailableAdapters return error " << adapters.error();
return;
}
auto previous_adapters = std::move(adapter_to_enabled_);
// Clear existing adapters.
adapter_to_enabled_.clear();
for (auto v : adapters.value()) {
adapter_to_enabled_.insert({v.adapter, v.enabled});
}
// Trigger the observers for adapter present on any new ones we listed.
for (auto& observer : observers_) {
// Emit present for new adapters that weren't in old list. Also emit the
// enabled changed for them.
for (auto& [adapter, enabled] : adapter_to_enabled_) {
if (!base::Contains(previous_adapters, adapter)) {
observer.AdapterPresent(adapter, true);
observer.AdapterEnabledChanged(adapter, enabled);
}
}
// Emit not present for adapters that aren't in new list.
// We don't need to emit AdapterEnabledChanged since we emit
// AdapterPresent is false
for (auto& [adapter, enabled] : previous_adapters) {
if (!base::Contains(adapter_to_enabled_, adapter)) {
observer.AdapterPresent(adapter, false);
}
}
}
}
void FlossManagerClient::HandleRegisterCallback(DBusResult<Void> result) {
if (!result.has_value()) {
LOG(ERROR) << "Floss manager RegisterCallback returned error: "
<< result.error();
return;
}
if (on_ready_) {
std::move(on_ready_).Run();
}
}
void FlossManagerClient::OnHciDeviceChanged(int32_t adapter, bool present) {
for (auto& observer : observers_) {
observer.AdapterPresent(adapter, present);
}
// Update the cached list of available adapters.
auto iter = adapter_to_enabled_.find(adapter);
if (present && iter == adapter_to_enabled_.end()) {
adapter_to_enabled_.insert({adapter, false});
} else if (!present && iter != adapter_to_enabled_.end()) {
adapter_to_enabled_.erase(iter);
}
}
void FlossManagerClient::OnHciEnabledChanged(int32_t adapter, bool enabled) {
if (adapter == GetDefaultAdapter() && adapter_enabled_callback_) {
adapter_enabled_callback_->Run(Void{});
adapter_enabled_callback_.reset();
}
adapter_to_enabled_[adapter] = enabled;
for (auto& observer : observers_) {
observer.AdapterEnabledChanged(adapter, enabled);
}
}
void FlossManagerClient::OnDefaultAdapterChanged(int32_t adapter) {
int32_t previous_default = default_adapter_;
default_adapter_ = adapter;
for (auto& observer : observers_) {
observer.DefaultAdapterChanged(previous_default, adapter);
}
}
void FlossManagerClient::HandleSetFlossEnabled(bool target,
int retry,
int retry_wait_ms,
DBusResult<Void> response) {
// Failed to call |SetFlossEnabled| so first log the error and post a delayed
// set if there are retries left.
if (!response.has_value()) {
LOG(ERROR) << response.error();
if (retry > 0) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FlossManagerClient::SetFlossEnabled,
weak_ptr_factory_.GetWeakPtr(), target, retry - 1,
retry_wait_ms, absl::nullopt),
base::Milliseconds(retry_wait_ms));
} else if (set_floss_enabled_callback_) {
set_floss_enabled_callback_->Run(base::unexpected(response.error()));
set_floss_enabled_callback_.reset();
}
return;
}
GetFlossEnabledWithTarget(target, retry, retry_wait_ms);
}
void FlossManagerClient::HandleGetFlossEnabled(bool target,
int retry,
int retry_wait_ms,
DBusResult<bool> response) {
if (!response.has_value()) {
LOG(ERROR) << response.error();
if (retry > 0) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FlossManagerClient::GetFlossEnabledWithTarget,
weak_ptr_factory_.GetWeakPtr(), target, retry - 1,
retry_wait_ms),
base::Milliseconds(retry_wait_ms));
} else if (set_floss_enabled_callback_) {
set_floss_enabled_callback_->Run(base::unexpected(response.error()));
set_floss_enabled_callback_.reset();
}
return;
}
bool floss_enabled = response.value();
// Target doesn't match reality. Retry |SetFlossEnabled|.
if (floss_enabled != target && retry > 0) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FlossManagerClient::SetFlossEnabled,
weak_ptr_factory_.GetWeakPtr(), target, retry - 1,
retry_wait_ms, absl::nullopt),
base::Milliseconds(kSetFlossRetryDelayMs));
} else {
DVLOG(1) << "Floss is currently "
<< (floss_enabled ? "enabled" : "disabled") << " and target was "
<< (target ? "enabled" : "disabled");
if (set_floss_enabled_callback_) {
set_floss_enabled_callback_->Run(floss_enabled);
set_floss_enabled_callback_.reset();
}
}
}
void FlossManagerClient::CompleteSetFlossEnabled(DBusResult<bool> ret) {
if (!ret.has_value()) {
LOG(ERROR) << "Floss couldn't be enabled. Error=" << ret.error();
} else {
DVLOG(1) << "Completed SetFlossEnabled with value " << *ret;
}
}
dbus::PropertySet* FlossManagerClient::CreateProperties(
dbus::ObjectProxy* object_proxy,
const dbus::ObjectPath& object_path,
const std::string& interface_name) {
return new dbus::PropertySet(object_proxy, interface_name, base::DoNothing());
}
// Manager interface is available.
void FlossManagerClient::ObjectAdded(const dbus::ObjectPath& object_path,
const std::string& interface_name) {
// TODO(b/193839304) - When manager exits, we're not getting the
// ObjectRemoved notification. So remove the manager
// before re-adding it here.
if (manager_available_) {
RemoveManager();
}
DVLOG(0) << __func__ << ": " << object_path.value() << ", " << interface_name;
RegisterWithManager();
}
// Manager interface is gone (no longer present).
void FlossManagerClient::ObjectRemoved(const dbus::ObjectPath& object_path,
const std::string& interface_name) {
if (!manager_available_)
return;
DVLOG(0) << __func__ << ": " << object_path.value() << ", " << interface_name;
RemoveManager();
}
// static
std::unique_ptr<FlossManagerClient> FlossManagerClient::Create() {
return std::make_unique<FlossManagerClient>();
}
} // namespace floss