chromeos-dbus-bindings: Add support for ObjectManager/Properties
When generating D-Bus object proxy classes, add support for D-Bus
Object Manager proxy and properties on D-Bus objects.
BUG=chromium:431737
TEST=FEATURES=test emerge-link chromeos-dbus-bindings
Change-Id: I4d399fc5ed9613e7c51a7b489383fada184e498f
Reviewed-on: https://chromium-review.googlesource.com/232532
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
index 946156b..a4d108e 100644
--- a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
+++ b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
@@ -84,6 +84,18 @@
return false;
dict->GetStringWithoutPathExpansion("service_name", &config->service_name);
+
+ base::DictionaryValue* om_dict = nullptr; // Owned by |dict|.
+ if (dict->GetDictionaryWithoutPathExpansion("object_manager", &om_dict)) {
+ if (!om_dict->GetStringWithoutPathExpansion("name",
+ &config->object_manager.name) &&
+ !config->service_name.empty()) {
+ config->object_manager.name = config->service_name + ".ObjectManager";
+ }
+ om_dict->GetStringWithoutPathExpansion("object_path",
+ &config->object_manager.object_path);
+ }
+
return true;
}
diff --git a/chromeos-dbus-bindings/header_generator.h b/chromeos-dbus-bindings/header_generator.h
index 55b1265..1be7d34 100644
--- a/chromeos-dbus-bindings/header_generator.h
+++ b/chromeos-dbus-bindings/header_generator.h
@@ -21,8 +21,25 @@
struct Interface;
class IndentedText;
+// General D-Bus service configuration settings used by Adaptor/Proxy code
+// generators.
struct ServiceConfig {
+ // D-Bus service name to be used when constructing proxy objects.
+ // If omitted (empty), the service name parameter will be added to the
+ // constructor of generated proxy class(es).
std::string service_name;
+ // Object Manager settings.
+ struct {
+ // The name of the Object Manager class to use. If empty, no object manager
+ // is generated in the proxy code (this also disables property support on
+ // proxy objects).
+ // This is a "fake" name used to generate namespaces and the actual class
+ // name for the object manager proxy. This name has no relationship to the
+ // actual D-Bus properties of the actual object manager.
+ std::string name;
+ // The D-Bus path to Object Manager instance.
+ std::string object_path;
+ } object_manager;
};
class HeaderGenerator {
diff --git a/chromeos-dbus-bindings/indented_text.cc b/chromeos-dbus-bindings/indented_text.cc
index 5a8ed6f..a261fb4 100644
--- a/chromeos-dbus-bindings/indented_text.cc
+++ b/chromeos-dbus-bindings/indented_text.cc
@@ -41,6 +41,20 @@
contents_.emplace_back(line, shift + offset_);
}
+void IndentedText::AddLineAndPushOffsetTo(const std::string& line,
+ size_t occurrence,
+ char c) {
+ AddLine(line);
+ size_t pos = 0;
+ while (occurrence > 0) {
+ pos = line.find(c, pos);
+ CHECK(pos != string::npos);
+ pos++;
+ occurrence--;
+ }
+ PushOffset(pos);
+}
+
void IndentedText::AddComments(const std::string& doc_string) {
// Try to retain indentation in the comments. Find the first non-empty line
// of the comment and find its whitespace indentation prefix.
diff --git a/chromeos-dbus-bindings/indented_text.h b/chromeos-dbus-bindings/indented_text.h
index 701c2e6..42fd744 100644
--- a/chromeos-dbus-bindings/indented_text.h
+++ b/chromeos-dbus-bindings/indented_text.h
@@ -28,6 +28,12 @@
// Add a line at the current indentation.
void AddLine(const std::string& line);
void AddLineWithOffset(const std::string& line, size_t shift);
+ // Adds a line and pushes an offset past the |nth_occurrence| of character |c|
+ // in that line, effectively allowing to align following line to the position
+ // following that character.
+ void AddLineAndPushOffsetTo(const std::string& line,
+ size_t nth_occurrence,
+ char c);
// Adds a block of comments.
void AddComments(const std::string& doc_string);
diff --git a/chromeos-dbus-bindings/indented_text_unittest.cc b/chromeos-dbus-bindings/indented_text_unittest.cc
index d615770..2bb0e1e 100644
--- a/chromeos-dbus-bindings/indented_text_unittest.cc
+++ b/chromeos-dbus-bindings/indented_text_unittest.cc
@@ -52,6 +52,11 @@
EXPECT_EQ(string(kShift, ' ') + kTestString + "\n", text_.GetContents());
}
+TEST_F(IndentedTextTest, AddLineAndPushOffsetTo) {
+ text_.AddLineAndPushOffsetTo("foo(bar(baz", 2, '(');
+ EXPECT_THAT(GetHistory(), ElementsAre(8));
+}
+
TEST_F(IndentedTextTest, AddBlock) {
IndentedText block0;
const char kTestString[] = "test";
diff --git a/chromeos-dbus-bindings/proxy_generator.cc b/chromeos-dbus-bindings/proxy_generator.cc
index 1c09f33..96f2bc8 100644
--- a/chromeos-dbus-bindings/proxy_generator.cc
+++ b/chromeos-dbus-bindings/proxy_generator.cc
@@ -22,9 +22,20 @@
namespace chromeos_dbus_bindings {
namespace {
-string GetParamString(const pair<string, string>& param_def) {
- return StringPrintf("const %s& %s",
- param_def.first.c_str(), param_def.second.c_str());
+// Helper struct to encapsulate information about method call parameter during
+// code generation.
+struct ParamDef {
+ ParamDef(const string& param_type, const string& param_name, bool param_ref)
+ : type(param_type), name(param_name), is_const_ref(param_ref) {}
+
+ string type;
+ string name;
+ bool is_const_ref;
+};
+
+string GetParamString(const ParamDef& param_def) {
+ return StringPrintf(param_def.is_const_ref ? "const %s& %s" : "%s* %s",
+ param_def.type.c_str(), param_def.name.c_str());
}
} // anonymous namespace
@@ -42,6 +53,7 @@
string header_guard = GenerateHeaderGuard(output_file);
text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
+ text.AddLine("#include <memory>");
text.AddLine("#include <string>");
text.AddLine("#include <vector>");
text.AddBlankLine();
@@ -52,19 +64,33 @@
text.AddLine("#include <base/memory/ref_counted.h>");
text.AddLine("#include <chromeos/any.h>");
text.AddLine("#include <chromeos/dbus/dbus_method_invoker.h>");
+ text.AddLine("#include <chromeos/dbus/dbus_property.h>");
text.AddLine("#include <chromeos/dbus/dbus_signal_handler.h>");
text.AddLine("#include <chromeos/errors/error.h>");
text.AddLine("#include <chromeos/variant_dictionary.h>");
text.AddLine("#include <dbus/bus.h>");
text.AddLine("#include <dbus/message.h>");
+ text.AddLine("#include <dbus/object_manager.h>");
text.AddLine("#include <dbus/object_path.h>");
text.AddLine("#include <dbus/object_proxy.h>");
text.AddBlankLine();
+ if (!config.object_manager.name.empty()) {
+ // Add forward-declaration for Object Manager proxy class.
+ NameParser parser{config.object_manager.name};
+ parser.AddOpenNamespaces(&text, false);
+ text.AddLine(StringPrintf("class %s;",
+ parser.MakeProxyName(false).c_str()));
+ parser.AddCloseNamespaces(&text, false);
+ text.AddBlankLine();
+ }
+
for (const auto& interface : interfaces) {
GenerateInterfaceProxy(config, interface, &text);
}
+ ObjectManager::GenerateProxy(config, interfaces, &text);
+
text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
return WriteTextToFile(output_file, text);
}
@@ -86,19 +112,27 @@
text->AddLineWithOffset("public:", kScopeOffset);
text->PushOffset(kBlockOffset);
AddSignalReceiver(interface, text);
+ AddPropertySet(config, interface, text);
AddConstructor(config, interface, proxy_name, text);
AddDestructor(proxy_name, text);
AddReleaseObjectProxy(text);
+ AddGetObjectPath(text);
+ AddGetObjectProxy(text);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ AddPropertyPublicMethods(proxy_name, text);
if (!interface.signals.empty())
AddSignalConnectedCallback(text);
for (const auto& method : interface.methods) {
AddMethodProxy(method, interface.name, text);
}
+ AddProperties(config, interface, text);
text->PopOffset();
text->AddLineWithOffset("private:", kScopeOffset);
text->PushOffset(kBlockOffset);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ AddOnPropertyChanged(text);
text->AddLine("scoped_refptr<dbus::Bus> bus_;");
if (config.service_name.empty()) {
text->AddLine("std::string service_name_;");
@@ -112,11 +146,22 @@
text->AddLine(StringPrintf("const dbus::ObjectPath object_path_{\"%s\"};",
interface.path.c_str()));
}
+ if (!config.object_manager.name.empty() && !interface.properties.empty()) {
+ text->AddLine("PropertySet* property_set_;");
+ text->AddLine(StringPrintf("base::Callback<void(%s*, const std::string&)> "
+ "on_property_changed_;",
+ proxy_name.c_str()));
+ }
text->AddLine("dbus::ObjectProxy* dbus_object_proxy_;");
text->AddBlankLine();
- text->AddLine(StringPrintf(
- "DISALLOW_COPY_AND_ASSIGN(%s);", proxy_name.c_str()));
+ if (!config.object_manager.name.empty() && !interface.properties.empty()) {
+ text->AddLine(StringPrintf(
+ "friend class %s;",
+ NameParser{config.object_manager.name}.MakeProxyName(true).c_str()));
+ }
+ text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ proxy_name.c_str()));
text->PopOffset();
text->AddLine("};");
@@ -133,11 +178,13 @@
const string& class_name,
IndentedText* text) {
IndentedText block;
- vector<std::pair<string, string>> args{{"scoped_refptr<dbus::Bus>", "bus"}};
+ vector<ParamDef> args{{"scoped_refptr<dbus::Bus>", "bus", true}};
if (config.service_name.empty())
- args.emplace_back("std::string", "service_name");
+ args.emplace_back("std::string", "service_name", true);
if (interface.path.empty())
- args.emplace_back("std::string", "object_path");
+ args.emplace_back("dbus::ObjectPath", "object_path", true);
+ if (!config.object_manager.name.empty() && !interface.properties.empty())
+ args.emplace_back("PropertySet", "property_set", false);
if (args.size() == 1) {
block.AddLine(StringPrintf("%s(%s) :", class_name.c_str(),
@@ -152,12 +199,12 @@
}
block.PushOffset(kLineContinuationOffset);
for (const auto& arg : args) {
- block.AddLine(StringPrintf("%s_(%s),", arg.second.c_str(),
- arg.second.c_str()));
+ block.AddLine(StringPrintf("%s_{%s},", arg.name.c_str(),
+ arg.name.c_str()));
}
- block.AddLine("dbus_object_proxy_(");
+ block.AddLine("dbus_object_proxy_{");
block.AddLineWithOffset(
- "bus_->GetObjectProxy(service_name_, object_path_)) {",
+ "bus_->GetObjectProxy(service_name_, object_path_)} {",
kLineContinuationOffset);
block.PopOffset();
if (args.size() > 1)
@@ -170,7 +217,7 @@
vector<string> param_names;
for (const auto& arg : args) {
block.AddLine(StringPrintf("%s,", GetParamString(arg).c_str()));
- param_names.push_back(arg.second);
+ param_names.push_back(arg.name);
}
block.AddLine("SignalReceiver* signal_receiver) :");
string param_list = chromeos::string_utils::Join(", ", param_names);
@@ -220,15 +267,58 @@
// static
void ProxyGenerator::AddReleaseObjectProxy(IndentedText* text) {
- IndentedText block;
- block.AddLine("void ReleaseObjectProxy(const base::Closure& callback) {");
- block.PushOffset(kBlockOffset);
- block.AddLine(
- "bus_->RemoveObjectProxy(service_name_, object_path_, callback);");
- block.PopOffset();
- block.AddLine("}");
- block.AddBlankLine();
- text->AddBlock(block);
+ text->AddLine("void ReleaseObjectProxy(const base::Closure& callback) {");
+ text->AddLineWithOffset(
+ "bus_->RemoveObjectProxy(service_name_, object_path_, callback);",
+ kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::AddGetObjectPath(IndentedText* text) {
+ text->AddLine("const dbus::ObjectPath& GetObjectPath() const {");
+ text->AddLineWithOffset("return object_path_;", kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::AddGetObjectProxy(IndentedText* text) {
+ text->AddLine("dbus::ObjectProxy* GetObjectProxy() const { "
+ "return dbus_object_proxy_; }");
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::AddPropertyPublicMethods(const string& class_name,
+ IndentedText* text) {
+ text->AddLine("void SetPropertyChangedCallback(");
+ text->AddLineWithOffset(
+ StringPrintf("const base::Callback<void(%s*, "
+ "const std::string&)>& callback) {", class_name.c_str()),
+ kLineContinuationOffset);
+ text->AddLineWithOffset("on_property_changed_ = callback;", kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+
+ text->AddLine("const PropertySet* GetProperties() const "
+ "{ return property_set_; }");
+ text->AddLine("PropertySet* GetProperties() { return property_set_; }");
+ text->AddBlankLine();
+}
+
+// static
+void ProxyGenerator::AddOnPropertyChanged(IndentedText* text) {
+ text->AddLine("void OnPropertyChanged(const std::string& property_name) {");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("if (!on_property_changed_.is_null())");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("on_property_changed_.Run(this, property_name);");
+ text->PopOffset();
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
}
// static
@@ -304,6 +394,86 @@
}
// static
+void ProxyGenerator::AddPropertySet(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ // Must have ObjectManager in order for property system to work correctly.
+ if (config.object_manager.name.empty())
+ return;
+
+ IndentedText block;
+ block.AddLine("class PropertySet : public dbus::PropertySet {");
+ block.AddLineWithOffset("public:", kScopeOffset);
+ block.PushOffset(kBlockOffset);
+ block.AddLineAndPushOffsetTo("PropertySet(dbus::ObjectProxy* object_proxy,",
+ 1, '(');
+ block.AddLine("const PropertyChangedCallback& callback)");
+ block.PopOffset();
+ block.PushOffset(kLineContinuationOffset);
+ block.AddLineAndPushOffsetTo(": dbus::PropertySet{object_proxy,", 1, '{');
+ block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
+ block.AddLine("callback} {");
+ block.PopOffset();
+ block.PopOffset();
+ block.PushOffset(kBlockOffset);
+ for (const auto& prop : interface.properties) {
+ block.AddLine(
+ StringPrintf("RegisterProperty(\"%s\", &%s);",
+ prop.name.c_str(),
+ NameParser{prop.name}.MakeVariableName().c_str()));
+ }
+ block.PopOffset();
+ block.AddLine("}");
+ block.AddBlankLine();
+
+ DbusSignature signature;
+ for (const auto& prop : interface.properties) {
+ string type;
+ CHECK(signature.Parse(prop.type, &type));
+ block.AddLine(
+ StringPrintf("chromeos::dbus_utils::Property<%s> %s;",
+ type.c_str(),
+ NameParser{prop.name}.MakeVariableName().c_str()));
+ }
+ block.AddBlankLine();
+
+ block.PopOffset();
+ block.AddLineWithOffset("private:", kScopeOffset);
+ block.AddLineWithOffset("DISALLOW_COPY_AND_ASSIGN(PropertySet);",
+ kBlockOffset);
+ block.AddLine("};");
+ block.AddBlankLine();
+
+ text->AddBlock(block);
+}
+
+// static
+void ProxyGenerator::AddProperties(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text) {
+ // Must have ObjectManager in order for property system to work correctly.
+ if (config.object_manager.name.empty())
+ return;
+
+ DbusSignature signature;
+ for (const auto& prop : interface.properties) {
+ string type;
+ CHECK(signature.Parse(prop.type, &type));
+ MakeConstReferenceIfNeeded(&type);
+ string name = NameParser{prop.name}.MakeVariableName();
+ text->AddLine(
+ StringPrintf("%s %s() const {",
+ type.c_str(),
+ name.c_str()));
+ text->AddLineWithOffset(
+ StringPrintf("return property_set_->%s.value();", name.c_str()),
+ kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+ }
+}
+
+// static
void ProxyGenerator::AddMethodProxy(const Interface::Method& method,
const string& interface_name,
IndentedText* text) {
@@ -362,6 +532,369 @@
}
// static
+void ProxyGenerator::ObjectManager::GenerateProxy(
+ const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ if (config.object_manager.name.empty())
+ return;
+
+ NameParser object_manager{config.object_manager.name};
+ object_manager.AddOpenNamespaces(text, false);
+ text->AddBlankLine();
+
+ string class_name = object_manager.type_name + "Proxy";
+ text->AddLine(StringPrintf("class %s : "
+ "public dbus::ObjectManager::Interface {",
+ class_name.c_str()));
+ text->AddLineWithOffset("public:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+
+ AddConstructor(config, class_name, interfaces, text);
+ AddGetObjectManagerProxy(text);
+ for (const auto& itf : interfaces) {
+ AddInterfaceAccessors(itf, text);
+ }
+ text->PopOffset();
+
+ text->AddLineWithOffset("private:", kScopeOffset);
+ text->PushOffset(kBlockOffset);
+ AddOnPropertyChanged(interfaces, text);
+ AddObjectAdded(interfaces, text);
+ AddObjectRemoved(interfaces, text);
+ AddCreateProperties(interfaces, class_name, text);
+ AddDataMembers(interfaces, class_name, text);
+
+ text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+ class_name.c_str()));
+ text->PopOffset();
+ text->AddLine("};");
+ text->AddBlankLine();
+ object_manager.AddCloseNamespaces(text, false);
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddConstructor(
+ const ServiceConfig& config,
+ const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ if (config.service_name.empty()) {
+ text->AddLineAndPushOffsetTo(
+ StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus,",
+ class_name.c_str()),
+ 1, '(');
+ text->AddLine("const std::string& service_name)");
+ text->PopOffset();
+ } else {
+ text->AddLine(StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus)",
+ class_name.c_str()));
+ }
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(": bus_{bus},");
+ text->PushOffset(kBlockOffset);
+ text->AddLine("dbus_object_manager_{bus->GetObjectManager(");
+ text->PushOffset(kLineContinuationOffset);
+ if (config.service_name.empty()) {
+ text->AddLine("service_name,");
+ } else {
+ text->AddLine(StringPrintf("\"%s\",", config.service_name.c_str()));
+ }
+ text->AddLine(StringPrintf("dbus::ObjectPath{\"%s\"})} {",
+ config.object_manager.object_path.c_str()));
+ text->PopOffset();
+ text->PopOffset();
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ text->AddLine(
+ StringPrintf("dbus_object_manager_->RegisterInterface(\"%s\", this);",
+ itf.name.c_str()));
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddGetObjectManagerProxy(
+ IndentedText* text) {
+ text->AddLine("dbus::ObjectManager* GetObjectManagerProxy() const {");
+ text->AddLineWithOffset("return dbus_object_manager_;", kBlockOffset);
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddInterfaceAccessors(
+ const Interface& interface,
+ IndentedText* text) {
+ NameParser itf_name{interface.name};
+ string map_name = itf_name.MakeVariableName() + "_instances_";
+
+ // GetProxy().
+ text->AddLine(StringPrintf("%s* Get%s(",
+ itf_name.MakeProxyName(true).c_str(),
+ itf_name.MakeProxyName(false).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("auto p = %s.find(object_path);",
+ map_name.c_str()));
+ text->AddLine(StringPrintf("if (p != %s.end())", map_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine("return p->second.get();");
+ text->PopOffset();
+ text->AddLine("return nullptr;");
+ text->PopOffset();
+ text->AddLine("}");
+
+ // GetInstances().
+ text->AddLine(StringPrintf("std::vector<%s*> Get%sInstances() const {",
+ itf_name.MakeProxyName(true).c_str(),
+ itf_name.type_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("std::vector<%s*> values;",
+ itf_name.MakeProxyName(true).c_str()));
+ text->AddLine(StringPrintf("values.reserve(%s.size());", map_name.c_str()));
+ text->AddLine(StringPrintf("for (const auto& pair : %s)", map_name.c_str()));
+ text->AddLineWithOffset("values.push_back(pair.second.get());", kBlockOffset);
+ text->AddLine("return values;");
+ text->PopOffset();
+ text->AddLine("}");
+
+ // SetAddedCallback().
+ text->AddLine(StringPrintf("void Set%sAddedCallback(",
+ itf_name.type_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(
+ StringPrintf("const base::Callback<void(%s*)>& callback) {",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_added_ = callback;",
+ itf_name.MakeVariableName().c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+
+ // SetRemovedCallback().
+ text->AddLine(StringPrintf("void Set%sRemovedCallback(",
+ itf_name.type_name.c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const base::Callback<void(const dbus::ObjectPath&)>& "
+ "callback) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_removed_ = callback;",
+ itf_name.MakeVariableName().c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddOnPropertyChanged(
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLineAndPushOffsetTo("void OnPropertyChanged("
+ "const dbus::ObjectPath& object_path,",
+ 1, '(');
+ text->AddLine("const std::string& interface_name,");
+ text->AddLine("const std::string& property_name) {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ if (itf.properties.empty())
+ continue;
+
+ NameParser itf_name{itf.name};
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ string map_name = itf_name.MakeVariableName() + "_instances_";
+ text->AddLine(StringPrintf("auto p = %s.find(object_path);",
+ map_name.c_str()));
+ text->AddLine(StringPrintf("if (p == %s.end())", map_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("p->second->OnPropertyChanged(property_name);");
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddObjectAdded(
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLine("void ObjectAdded(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ if (!itf.properties.empty()) {
+ text->AddLine("auto property_set =");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine(StringPrintf("static_cast<%s::PropertySet*>(",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("dbus_object_manager_->GetProperties(object_path, "
+ "interface_name));");
+ text->PopOffset();
+ text->PopOffset();
+ }
+ text->AddLine(StringPrintf("std::unique_ptr<%s> %s_proxy{",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ string new_instance = StringPrintf("new %s{bus_",
+ itf_name.MakeProxyName(true).c_str());
+ if (itf.path.empty())
+ new_instance += ", object_path";
+ if (!itf.properties.empty())
+ new_instance += ", property_set";
+ new_instance += "}";
+ text->AddLine(new_instance);
+ text->PopOffset();
+ text->AddLine("};");
+ text->AddLine(StringPrintf("auto p = %s_instances_.emplace(object_path, "
+ "std::move(%s_proxy));",
+ var_name.c_str(), var_name.c_str()));
+ text->AddLine(StringPrintf("if (!on_%s_added_.is_null())",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_added_.Run(p.first->second.get());",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddObjectRemoved(
+ const std::vector<Interface>& interfaces,
+ IndentedText* text) {
+ text->AddLine("void ObjectRemoved(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("auto p = %s_instances_.find(object_path);",
+ var_name.c_str()));
+ text->AddLine(StringPrintf("if (p != %s_instances_.end()) {",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("if (!on_%s_removed_.is_null())",
+ var_name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("on_%s_removed_.Run(object_path);",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine(StringPrintf("%s_instances_.erase(p);",
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddLine("return;");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddCreateProperties(
+ const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text) {
+ text->AddLine("dbus::PropertySet* CreateProperties(");
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("dbus::ObjectProxy* object_proxy,");
+ text->AddLine("const dbus::ObjectPath& object_path,");
+ text->AddLine("const std::string& interface_name) override {");
+ text->PopOffset();
+ text->PushOffset(kBlockOffset);
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
+ itf.name.c_str()));
+ text->PushOffset(kBlockOffset);
+ text->AddLine(StringPrintf("return new %s::PropertySet{",
+ itf_name.MakeProxyName(true).c_str()));
+ text->PushOffset(kLineContinuationOffset);
+ text->AddLine("object_proxy,");
+ text->AddLineAndPushOffsetTo(
+ StringPrintf("base::Bind(&%s::OnPropertyChanged,",
+ class_name.c_str()),
+ 1, '(');
+ text->AddLine("weak_ptr_factory_.GetWeakPtr(),");
+ text->AddLine("object_path,");
+ text->AddLine("interface_name)");
+ text->PopOffset();
+ text->PopOffset();
+ text->AddLine("};");
+ text->PopOffset();
+ text->AddLine("}");
+ }
+ text->AddLineAndPushOffsetTo("LOG(FATAL) << \"Creating properties for "
+ "unsupported interface \"", 1, ' ');
+ text->AddLine("<< interface_name;");
+ text->PopOffset();
+ text->AddLine("return nullptr;");
+ text->PopOffset();
+ text->AddLine("}");
+ text->AddBlankLine();
+}
+
+void ProxyGenerator::ObjectManager::AddDataMembers(
+ const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text) {
+ text->AddLine("scoped_refptr<dbus::Bus> bus_;");
+ text->AddLine("dbus::ObjectManager* dbus_object_manager_;");
+ for (const auto& itf : interfaces) {
+ NameParser itf_name{itf.name};
+ string var_name = itf_name.MakeVariableName();
+ text->AddLineAndPushOffsetTo("std::map<dbus::ObjectPath,", 1, '<');
+ text->AddLine(StringPrintf("std::unique_ptr<%s>> %s_instances_;",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->PopOffset();
+ text->AddLine(StringPrintf("base::Callback<void(%s*)> on_%s_added_;",
+ itf_name.MakeProxyName(true).c_str(),
+ var_name.c_str()));
+ text->AddLine(StringPrintf("base::Callback<void(const dbus::ObjectPath&)> "
+ "on_%s_removed_;",
+ var_name.c_str()));
+ }
+ text->AddLine(
+ StringPrintf("base::WeakPtrFactory<%s> weak_ptr_factory_{this};",
+ class_name.c_str()));
+ text->AddBlankLine();
+}
+
+// static
string ProxyGenerator::GetHandlerNameForSignal(const string& signal) {
return StringPrintf("On%sSignal", signal.c_str());
}
diff --git a/chromeos-dbus-bindings/proxy_generator.h b/chromeos-dbus-bindings/proxy_generator.h
index c0c909b..7d4549b 100644
--- a/chromeos-dbus-bindings/proxy_generator.h
+++ b/chromeos-dbus-bindings/proxy_generator.h
@@ -54,15 +54,80 @@
// of the object proxy.
static void AddReleaseObjectProxy(IndentedText* text);
+ // Generates AddGetObjectPath() method.
+ static void AddGetObjectPath(IndentedText* text);
+
+ // Generates GetObjectProxy() method.
+ static void AddGetObjectProxy(IndentedText* text);
+
+ // Generates SetPropertyChangedCallback/GetProperties() methods.
+ static void AddPropertyPublicMethods(const std::string& class_name,
+ IndentedText* text);
+
+ // Generates OnPropertyChanged() method.
+ static void AddOnPropertyChanged(IndentedText* text);
+
// Generates the method signatures for signal receivers.
static void AddSignalReceiver(const Interface& interface,
IndentedText* text);
+ // Generates the property set class to contain interface properties.
+ static void AddPropertySet(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
+ // Generates the property accessors.
+ static void AddProperties(const ServiceConfig& config,
+ const Interface& interface,
+ IndentedText* text);
+
// Generates a native C++ method which calls a D-Bus method on the proxy.
static void AddMethodProxy(const Interface::Method& interface,
const std::string& interface_name,
IndentedText* text);
+ // Generates the Object Manager proxy class.
+ struct ObjectManager {
+ // Generates the top-level class for Object Manager proxy.
+ static void GenerateProxy(const ServiceConfig& config,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates Object Manager constructor.
+ static void AddConstructor(const ServiceConfig& config,
+ const std::string& class_name,
+ const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates GetObjectManagerProxy() method.
+ static void AddGetObjectManagerProxy(IndentedText* text);
+
+ // Generates code for interface-specific accessor methods
+ static void AddInterfaceAccessors(const Interface& interface,
+ IndentedText* text);
+
+ // Generates OnPropertyChanged() method.
+ static void AddOnPropertyChanged(const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates ObjectAdded() method.
+ static void AddObjectAdded(const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates ObjectRemoved() method.
+ static void AddObjectRemoved(const std::vector<Interface>& interfaces,
+ IndentedText* text);
+
+ // Generates CreateProperties() method.
+ static void AddCreateProperties(const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text);
+
+ // Generates data members of the class.
+ static void AddDataMembers(const std::vector<Interface>& interfaces,
+ const std::string& class_name,
+ IndentedText* text);
+ };
// Generates the signal handler name for a given signal name.
static std::string GetHandlerNameForSignal(const std::string& signal);
diff --git a/chromeos-dbus-bindings/proxy_generator_unittest.cc b/chromeos-dbus-bindings/proxy_generator_unittest.cc
index e4f596e..d3ebaad 100644
--- a/chromeos-dbus-bindings/proxy_generator_unittest.cc
+++ b/chromeos-dbus-bindings/proxy_generator_unittest.cc
@@ -45,6 +45,7 @@
const char kSignal2Argument1[] = "as";
const char kSignal2Argument2[] = "y";
const char kExpectedContent[] = R"literal_string(
+#include <memory>
#include <string>
#include <vector>
@@ -55,11 +56,13 @@
#include <base/memory/ref_counted.h>
#include <chromeos/any.h>
#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
#include <chromeos/dbus/dbus_signal_handler.h>
#include <chromeos/errors/error.h>
#include <chromeos/variant_dictionary.h>
#include <dbus/bus.h>
#include <dbus/message.h>
+#include <dbus/object_manager.h>
#include <dbus/object_path.h>
#include <dbus/object_proxy.h>
@@ -80,10 +83,10 @@
TestInterfaceProxy(
const scoped_refptr<dbus::Bus>& bus,
const std::string& service_name) :
- bus_(bus),
- service_name_(service_name),
- dbus_object_proxy_(
- bus_->GetObjectProxy(service_name_, object_path_)) {
+ bus_{bus},
+ service_name_{service_name},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
}
TestInterfaceProxy(
@@ -120,6 +123,12 @@
bus_->RemoveObjectProxy(service_name_, object_path_, callback);
}
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
void OnDBusSignalConnected(
const std::string& interface,
const std::string& signal,
@@ -207,12 +216,12 @@
TestInterface2Proxy(
const scoped_refptr<dbus::Bus>& bus,
const std::string& service_name,
- const std::string& object_path) :
- bus_(bus),
- service_name_(service_name),
- object_path_(object_path),
- dbus_object_proxy_(
- bus_->GetObjectProxy(service_name_, object_path_)) {
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ service_name_{service_name},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
}
~TestInterface2Proxy() {
@@ -222,6 +231,12 @@
bus_->RemoveObjectProxy(service_name_, object_path_, callback);
}
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
bool GetPersonInfo(
std::string* out_name,
int32_t* out_age,
@@ -246,10 +261,10 @@
} // namespace chromium
} // namespace org
-
)literal_string";
const char kExpectedContentWithService[] = R"literal_string(
+#include <memory>
#include <string>
#include <vector>
@@ -260,11 +275,13 @@
#include <base/memory/ref_counted.h>
#include <chromeos/any.h>
#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
#include <chromeos/dbus/dbus_signal_handler.h>
#include <chromeos/errors/error.h>
#include <chromeos/variant_dictionary.h>
#include <dbus/bus.h>
#include <dbus/message.h>
+#include <dbus/object_manager.h>
#include <dbus/object_path.h>
#include <dbus/object_proxy.h>
@@ -280,9 +297,9 @@
};
TestInterfaceProxy(const scoped_refptr<dbus::Bus>& bus) :
- bus_(bus),
- dbus_object_proxy_(
- bus_->GetObjectProxy(service_name_, object_path_)) {
+ bus_{bus},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
}
TestInterfaceProxy(
@@ -308,6 +325,12 @@
bus_->RemoveObjectProxy(service_name_, object_path_, callback);
}
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
void OnDBusSignalConnected(
const std::string& interface,
const std::string& signal,
@@ -340,11 +363,11 @@
public:
TestInterface2Proxy(
const scoped_refptr<dbus::Bus>& bus,
- const std::string& object_path) :
- bus_(bus),
- object_path_(object_path),
- dbus_object_proxy_(
- bus_->GetObjectProxy(service_name_, object_path_)) {
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
}
~TestInterface2Proxy() {
@@ -354,6 +377,12 @@
bus_->RemoveObjectProxy(service_name_, object_path_, callback);
}
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
private:
scoped_refptr<dbus::Bus> bus_;
const std::string service_name_{"org.chromium.Test"};
@@ -365,9 +394,705 @@
} // namespace chromium
} // namespace org
-
)literal_string";
+const char kExpectedContentWithObjectManager[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+class ObjectManagerProxy;
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf1.
+class Itf1Proxy final {
+ public:
+ class SignalReceiver {
+ public:
+ virtual void OnCloserSignal() {}
+ };
+
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf1",
+ callback} {
+ RegisterProperty("data", &data);
+ }
+
+ chromeos::dbus_utils::Property<std::string> data;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf1Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ PropertySet* property_set) :
+ bus_{bus},
+ service_name_{service_name},
+ property_set_{property_set},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ Itf1Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ PropertySet* property_set,
+ SignalReceiver* signal_receiver) :
+ Itf1Proxy(bus, service_name, property_set) {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.Itf1",
+ "Closer",
+ base::Bind(
+ &SignalReceiver::OnCloserSignal,
+ base::Unretained(signal_receiver)),
+ base::Bind(
+ &Itf1Proxy::OnDBusSignalConnected,
+ base::Unretained(this)));
+ }
+
+ ~Itf1Proxy() {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ void SetPropertyChangedCallback(
+ const base::Callback<void(Itf1Proxy*, const std::string&)>& callback) {
+ on_property_changed_ = callback;
+ }
+
+ const PropertySet* GetProperties() const { return property_set_; }
+ PropertySet* GetProperties() { return property_set_; }
+
+ void OnDBusSignalConnected(
+ const std::string& interface,
+ const std::string& signal,
+ bool success) {
+ if (!success) {
+ LOG(ERROR)
+ << "Failed to connect to " << interface << "." << signal
+ << " for " << service_name_ << " at "
+ << object_path_.value();
+ }
+ }
+
+ const std::string& data() const {
+ return property_set_->data.value();
+ }
+
+ private:
+ void OnPropertyChanged(const std::string& property_name) {
+ if (!on_property_changed_.is_null())
+ on_property_changed_.Run(this, property_name);
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ const dbus::ObjectPath object_path_{"/org/chromium/Test/Object"};
+ PropertySet* property_set_;
+ base::Callback<void(Itf1Proxy*, const std::string&)> on_property_changed_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ friend class org::chromium::ObjectManagerProxy;
+ DISALLOW_COPY_AND_ASSIGN(Itf1Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf2.
+class Itf2Proxy final {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf2",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ service_name_{service_name},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf2Proxy() {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ std::string service_name_;
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+class ObjectManagerProxy : public dbus::ObjectManager::Interface {
+ public:
+ ObjectManagerProxy(const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name)
+ : bus_{bus},
+ dbus_object_manager_{bus->GetObjectManager(
+ service_name,
+ dbus::ObjectPath{"/org/chromium/Test"})} {
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf1", this);
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf2", this);
+ }
+
+ dbus::ObjectManager* GetObjectManagerProxy() const {
+ return dbus_object_manager_;
+ }
+
+ org::chromium::Itf1Proxy* GetItf1Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf1Proxy*> GetItf1Instances() const {
+ std::vector<org::chromium::Itf1Proxy*> values;
+ values.reserve(itf1_instances_.size());
+ for (const auto& pair : itf1_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf1AddedCallback(
+ const base::Callback<void(org::chromium::Itf1Proxy*)>& callback) {
+ on_itf1_added_ = callback;
+ }
+ void SetItf1RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf1_removed_ = callback;
+ }
+
+ org::chromium::Itf2Proxy* GetItf2Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf2Proxy*> GetItf2Instances() const {
+ std::vector<org::chromium::Itf2Proxy*> values;
+ values.reserve(itf2_instances_.size());
+ for (const auto& pair : itf2_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf2AddedCallback(
+ const base::Callback<void(org::chromium::Itf2Proxy*)>& callback) {
+ on_itf2_added_ = callback;
+ }
+ void SetItf2RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf2_removed_ = callback;
+ }
+
+ private:
+ void OnPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& interface_name,
+ const std::string& property_name) {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p == itf1_instances_.end())
+ return;
+ p->second->OnPropertyChanged(property_name);
+ return;
+ }
+ }
+
+ void ObjectAdded(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto property_set =
+ static_cast<org::chromium::Itf1Proxy::PropertySet*>(
+ dbus_object_manager_->GetProperties(object_path, interface_name));
+ std::unique_ptr<org::chromium::Itf1Proxy> itf1_proxy{
+ new org::chromium::Itf1Proxy{bus_, property_set}
+ };
+ auto p = itf1_instances_.emplace(object_path, std::move(itf1_proxy));
+ if (!on_itf1_added_.is_null())
+ on_itf1_added_.Run(p.first->second.get());
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ std::unique_ptr<org::chromium::Itf2Proxy> itf2_proxy{
+ new org::chromium::Itf2Proxy{bus_, object_path}
+ };
+ auto p = itf2_instances_.emplace(object_path, std::move(itf2_proxy));
+ if (!on_itf2_added_.is_null())
+ on_itf2_added_.Run(p.first->second.get());
+ return;
+ }
+ }
+
+ void ObjectRemoved(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end()) {
+ if (!on_itf1_removed_.is_null())
+ on_itf1_removed_.Run(object_path);
+ itf1_instances_.erase(p);
+ }
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end()) {
+ if (!on_itf2_removed_.is_null())
+ on_itf2_removed_.Run(object_path);
+ itf2_instances_.erase(p);
+ }
+ return;
+ }
+ }
+
+ dbus::PropertySet* CreateProperties(
+ dbus::ObjectProxy* object_proxy,
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ return new org::chromium::Itf1Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ return new org::chromium::Itf2Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ LOG(FATAL) << "Creating properties for unsupported interface "
+ << interface_name;
+ return nullptr;
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectManager* dbus_object_manager_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf1Proxy>> itf1_instances_;
+ base::Callback<void(org::chromium::Itf1Proxy*)> on_itf1_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf1_removed_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf2Proxy>> itf2_instances_;
+ base::Callback<void(org::chromium::Itf2Proxy*)> on_itf2_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf2_removed_;
+ base::WeakPtrFactory<ObjectManagerProxy> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectManagerProxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
+
+const char kExpectedContentWithObjectManagerAndServiceName[] = R"literal_string(
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <chromeos/any.h>
+#include <chromeos/dbus/dbus_method_invoker.h>
+#include <chromeos/dbus/dbus_property.h>
+#include <chromeos/dbus/dbus_signal_handler.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/variant_dictionary.h>
+#include <dbus/bus.h>
+#include <dbus/message.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <dbus/object_proxy.h>
+
+namespace org {
+namespace chromium {
+class ObjectManagerProxy;
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf1.
+class Itf1Proxy final {
+ public:
+ class SignalReceiver {
+ public:
+ virtual void OnCloserSignal() {}
+ };
+
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf1",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf1Proxy(const scoped_refptr<dbus::Bus>& bus) :
+ bus_{bus},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ Itf1Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ SignalReceiver* signal_receiver) :
+ Itf1Proxy(bus) {
+ chromeos::dbus_utils::ConnectToSignal(
+ dbus_object_proxy_,
+ "org.chromium.Itf1",
+ "Closer",
+ base::Bind(
+ &SignalReceiver::OnCloserSignal,
+ base::Unretained(signal_receiver)),
+ base::Bind(
+ &Itf1Proxy::OnDBusSignalConnected,
+ base::Unretained(this)));
+ }
+
+ ~Itf1Proxy() {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ void OnDBusSignalConnected(
+ const std::string& interface,
+ const std::string& signal,
+ bool success) {
+ if (!success) {
+ LOG(ERROR)
+ << "Failed to connect to " << interface << "." << signal
+ << " for " << service_name_ << " at "
+ << object_path_.value();
+ }
+ }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ const dbus::ObjectPath object_path_{"/org/chromium/Test/Object"};
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf1Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface proxy for org::chromium::Itf2.
+class Itf2Proxy final {
+ public:
+ class PropertySet : public dbus::PropertySet {
+ public:
+ PropertySet(dbus::ObjectProxy* object_proxy,
+ const PropertyChangedCallback& callback)
+ : dbus::PropertySet{object_proxy,
+ "org.chromium.Itf2",
+ callback} {
+ }
+
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PropertySet);
+ };
+
+ Itf2Proxy(
+ const scoped_refptr<dbus::Bus>& bus,
+ const dbus::ObjectPath& object_path) :
+ bus_{bus},
+ object_path_{object_path},
+ dbus_object_proxy_{
+ bus_->GetObjectProxy(service_name_, object_path_)} {
+ }
+
+ ~Itf2Proxy() {
+ }
+
+ void ReleaseObjectProxy(const base::Closure& callback) {
+ bus_->RemoveObjectProxy(service_name_, object_path_, callback);
+ }
+
+ const dbus::ObjectPath& GetObjectPath() const {
+ return object_path_;
+ }
+
+ dbus::ObjectProxy* GetObjectProxy() const { return dbus_object_proxy_; }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ const std::string service_name_{"org.chromium.Test"};
+ dbus::ObjectPath object_path_;
+ dbus::ObjectProxy* dbus_object_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(Itf2Proxy);
+};
+
+} // namespace chromium
+} // namespace org
+
+namespace org {
+namespace chromium {
+
+class ObjectManagerProxy : public dbus::ObjectManager::Interface {
+ public:
+ ObjectManagerProxy(const scoped_refptr<dbus::Bus>& bus)
+ : bus_{bus},
+ dbus_object_manager_{bus->GetObjectManager(
+ "org.chromium.Test",
+ dbus::ObjectPath{"/org/chromium/Test"})} {
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf1", this);
+ dbus_object_manager_->RegisterInterface("org.chromium.Itf2", this);
+ }
+
+ dbus::ObjectManager* GetObjectManagerProxy() const {
+ return dbus_object_manager_;
+ }
+
+ org::chromium::Itf1Proxy* GetItf1Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf1Proxy*> GetItf1Instances() const {
+ std::vector<org::chromium::Itf1Proxy*> values;
+ values.reserve(itf1_instances_.size());
+ for (const auto& pair : itf1_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf1AddedCallback(
+ const base::Callback<void(org::chromium::Itf1Proxy*)>& callback) {
+ on_itf1_added_ = callback;
+ }
+ void SetItf1RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf1_removed_ = callback;
+ }
+
+ org::chromium::Itf2Proxy* GetItf2Proxy(
+ const dbus::ObjectPath& object_path) {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end())
+ return p->second.get();
+ return nullptr;
+ }
+ std::vector<org::chromium::Itf2Proxy*> GetItf2Instances() const {
+ std::vector<org::chromium::Itf2Proxy*> values;
+ values.reserve(itf2_instances_.size());
+ for (const auto& pair : itf2_instances_)
+ values.push_back(pair.second.get());
+ return values;
+ }
+ void SetItf2AddedCallback(
+ const base::Callback<void(org::chromium::Itf2Proxy*)>& callback) {
+ on_itf2_added_ = callback;
+ }
+ void SetItf2RemovedCallback(
+ const base::Callback<void(const dbus::ObjectPath&)>& callback) {
+ on_itf2_removed_ = callback;
+ }
+
+ private:
+ void OnPropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& interface_name,
+ const std::string& property_name) {
+ }
+
+ void ObjectAdded(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ std::unique_ptr<org::chromium::Itf1Proxy> itf1_proxy{
+ new org::chromium::Itf1Proxy{bus_}
+ };
+ auto p = itf1_instances_.emplace(object_path, std::move(itf1_proxy));
+ if (!on_itf1_added_.is_null())
+ on_itf1_added_.Run(p.first->second.get());
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ std::unique_ptr<org::chromium::Itf2Proxy> itf2_proxy{
+ new org::chromium::Itf2Proxy{bus_, object_path}
+ };
+ auto p = itf2_instances_.emplace(object_path, std::move(itf2_proxy));
+ if (!on_itf2_added_.is_null())
+ on_itf2_added_.Run(p.first->second.get());
+ return;
+ }
+ }
+
+ void ObjectRemoved(
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ auto p = itf1_instances_.find(object_path);
+ if (p != itf1_instances_.end()) {
+ if (!on_itf1_removed_.is_null())
+ on_itf1_removed_.Run(object_path);
+ itf1_instances_.erase(p);
+ }
+ return;
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ auto p = itf2_instances_.find(object_path);
+ if (p != itf2_instances_.end()) {
+ if (!on_itf2_removed_.is_null())
+ on_itf2_removed_.Run(object_path);
+ itf2_instances_.erase(p);
+ }
+ return;
+ }
+ }
+
+ dbus::PropertySet* CreateProperties(
+ dbus::ObjectProxy* object_proxy,
+ const dbus::ObjectPath& object_path,
+ const std::string& interface_name) override {
+ if (interface_name == "org.chromium.Itf1") {
+ return new org::chromium::Itf1Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ if (interface_name == "org.chromium.Itf2") {
+ return new org::chromium::Itf2Proxy::PropertySet{
+ object_proxy,
+ base::Bind(&ObjectManagerProxy::OnPropertyChanged,
+ weak_ptr_factory_.GetWeakPtr(),
+ object_path,
+ interface_name)
+ };
+ }
+ LOG(FATAL) << "Creating properties for unsupported interface "
+ << interface_name;
+ return nullptr;
+ }
+
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectManager* dbus_object_manager_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf1Proxy>> itf1_instances_;
+ base::Callback<void(org::chromium::Itf1Proxy*)> on_itf1_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf1_removed_;
+ std::map<dbus::ObjectPath,
+ std::unique_ptr<org::chromium::Itf2Proxy>> itf2_instances_;
+ base::Callback<void(org::chromium::Itf2Proxy*)> on_itf2_added_;
+ base::Callback<void(const dbus::ObjectPath&)> on_itf2_removed_;
+ base::WeakPtrFactory<ObjectManagerProxy> weak_ptr_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectManagerProxy);
+};
+
+} // namespace chromium
+} // namespace org
+)literal_string";
} // namespace
class ProxyGeneratorTest : public Test {
@@ -456,4 +1181,53 @@
<< kExpectedContentWithService << "...within content...\n" << contents;
}
+TEST_F(ProxyGeneratorTest, GenerateAdaptorsWithObjectManager) {
+ Interface interface;
+ interface.name = "org.chromium.Itf1";
+ interface.path = "/org/chromium/Test/Object";
+ interface.signals.emplace_back(kSignal1Name);
+ interface.properties.emplace_back("data", "s", "read");
+ Interface interface2;
+ interface2.name = "org.chromium.Itf2";
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output3.h");
+ ServiceConfig config;
+ config.object_manager.name = "org.chromium.ObjectManager";
+ config.object_manager.object_path = "/org/chromium/Test";
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ EXPECT_NE(string::npos, contents.find(kExpectedContentWithObjectManager))
+ << "Expected to find the following content...\n"
+ << kExpectedContentWithObjectManager << "...within content...\n"
+ << contents;
+}
+
+TEST_F(ProxyGeneratorTest, GenerateAdaptorsWithObjectManagerAndServiceName) {
+ Interface interface;
+ interface.name = "org.chromium.Itf1";
+ interface.path = "/org/chromium/Test/Object";
+ interface.signals.emplace_back(kSignal1Name);
+ Interface interface2;
+ interface2.name = "org.chromium.Itf2";
+ vector<Interface> interfaces{interface, interface2};
+ base::FilePath output_path = temp_dir_.path().Append("output4.h");
+ ServiceConfig config;
+ config.service_name = "org.chromium.Test";
+ config.object_manager.name = "org.chromium.ObjectManager";
+ config.object_manager.object_path = "/org/chromium/Test";
+ EXPECT_TRUE(ProxyGenerator::GenerateProxies(config, interfaces, output_path));
+ string contents;
+ EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
+ // The header guards contain the (temporary) filename, so we search for
+ // the content we need within the string.
+ EXPECT_NE(string::npos,
+ contents.find(kExpectedContentWithObjectManagerAndServiceName))
+ << "Expected to find the following content...\n"
+ << kExpectedContentWithObjectManagerAndServiceName
+ << "...within content...\n" << contents;
+}
+
} // namespace chromeos_dbus_bindings