910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// 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/containers/contains.h"
#include "base/functional/bind.h"
#include "base/notimplemented.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(std::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, dbus_utils::Variant>{})
          .second;
  DCHECK(inserted);
}

std::optional<dbus_utils::Variant> DbusProperties::DeleteProperty(
    const std::string& interface,
    const std::string& property_name,
    bool emit_signal) {
  auto interface_it = properties_.find(interface);
  if (interface_it == properties_.end()) {
    return std::nullopt;
  }
  auto& properties = interface_it->second;
  auto property_it = properties.find(property_name);
  if (property_it == properties.end()) {
    return std::nullopt;
  }
  auto value = std::move(property_it->second);
  properties.erase(property_it);
  if (emit_signal) {
    PropertyUpdated(interface, property_name, /*deleted=*/true);
  }
  return value;
}

void DbusProperties::PropertyUpdated(const std::string& interface,
                                     const std::string& property_name,
                                     bool deleted) {
  if (!initialized_) {
    return;
  }
  // org.freedesktop.DBus.Properties.PropertiesChanged(
  //     DICT<STRING,VARIANT> changed_properties,
  //     ARRAY<STRING> invalidated_properties);
  dbus::Signal signal(DBUS_INTERFACE_PROPERTIES, kSignalPropertiesChanged);
  dbus::MessageWriter writer(&signal);
  writer.AppendString(interface);

  // Changed properties.
  dbus::MessageWriter array_writer(nullptr);
  writer.OpenArray("{sv}", &array_writer);
  if (!deleted) {
    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.
  if (deleted) {
    writer.AppendArrayOfStrings({property_name});
  } else {
    writer.AppendArrayOfStrings({});
  }

  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.
    dbus::MessageWriter array_writer(nullptr);
    writer.OpenArray("{sv}", &array_writer);
    writer.CloseContainer(&array_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));
}