blob: ef5d2a15e6198ad830c4e4a81ad266164f22968a [file] [log] [blame]
// Copyright 2019 The Chromium 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 "components/dbus/properties/dbus_properties.h"
#include <dbus/dbus-shared.h>
#include "base/bind.h"
#include "base/stl_util.h"
#include "components/dbus/properties/success_barrier_callback.h"
#include "dbus/exported_object.h"
#include "dbus/message.h"
namespace {
// Methods.
const char kMethodPropertiesGetAll[] = "GetAll";
const char kMethodPropertiesGet[] = "Get";
const char kMethodPropertiesSet[] = "Set";
// Signals.
const char kSignalPropertiesChanged[] = "PropertiesChanged";
} // namespace
DbusProperties::DbusProperties(dbus::ExportedObject* exported_object,
InitializedCallback callback)
: exported_object_(exported_object) {
static constexpr struct {
const char* name;
void (DbusProperties::*callback)(dbus::MethodCall*,
dbus::ExportedObject::ResponseSender);
} methods[3] = {
{kMethodPropertiesGetAll, &DbusProperties::OnGetAllProperties},
{kMethodPropertiesGet, &DbusProperties::OnGetProperty},
{kMethodPropertiesSet, &DbusProperties::OnSetProperty},
};
barrier_ = SuccessBarrierCallback(base::size(methods), std::move(callback));
for (const auto& method : methods) {
exported_object_->ExportMethod(
DBUS_INTERFACE_PROPERTIES, method.name,
base::BindRepeating(method.callback, weak_factory_.GetWeakPtr()),
base::BindRepeating(&DbusProperties::OnExported,
weak_factory_.GetWeakPtr()));
}
}
DbusProperties::~DbusProperties() = default;
void DbusProperties::RegisterInterface(const std::string& interface) {
bool inserted =
properties_.emplace(interface, std::map<std::string, DbusVariant>{})
.second;
DCHECK(inserted);
}
DbusVariant* DbusProperties::GetProperty(const std::string& interface,
const std::string& property_name) {
auto interface_it = properties_.find(interface);
if (interface_it == properties_.end())
return nullptr;
auto name_it = interface_it->second.find(property_name);
return name_it != interface_it->second.end() ? &name_it->second : nullptr;
}
void DbusProperties::PropertyUpdated(const std::string& interface,
const std::string& property_name,
bool send_change) {
if (!initialized_)
return;
// |signal| follows the PropertiesChanged API:
// org.freedesktop.DBus.Properties.PropertiesChanged(
// STRING interface_name,
// DICT<STRING,VARIANT> changed_properties,
// ARRAY<STRING> invalidated_properties);
dbus::Signal signal(DBUS_INTERFACE_PROPERTIES, kSignalPropertiesChanged);
dbus::MessageWriter writer(&signal);
writer.AppendString(interface);
if (send_change) {
// Changed properties.
dbus::MessageWriter array_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
dbus::MessageWriter dict_entry_writer(nullptr);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(property_name);
properties_[interface][property_name].Write(&dict_entry_writer);
array_writer.CloseContainer(&dict_entry_writer);
writer.CloseContainer(&array_writer);
// Invalidated properties.
writer.AppendArrayOfStrings({});
} else {
// Changed properties.
DbusDictionary().Write(&writer);
// Invalidated properties.
writer.AppendArrayOfStrings({property_name});
}
exported_object_->SendSignal(&signal);
}
void DbusProperties::OnExported(const std::string& interface_name,
const std::string& method_name,
bool success) {
initialized_ = success;
barrier_.Run(success);
}
void DbusProperties::OnGetAllProperties(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
// org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
// out DICT<STRING,VARIANT> props);
dbus::MessageReader reader(method_call);
std::string interface;
if (!reader.PopString(&interface)) {
std::move(response_sender).Run(nullptr);
return;
}
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
if (base::Contains(properties_, interface)) {
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
for (const auto& pair : properties_[interface]) {
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString(pair.first);
pair.second.Write(&dict_entry_writer);
array_writer.CloseContainer(&dict_entry_writer);
}
writer.CloseContainer(&array_writer);
} else if (interface == DBUS_INTERFACE_PROPERTIES) {
// There are no properties to give for this interface.
DbusDictionary().Write(&writer);
} else {
// The given interface is not supported, so return a null response.
response = nullptr;
}
std::move(response_sender).Run(std::move(response));
}
void DbusProperties::OnGetProperty(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
// org.freedesktop.DBus.Properties.Get(in STRING interface_name,
// in STRING property_name,
// out VARIANT value);
dbus::MessageReader reader(method_call);
std::string interface;
std::string property_name;
if (!reader.PopString(&interface) || !reader.PopString(&property_name) ||
!base::Contains(properties_, interface) ||
!base::Contains(properties_[interface], property_name)) {
std::move(response_sender).Run(nullptr);
return;
}
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get());
properties_[interface][property_name].Write(&writer);
std::move(response_sender).Run(std::move(response));
}
void DbusProperties::OnSetProperty(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
// Not needed for now.
NOTIMPLEMENTED();
std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
}