blob: 6d2a488e5675017813c091ecc921531c13f982da [file] [log] [blame]
// Copyright 2016 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/dbus_wrapper.h"
#include <memory>
#include <utility>
#include <base/check.h>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/bus.h>
#include <dbus/message.h>
#include <google/protobuf/message_lite.h>
#include "power_manager/common/power_constants.h"
#include "power_manager/common/tracing.h"
namespace power_manager::system {
namespace {
// Handles the result of an attempt to connect to a D-Bus signal, logging an
// error on failure.
void HandleSignalConnected(const std::string& interface,
const std::string& signal,
bool success) {
if (!success)
LOG(ERROR) << "Failed to connect to signal " << interface << "." << signal;
}
// No-op
void OnPropertyChanged(const std::string& name) {}
} // namespace
DBusWrapper::DBusWrapper(scoped_refptr<dbus::Bus> bus,
dbus::ExportedObject* exported_object)
: bus_(bus), exported_object_(exported_object), weak_ptr_factory_(this) {
// Listen for NameOwnerChanged signals from the bus itself.
dbus::ObjectProxy* bus_proxy =
bus->GetObjectProxy(kBusServiceName, dbus::ObjectPath(kBusServicePath));
bus_proxy->ConnectToSignal(
kBusInterface, kBusNameOwnerChangedSignal,
base::BindRepeating(&DBusWrapper::HandleNameOwnerChangedSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&HandleSignalConnected));
}
DBusWrapper::~DBusWrapper() = default;
// static
std::unique_ptr<DBusWrapper> DBusWrapper::Create() {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
if (!bus->Connect()) {
LOG(ERROR) << "Failed to connect to system bus";
return nullptr;
}
dbus::ExportedObject* exported_object =
bus->GetExportedObject(dbus::ObjectPath(kPowerManagerServicePath));
if (!exported_object) {
LOG(ERROR) << "Failed to export " << kPowerManagerServicePath << " object";
return nullptr;
}
return base::WrapUnique(new DBusWrapper(bus, exported_object));
}
void DBusWrapper::AddObserver(Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void DBusWrapper::RemoveObserver(Observer* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
scoped_refptr<dbus::Bus> DBusWrapper::GetBus() {
return bus_;
}
dbus::ObjectProxy* DBusWrapper::GetObjectProxy(const std::string& service_name,
const std::string& object_path) {
return bus_->GetObjectProxy(service_name, dbus::ObjectPath(object_path));
}
void DBusWrapper::RegisterForServiceAvailability(
dbus::ObjectProxy* proxy,
dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) {
DCHECK(proxy);
proxy->WaitForServiceToBeAvailable(std::move(callback));
}
void DBusWrapper::RegisterForSignal(
dbus::ObjectProxy* proxy,
const std::string& interface_name,
const std::string& signal_name,
dbus::ObjectProxy::SignalCallback callback) {
DCHECK(proxy);
proxy->ConnectToSignal(interface_name, signal_name, callback,
base::BindOnce(&HandleSignalConnected));
}
void DBusWrapper::ExportMethod(
const std::string& method_name,
const std::string& interface_name,
dbus::ExportedObject::MethodCallCallback callback) {
// Annotate the method handler with a trace event.
callback = base::BindRepeating(
[](const std::string& method_name,
dbus::ExportedObject::MethodCallCallback callback,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender sender) {
TRACE_EVENT("power", perfetto::DynamicString{method_name});
callback.Run(method_call, std::move(sender));
},
method_name, std::move(callback));
CHECK(exported_object_->ExportMethodAndBlock(interface_name, method_name,
callback));
}
void DBusWrapper::ExportMethod(
const std::string& method_name,
dbus::ExportedObject::MethodCallCallback callback) {
ExportMethod(method_name, kPowerManagerInterface, callback);
}
bool DBusWrapper::PublishService() {
// Publish the service.
bool success = bus_->RequestOwnershipAndBlock(kPowerManagerServiceName,
dbus::Bus::REQUIRE_PRIMARY);
if (!success) {
return false;
}
// Notify our observers.
for (DBusWrapper::Observer& observer : observers_) {
observer.OnServicePublished();
}
return true;
}
void DBusWrapper::EmitSignal(dbus::Signal* signal) {
DCHECK(exported_object_);
DCHECK(signal);
TRACE_EVENT("power", "DBusWrapper::EmitSignal", "signal", signal->ToString());
exported_object_->SendSignal(signal);
}
void DBusWrapper::EmitBareSignal(const std::string& signal_name) {
dbus::Signal signal(kPowerManagerInterface, signal_name);
EmitSignal(&signal);
}
void DBusWrapper::EmitSignalWithProtocolBuffer(
const std::string& signal_name,
const google::protobuf::MessageLite& protobuf) {
dbus::Signal signal(kPowerManagerInterface, signal_name);
dbus::MessageWriter writer(&signal);
writer.AppendProtoAsArrayOfBytes(protobuf);
EmitSignal(&signal);
}
std::unique_ptr<dbus::Response> DBusWrapper::CallMethodSync(
dbus::ObjectProxy* proxy,
dbus::MethodCall* method_call,
base::TimeDelta timeout) {
TRACE_EVENT("power", "DBusWrapper::CallMethodSync", "method_call",
method_call->ToString(), "timeout_ms", timeout.InMilliseconds());
DCHECK(proxy);
DCHECK(method_call);
base::expected<std::unique_ptr<dbus::Response>, dbus::Error> response(
proxy->CallMethodAndBlock(method_call, timeout.InMilliseconds()));
return std::move(response).value_or(nullptr);
}
void DBusWrapper::CallMethodAsync(
dbus::ObjectProxy* proxy,
dbus::MethodCall* method_call,
base::TimeDelta timeout,
dbus::ObjectProxy::ResponseCallback callback) {
DCHECK(proxy);
DCHECK(method_call);
uint64_t trace_id = next_async_trace_id_++;
TRACE_EVENT("power", perfetto::DynamicString{method_call->ToString()},
perfetto::Flow::ProcessScoped(trace_id));
// Annotate the response with a trace event that is connected to the original
// method call.
callback = base::BindOnce(
[](uint64_t trace_id, dbus::ObjectProxy::ResponseCallback callback,
dbus::Response* response) {
TRACE_EVENT("power", "DBusWrapper::AsyncMethodResponse",
perfetto::Flow::ProcessScoped(trace_id));
std::move(callback).Run(response);
},
trace_id, std::move(callback));
proxy->CallMethod(method_call, timeout.InMilliseconds(), std::move(callback));
}
void DBusWrapper::HandleNameOwnerChangedSignal(dbus::Signal* signal) {
DCHECK(signal);
TRACE_EVENT("power", "DBusWrapper::HandleNameOwnerChangedSignal", "signal",
signal->ToString());
dbus::MessageReader reader(signal);
std::string name, old_owner, new_owner;
if (!reader.PopString(&name) || !reader.PopString(&old_owner) ||
!reader.PopString(&new_owner)) {
LOG(ERROR) << "Unable to parse " << kBusNameOwnerChangedSignal << " signal";
return;
}
for (DBusWrapper::Observer& observer : observers_)
observer.OnDBusNameOwnerChanged(name, old_owner, new_owner);
}
void DBusWrapper::RegisterForInterfaceAvailability(
dbus::ObjectManager* object_manager,
const std::string& interface_name,
InterfaceCallback callback) {
interface_callback_ = callback;
object_manager->RegisterInterface(interface_name, this);
}
void DBusWrapper::ObjectAdded(const dbus::ObjectPath& object_path,
const std::string& interface_name) {
if (!interface_callback_)
return;
std::move(interface_callback_).Run(interface_name, true);
}
void DBusWrapper::ObjectRemoved(const dbus::ObjectPath& object_path,
const std::string& interface_name) {
if (!interface_callback_)
return;
std::move(interface_callback_).Run(interface_name, false);
}
// No-op
dbus::PropertySet* DBusWrapper::CreateProperties(
dbus::ObjectProxy* object_proxy,
const dbus::ObjectPath& object_path,
const std::string& interface_name) {
return new dbus::PropertySet(object_proxy, interface_name,
base::BindRepeating(&OnPropertyChanged));
}
} // namespace power_manager::system