chromeos-dbus-bindings: Redesign due to underlying DBusObject change.

DBusObject method handlers have been changed to allow for async
method handlers. D-Bus proxy generator is changing accordingly
to reflect the underlying implementation change.

Now it is possible to generate asyc-ready method handlers by
adding annotations to the method elements in the source XML file.
With the "org.chromium.DBus.Method.Kind" attribute on a method
description element, you can now control which of the four
supported method handers are to be used:

- "normal" (default): the handler signature is as follows:

  bool Handler(ErrorPtr* error, const Type1& in1, const Type2& in2, ...
               Type3* out1, Type4* out2, ...);

- "simple": the handler does not fail and hence does not return
  any error information. One of the following two signatures:

  RetType Handler(const Type1& in1, const Type2& in2, ...);
  void Handler(const Type1& in1, const Type2& in2, ...,
               Type3* out1, Type4* out2, ...);

- "async": is a possibly asynchronous method handler:

  void Handler(scoped_ptr<DBusMethodResponse> response,
               const Type1& in1, const Type2& in2, ...);

- "raw": the fully custom specialty handler that allows
  raw implementation of D-Bus method with no additional framework:

  void Handler(dbus::MethodCall* method_call, ResponseSender sender);

As part of this change, it was also possible to provide implementations
for methods returning more than one value.

A method element can also specify that the method handler can be
marked as 'const' in the C++ adapter by adding the following attribute
with the value of "true": "org.chromium.DBus.Method.Const"

Another significant change to the D-Bus binding generator is the
support of multiple interfaces per D-Bus object. Now the XML file
can list multiple <interface> tags for a class node. This makes the
generator to create the abstract C++ interface adaptors that contain
protorypes for all the methods, signals and properties for that interface.

The final interface implementation class derives from one or more adaptor(s)
and implements the actual interface methods.

Finally, updated lorgnette to use the new generated adaptor for its
D-Bus interfaces.

BUG=chromium:419271, chromium:420925, chromium:428390
TEST=FEATURES=test emerge-link chromeos-dbus-bindings

Change-Id: I02d1fc2e21a230e0f4c959c54ed7c71490945b12
Reviewed-on: https://chromium-review.googlesource.com/227281
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc
index 990976a..5f71816 100644
--- a/chromeos-dbus-bindings/adaptor_generator.cc
+++ b/chromeos-dbus-bindings/adaptor_generator.cc
@@ -9,6 +9,7 @@
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
 
 #include "chromeos-dbus-bindings/dbus_signature.h"
 #include "chromeos-dbus-bindings/indented_text.h"
@@ -21,15 +22,20 @@
 namespace chromeos_dbus_bindings {
 
 // static
-bool AdaptorGenerator::GenerateAdaptor(
-    const Interface& interface,
+bool AdaptorGenerator::GenerateAdaptors(
+    const std::vector<Interface>& interfaces,
     const base::FilePath& output_file) {
   IndentedText text;
-  text.AddLine(StringPrintf("// Automatic generation of interface for %s",
-                            interface.name.c_str()));
-  string header_guard = GenerateHeaderGuard(output_file, interface.name);
+  CHECK(!interfaces.empty()) << "At least one interface must be provided";
+
+  text.AddLine("// Automatic generation of D-Bus interfaces:");
+  for (const auto& interface : interfaces) {
+    text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
+  }
+  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();
@@ -39,209 +45,282 @@
   text.AddLine("#include <chromeos/dbus/dbus_object.h>");
   text.AddLine("#include <chromeos/dbus/exported_object_manager.h>");
   text.AddLine("#include <chromeos/variant_dictionary.h>");
-  text.AddBlankLine();
 
-  vector<string> namespaces;
-  string class_name;
-  CHECK(GetNamespacesAndClassName(interface.name, &namespaces, &class_name));
-  for (const auto& space : namespaces) {
-    text.AddLine(StringPrintf("namespace %s {", space.c_str()));
-  }
-  text.AddBlankLine();
+  for (const auto& interface : interfaces)
+    GenerateInterfaceAdaptor(interface, &text);
 
-  string adaptor_name = StringPrintf("%sAdaptor", class_name.c_str());
-  text.AddLine(StringPrintf("class %s {", adaptor_name.c_str()));
-  text.AddLineWithOffset("public:", kScopeOffset);
-
-  text.PushOffset(kBlockOffset);
-  AddMethodInterface(interface, &text);
-  AddConstructor(interface, adaptor_name, &text);
-  AddSendSignalMethods(interface, &text);
-  text.AddLine(StringPrintf("virtual ~%s() = default;", adaptor_name.c_str()));
-  text.AddLine("virtual void OnRegisterComplete(bool success) {}");
-
-  text.AddBlankLine();
-  AddPropertyMethods(interface, &text);
-  text.PopOffset();
-
-  text.AddLineWithOffset("protected:", kScopeOffset);
-  text.PushOffset(kBlockOffset);
-  text.AddLine("chromeos::dbus_utils::DBusInterface* dbus_interface() {");
-  text.PushOffset(kBlockOffset);
-  text.AddLine("return dbus_interface_;");
-  text.PopOffset();
-  text.AddLine("}");
-  text.PopOffset();
-
-  text.AddBlankLine();
-  text.AddLineWithOffset("private:", kScopeOffset);
-
-  text.PushOffset(kBlockOffset);
-  text.AddLine("// Exported properties");
-  AddPropertyDataMembers(interface, &text);
-  text.AddLine("MethodInterface* interface_;  // Owned by caller.");
-  text.AddLine("chromeos::dbus_utils::DBusObject dbus_object_;");
-  AddSignalDataMembers(interface, &text);
-  text.AddLine("// Owned by |dbus_object_|.");
-  text.AddLine("chromeos::dbus_utils::DBusInterface* dbus_interface_;");
-  text.AddLine(StringPrintf(
-      "DISALLOW_COPY_AND_ASSIGN(%s);", adaptor_name.c_str()));
-  text.PopOffset();
-
-  text.AddLine("};");
-  text.AddBlankLine();
-
-  for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
-    text.AddLine(StringPrintf("}  // namespace %s", it->c_str()));
-  }
   text.AddLine(StringPrintf("#endif  // %s", header_guard.c_str()));
 
   return WriteTextToFile(output_file, text);
 }
 
 // static
-void AdaptorGenerator::AddConstructor(const Interface& interface,
-                                      const string& class_name,
+void AdaptorGenerator::GenerateInterfaceAdaptor(
+    const Interface& interface,
+    IndentedText *text) {
+  vector<string> namespaces;
+  string itf_name;
+  CHECK(GetNamespacesAndClassName(interface.name, &namespaces, &itf_name));
+  string class_name = itf_name + "Adaptor";
+  string full_itf_name = GetFullClassName(namespaces, itf_name);
+  itf_name += "Interface";
+
+  text->AddBlankLine();
+  for (const auto& ns : namespaces) {
+    text->AddLine(StringPrintf("namespace %s {", ns.c_str()));
+  }
+
+  text->AddBlankLine();
+  text->AddLine(StringPrintf("// Interface definition for %s.",
+                             full_itf_name.c_str()));
+  text->AddLine(StringPrintf("class %s {", itf_name.c_str()));
+  text->AddLineWithOffset("public:", kScopeOffset);
+  text->PushOffset(kBlockOffset);
+  text->AddLine(StringPrintf("virtual ~%s() = default;", itf_name.c_str()));
+  AddInterfaceMethods(interface, text);
+  text->PopOffset();
+  text->AddLine("};");
+
+  text->AddBlankLine();
+  text->AddLine(StringPrintf("// Interface adaptor for %s.",
+                             full_itf_name.c_str()));
+  text->AddLine(StringPrintf("class %s {", class_name.c_str()));
+  text->AddLineWithOffset("public:", kScopeOffset);
+  text->PushOffset(kBlockOffset);
+  AddConstructor(class_name, itf_name, text);
+  AddRegisterWithDBusObject(itf_name, interface, text);
+  AddSendSignalMethods(interface, text);
+  AddPropertyMethodImplementation(interface, text);
+  text->PopOffset();
+
+  text->AddBlankLine();
+  text->AddLineWithOffset("private:", kScopeOffset);
+  text->PushOffset(kBlockOffset);
+  AddSignalDataMembers(interface, text);
+  AddPropertyDataMembers(interface, text);
+
+  text->AddBlankLine();
+  text->AddLine(StringPrintf(
+      "%s* interface_;  // Owned by container of this adapter.",
+      itf_name.c_str()));
+
+  text->AddBlankLine();
+  text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
+                             class_name.c_str()));
+  text->PopOffset();
+  text->AddLine("};");
+
+  text->AddBlankLine();
+  for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
+    text->AddLine(StringPrintf("}  // namespace %s", it->c_str()));
+  }
+}
+
+// static
+void AdaptorGenerator::AddConstructor(const string& class_name,
+                                      const string& itf_name,
                                       IndentedText *text) {
-  IndentedText block;
-  block.AddLine(StringPrintf("%s(", class_name.c_str()));
-  block.PushOffset(kLineContinuationOffset);
-  block.AddLine("chromeos::dbus_utils::ExportedObjectManager* object_manager,");
-  block.AddLine("const scoped_refptr<dbus::Bus>& bus,");
-  block.AddLine("const std::string& object_path,");
-  block.AddLine("MethodInterface* interface)  // Owned by caller.");
-  block.AddLine(": interface_(interface),");
-  block.PushOffset(kBlockOffset);
-  block.AddLine("dbus_object_(");
-  block.PushOffset(kLineContinuationOffset);
-  block.AddLine("object_manager,");
-  block.AddLine("bus,");
-  block.AddLine("dbus::ObjectPath(object_path)),");
-  block.PopOffset();
+  text->AddLine(StringPrintf("%s(%s* interface) : interface_(interface) {}",
+                             class_name.c_str(), itf_name.c_str()));
+}
 
-  // Signal constructors.
-  for (const auto& signal : interface.signals) {
-    block.AddLine(StringPrintf("signal_%s_(", signal.name.c_str()));
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine("&dbus_object_,");
-    block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
-    block.AddLine(StringPrintf("\"%s\"),", signal.name.c_str()));
-    block.PopOffset();
-  }
+// static
+void AdaptorGenerator::AddRegisterWithDBusObject(
+    const std::string& itf_name,
+    const Interface& interface,
+    IndentedText *text) {
+  text->AddBlankLine();
+  text->AddLine(
+    "void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {");
+  text->PushOffset(kBlockOffset);
+  text->AddLine("chromeos::dbus_utils::DBusInterface* itf =");
+  text->AddLineWithOffset(
+      StringPrintf("object->AddOrGetInterface(\"%s\");",
+                   interface.name.c_str()), kLineContinuationOffset);
+  RegisterInterface(itf_name, interface, text);
+  text->PopOffset();
+  text->AddLine("}");
+}
 
-  block.AddLine("dbus_interface_(");
-  block.PushOffset(kLineContinuationOffset);
-  block.AddLine(StringPrintf(
-      "dbus_object_.AddOrGetInterface(\"%s\")) {", interface.name.c_str()));
-  block.PopOffset();
-  block.PopOffset();
-  block.PopOffset();
-  block.PushOffset(kBlockOffset);
+// static
+void AdaptorGenerator::RegisterInterface(const string& itf_name,
+                                         const Interface& interface,
+                                         IndentedText *text) {
+  if (!interface.methods.empty())
+    text->AddBlankLine();
   for (const auto& method : interface.methods) {
-    if (method.output_arguments.size() > 1) {
-      // TODO(pstew): Accept multiple output arguments.  crbug.com/419271
-      continue;
+    string add_handler_name;
+    switch (method.kind) {
+      case Interface::Method::Kind::kSimple:
+        add_handler_name = "AddSimpleMethodHandler";
+        break;
+      case Interface::Method::Kind::kNormal:
+        add_handler_name = "AddSimpleMethodHandlerWithError";
+        break;
+      case Interface::Method::Kind::kAsync:
+        add_handler_name = "AddMethodHandler";
+        break;
+      case Interface::Method::Kind::kRaw:
+        add_handler_name = "AddRawMethodHandler";
+        break;
     }
-    block.AddLine("dbus_interface_->AddMethodHandler(");
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
-    block.AddLine("base::Unretained(interface_),");
-    block.AddLine(StringPrintf("&MethodInterface::%s);", method.name.c_str()));
-    block.PopOffset();
+
+    text->AddLine(StringPrintf("itf->%s(", add_handler_name.c_str()));
+    text->PushOffset(kLineContinuationOffset);
+    text->AddLine(StringPrintf("\"%s\",", method.name.c_str()));
+    text->AddLine("base::Unretained(interface_),");
+    text->AddLine(StringPrintf("&%s::%s);", itf_name.c_str(),
+                               method.name.c_str()));
+    text->PopOffset();
   }
+
+  // Register signals.
+  if (!interface.signals.empty())
+    text->AddBlankLine();
+  for (const auto& signal : interface.signals) {
+    string signal_var_name = StringPrintf("signal_%s_", signal.name.c_str());
+    string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
+    text->AddLine(StringPrintf("%s = itf->RegisterSignalOfType<%s>(\"%s\");",
+                               signal_var_name.c_str(),
+                               signal_type_name.c_str(),
+                               signal.name.c_str()));
+  }
+
   // Register exported properties.
+  if (!interface.properties.empty())
+    text->AddBlankLine();
   for (const auto& property : interface.properties) {
     string variable_name = GetPropertyVariableName(property.name);
-    block.AddLine("dbus_interface_->AddProperty(");
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine(StringPrintf("\"%s\",", property.name.c_str()));
-    block.AddLine(StringPrintf("&%s_);", variable_name.c_str()));
-    block.PopOffset();
+    text->AddLine(StringPrintf("itf->AddProperty(\"%s\", &%s_);",
+                               property.name.c_str(), variable_name.c_str()));
   }
-  block.AddLine("dbus_object_.RegisterAsync(base::Bind(");
-  block.AddLineWithOffset(
-      StringPrintf("&%s::OnRegisterComplete, base::Unretained(this)));",
-                   class_name.c_str()),
-      kLineContinuationOffset);
-  block.PopOffset();
-  block.AddLine("}");
-  text->AddBlock(block);
 }
 
 // static
-void AdaptorGenerator::AddMethodInterface(const Interface& interface,
-                                          IndentedText *text) {
+void AdaptorGenerator::AddInterfaceMethods(const Interface& interface,
+                                           IndentedText *text) {
   IndentedText block;
-  block.AddLine("class MethodInterface {");
-  block.AddLineWithOffset("public:", kScopeOffset);
   DbusSignature signature;
-  block.PushOffset(kBlockOffset);
+  if (!interface.methods.empty())
+    block.AddBlankLine();
+
   for (const auto& method : interface.methods) {
-    string return_type("void");
-    if (!method.output_arguments.empty()) {
-      if (method.output_arguments.size() > 1) {
-        // TODO(pstew): Accept multiple output arguments.  crbug.com://419271
-        LOG(WARNING) << "Method " << method.name << " has "
-                     << method.output_arguments.size()
-                     << " output arguments which is unsupported.";
-        continue;
-      }
-      CHECK(signature.Parse(method.output_arguments[0].type, &return_type));
-    }
-    block.AddLine(StringPrintf("virtual %s %s(",
-                               return_type.c_str(), method.name.c_str()));
-    block.PushOffset(kLineContinuationOffset);
-    string last_argument = "chromeos::ErrorPtr* /* error */";
-    for (const auto& argument : method.input_arguments) {
-      block.AddLine(last_argument + ",");
-      CHECK(signature.Parse(argument.type, &last_argument));
-      if (!IsIntegralType(last_argument)) {
-        last_argument = StringPrintf("const %s&", last_argument.c_str());
-      }
-      if (!argument.name.empty()) {
-        last_argument.append(StringPrintf(" /* %s */", argument.name.c_str()));
-      }
-    }
-    block.AddLine(last_argument + ") = 0;");
-    block.PopOffset();
-  }
-  block.PopOffset();
-  block.AddLine("};");
+    string const_method;
+    if (method.is_const)
+      const_method = " const";
 
+    string return_type = "void";
+    vector<string> method_params;
+    auto input_arguments_copy = method.input_arguments;
+    auto output_arguments_copy = method.output_arguments;
+    switch (method.kind) {
+      case Interface::Method::Kind::kSimple:
+        if (output_arguments_copy.size() == 1) {
+          CHECK(signature.Parse(output_arguments_copy[0].type, &return_type));
+          output_arguments_copy.clear();
+        }
+        break;
+      case Interface::Method::Kind::kNormal:
+        method_params.push_back("chromeos::ErrorPtr* error");
+        return_type = "bool";
+        break;
+      case Interface::Method::Kind::kAsync:
+        method_params.push_back(
+            "scoped_ptr<chromeos::dbus_utils::DBusMethodResponse> response");
+        // Async methods don't return values directly.
+        output_arguments_copy.clear();
+        break;
+      case Interface::Method::Kind::kRaw:
+        method_params.push_back("dbus::MethodCall* method_call");
+        method_params.push_back("chromeos::dbus_utils::ResponseSender sender");
+        // Raw methods don't take static parameters or return values directly.
+        input_arguments_copy.clear();
+        output_arguments_copy.clear();
+        break;
+    }
+    string method_start = StringPrintf("virtual %s %s(",
+                                       return_type.c_str(),
+                                       method.name.c_str());
+    string method_end = StringPrintf(")%s = 0;", const_method.c_str());
+    int index = 0;
+    for (const auto& argument : input_arguments_copy) {
+      string param_type;
+      CHECK(signature.Parse(argument.type, &param_type));
+      if (!IsIntegralType(param_type)) {
+        param_type = StringPrintf("const %s&", param_type.c_str());
+      }
+      string param_name = GetArgName("in", argument.name, ++index);
+      method_params.push_back(param_type + ' ' + param_name);
+    }
+
+    for (const auto& argument : output_arguments_copy) {
+      string param_type;
+      CHECK(signature.Parse(argument.type, &param_type));
+      string param_name = GetArgName("out", argument.name, ++index);
+      method_params.push_back(param_type + "* " + param_name);
+    }
+
+    if (method_params.empty()) {
+      block.AddLine(method_start + method_end);
+    } else {
+      block.AddLine(method_start);
+      block.PushOffset(kLineContinuationOffset);
+      for (size_t i = 0; i < method_params.size() - 1; i++)
+        block.AddLine(method_params[i] + ',');
+      block.AddLine(method_params.back() + method_end);
+      block.PopOffset();
+    }
+  }
   text->AddBlock(block);
 }
 
 // static
-void AdaptorGenerator::AddSendSignalMethods(const Interface& interface,
-                                            IndentedText *text) {
+void AdaptorGenerator::AddSendSignalMethods(
+    const Interface& interface,
+    IndentedText *text) {
   IndentedText block;
   DbusSignature signature;
 
+  if (!interface.signals.empty())
+    block.AddBlankLine();
+
   for (const auto& signal : interface.signals) {
-    block.AddLine(StringPrintf("void Send%sSignal(", signal.name.c_str()));
-    block.PushOffset(kLineContinuationOffset);
-    string last_argument;
-    int unnamed_args = 0;
-    string call_arguments;  // The arguments to pass to the Send() call.
+    string method_start = StringPrintf("void Send%sSignal(",
+                                       signal.name.c_str());
+    string method_end = ") {";
+
+    int index = 0;
+    vector<string> method_params;
+    vector<string> param_names;
     for (const auto& argument : signal.arguments) {
-      if (!last_argument.empty())
-        block.AddLine(last_argument + ",");
-      CHECK(signature.Parse(argument.type, &last_argument));
-      if (!IsIntegralType(last_argument))
-        last_argument = StringPrintf("const %s&", last_argument.c_str());
-      string argument_name = argument.name;
-      if (argument.name.empty())
-        argument_name = StringPrintf("_arg_%d", ++unnamed_args);
-      last_argument.append(" " + argument_name);
-      if (!call_arguments.empty())
-        call_arguments.append(", ");
-      call_arguments.append(argument_name);
+      string param_type;
+      CHECK(signature.Parse(argument.type, &param_type));
+      if (!IsIntegralType(param_type)) {
+        param_type = StringPrintf("const %s&", param_type.c_str());
+      }
+      string param_name = GetArgName("in", argument.name, ++index);
+      param_names.push_back(param_name);
+      method_params.push_back(param_type + ' ' + param_name);
     }
-    block.AddLine(last_argument + ") {");
-    block.PopOffset();
+
+    if (method_params.empty()) {
+      block.AddLine(method_start + method_end);
+    } else {
+      block.AddLine(method_start);
+      block.PushOffset(kLineContinuationOffset);
+      for (size_t i = 0; i < method_params.size() - 1; i++)
+        block.AddLine(method_params[i] + ',');
+      block.AddLine(method_params.back() + method_end);
+      block.PopOffset();
+    }
+
+    string args = chromeos::string_utils::Join(", ", param_names);
     block.PushOffset(kBlockOffset);
-    block.AddLine(StringPrintf(
-        "signal_%s_.Send(%s);", signal.name.c_str(), call_arguments.c_str()));
+    block.AddLine(StringPrintf("auto signal = signal_%s_.lock();",
+                                signal.name.c_str()));
+    block.AddLine("if (signal)");
+    block.AddLineWithOffset(StringPrintf("signal->Send(%s);", args.c_str()),
+                            kBlockOffset);
     block.PopOffset();
     block.AddLine("}");
   }
@@ -255,30 +334,46 @@
   DbusSignature signature;
 
   for (const auto& signal : interface.signals) {
-    block.AddLine("chromeos::dbus_utils::DBusSignal<");
-    block.PushOffset(kLineContinuationOffset);
-    string last_argument;
+    string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
+    string signal_type_alias_begin =
+        StringPrintf("using %s = chromeos::dbus_utils::DBusSignal<",
+                     signal_type_name.c_str());
+    string signal_type_alias_end = ">;";
+    vector<string> params;
     for (const auto& argument : signal.arguments) {
-      if (!last_argument.empty())
-        block.AddLine(last_argument + ",");
-      CHECK(signature.Parse(argument.type, &last_argument));
+      string param;
+      CHECK(signature.Parse(argument.type, &param));
       if (!argument.name.empty())
-        last_argument.append(StringPrintf(" /* %s */", argument.name.c_str()));
+        base::StringAppendF(&param, " /*%s*/", argument.name.c_str());
+      params.push_back(param);
     }
-    block.AddLine(StringPrintf(
-        "%s> signal_%s_;", last_argument.c_str(), signal.name.c_str()));
-    block.PopOffset();
+    if (params.empty()) {
+      block.AddLine(signal_type_alias_begin + signal_type_alias_end);
+    } else {
+      block.AddLine(signal_type_alias_begin);
+      block.PushOffset(kLineContinuationOffset);
+      for (size_t i = 0; i < params.size() - 1; i++)
+        block.AddLine(params[i] + ',');
+      block.AddLine(params.back() + signal_type_alias_end);
+      block.PopOffset();
+    }
+    block.AddLine(
+        StringPrintf("std::weak_ptr<%s> signal_%s_;",
+                      signal_type_name.c_str(), signal.name.c_str()));
+    block.AddBlankLine();
   }
   text->AddBlock(block);
 }
 
 // static
-void AdaptorGenerator::AddPropertyMethods(const Interface& interface,
-                                          IndentedText *text) {
+void AdaptorGenerator::AddPropertyMethodImplementation(
+    const Interface& interface,
+    IndentedText *text) {
   IndentedText block;
   DbusSignature signature;
 
   for (const auto& property : interface.properties) {
+    block.AddBlankLine();
     string type;
     CHECK(signature.Parse(property.type, &type));
     string variable_name = GetPropertyVariableName(property.name);
@@ -295,19 +390,18 @@
     block.AddLine("}");
 
     // Setter method.
-    block.AddLine(StringPrintf("void Set%s(", property.name.c_str()));
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine(StringPrintf("const %s& %s) {",
+    if (!IsIntegralType(type))
+      type = StringPrintf("const %s&", type.c_str());
+    block.AddLine(StringPrintf("void Set%s(%s %s) {",
+                               property.name.c_str(),
                                type.c_str(),
                                variable_name.c_str()));
-    block.PopOffset();
     block.PushOffset(kBlockOffset);
     block.AddLine(StringPrintf("%s_.SetValue(%s);",
                                variable_name.c_str(),
                                variable_name.c_str()));
     block.PopOffset();
     block.AddLine("}");
-    block.AddBlankLine();
   }
   text->AddBlock(block);
 }
diff --git a/chromeos-dbus-bindings/adaptor_generator.h b/chromeos-dbus-bindings/adaptor_generator.h
index 84adec7..87e1b26 100644
--- a/chromeos-dbus-bindings/adaptor_generator.h
+++ b/chromeos-dbus-bindings/adaptor_generator.h
@@ -26,20 +26,34 @@
 
 class AdaptorGenerator : public HeaderGenerator {
  public:
-  static bool GenerateAdaptor(const Interface& interface,
-                              const base::FilePath& output_file);
+  static bool GenerateAdaptors(const std::vector<Interface>& interfaces,
+                               const base::FilePath& output_file);
 
  private:
   friend class AdaptorGeneratorTest;
 
+  // Generates one interface adaptor.
+  static void GenerateInterfaceAdaptor(const Interface& interface,
+                                       IndentedText *text);
+
+  // Generates the method prototypes for an interface declaration.
+  static void AddInterfaceMethods(const Interface& interface,
+                                  IndentedText *text);
+
   // Generates the constructor for the adaptor.
-  static void AddConstructor(const Interface& interface,
-                             const std::string& class_name,
+  static void AddConstructor(const std::string& class_name,
+                             const std::string& itf_name,
                              IndentedText *text);
 
-  // Generates the method interface class.
-  static void AddMethodInterface(const Interface& interface,
-                                 IndentedText *text);
+  // Generates RegisterWithDBusObject() method.
+  static void AddRegisterWithDBusObject(const std::string& itf_name,
+                                        const Interface& interface,
+                                        IndentedText *text);
+
+  // Generates the code to register the interface with a D-Bus object.
+  static void RegisterInterface(const std::string& itf_name,
+                                const Interface& interface,
+                                IndentedText *text);
 
   // Generates adaptor methods to send the signals.
   static void AddSendSignalMethods(const Interface& interface,
@@ -50,8 +64,8 @@
                                    IndentedText *text);
 
   // Generates adaptor accessor methods for the properties.
-  static void AddPropertyMethods(const Interface& interface,
-                                 IndentedText *text);
+  static void AddPropertyMethodImplementation(const Interface& interface,
+                                              IndentedText *text);
 
   // Generate ExportProperty data members for the properties.
   static void AddPropertyDataMembers(const Interface& interface,
diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
index 41aeeb2..2646c26 100644
--- a/chromeos-dbus-bindings/adaptor_generator_unittest.cc
+++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
@@ -33,6 +33,7 @@
 const char kMethod1Return[] = "x";
 const char kMethod2Name[] = "Kei";
 const char kMethod3Name[] = "Kiyoko";
+const char kMethod3ReturnName0[] = "akira";
 const char kMethod3Return0[] = "x";
 const char kMethod3Return1[] = "s";
 const char kSignal0Name[] = "Update";
@@ -40,12 +41,16 @@
 const char kSignal1Argument0[] = "s";
 const char kSignal1ArgumentName0[] = "key";
 const char kSignal1Argument1[] = "ao";
-const char kProperty0Name[] = "InterfaceName";
+const char kProperty0Name[] = "CharacterName";
 const char kProperty0Type[] = "s";
 const char kProperty0Access[] = "read";
 
-const char kInterfaceName[] = "org.chromium.TestInterface";
+const char kInterfaceName[] = "org.chromium.Test";
+const char kInterfaceName2[] = "org.chromium.Test2";
+const char kMethod0Name2[] = "Kaneda2";
+const char kMethod1Name2[] = "Tetsuo2";
 const char kExpectedContent[] = R"literal_string(
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -59,97 +64,139 @@
 namespace org {
 namespace chromium {
 
-class TestInterfaceAdaptor {
+// Interface definition for org::chromium::Test.
+class TestInterface {
  public:
-  class MethodInterface {
-   public:
-    virtual std::string Kaneda(
-        chromeos::ErrorPtr* /* error */,
-        const std::string& /* iwata */,
-        const std::vector<dbus::ObjectPath>& /* clarke */) = 0;
-    virtual int64_t Tetsuo(
-        chromeos::ErrorPtr* /* error */,
-        int32_t) = 0;
-    virtual void Kei(
-        chromeos::ErrorPtr* /* error */) = 0;
-  };
-  TestInterfaceAdaptor(
-      chromeos::dbus_utils::ExportedObjectManager* object_manager,
-      const scoped_refptr<dbus::Bus>& bus,
-      const std::string& object_path,
-      MethodInterface* interface)  // Owned by caller.
-      : interface_(interface),
-        dbus_object_(
-            object_manager,
-            bus,
-            dbus::ObjectPath(object_path)),
-        signal_Update_(
-            &dbus_object_,
-            "org.chromium.TestInterface",
-            "Update"),
-        signal_Mapping_(
-            &dbus_object_,
-            "org.chromium.TestInterface",
-            "Mapping"),
-        dbus_interface_(
-            dbus_object_.AddOrGetInterface("org.chromium.TestInterface")) {
-    dbus_interface_->AddMethodHandler(
+  virtual ~TestInterface() = default;
+
+  virtual bool Kaneda(
+      chromeos::ErrorPtr* error,
+      const std::string& in_iwata,
+      const std::vector<dbus::ObjectPath>& in_clarke,
+      std::string* out_3) = 0;
+  virtual bool Tetsuo(
+      chromeos::ErrorPtr* error,
+      int32_t in_1,
+      int64_t* out_2) = 0;
+  virtual bool Kei(
+      chromeos::ErrorPtr* error) = 0;
+  virtual bool Kiyoko(
+      chromeos::ErrorPtr* error,
+      int64_t* out_akira,
+      std::string* out_2) = 0;
+};
+
+// Interface adaptor for org::chromium::Test.
+class TestAdaptor {
+ public:
+  TestAdaptor(TestInterface* interface) : interface_(interface) {}
+
+  void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {
+    chromeos::dbus_utils::DBusInterface* itf =
+        object->AddOrGetInterface("org.chromium.Test");
+
+    itf->AddSimpleMethodHandlerWithError(
         "Kaneda",
         base::Unretained(interface_),
-        &MethodInterface::Kaneda);
-    dbus_interface_->AddMethodHandler(
+        &TestInterface::Kaneda);
+    itf->AddSimpleMethodHandlerWithError(
         "Tetsuo",
         base::Unretained(interface_),
-        &MethodInterface::Tetsuo);
-    dbus_interface_->AddMethodHandler(
+        &TestInterface::Tetsuo);
+    itf->AddSimpleMethodHandlerWithError(
         "Kei",
         base::Unretained(interface_),
-        &MethodInterface::Kei);
-    dbus_interface_->AddProperty(
-        "InterfaceName",
-        &interface_name_);
-    dbus_object_.RegisterAsync(base::Bind(
-        &TestInterfaceAdaptor::OnRegisterComplete, base::Unretained(this)));
+        &TestInterface::Kei);
+    itf->AddSimpleMethodHandlerWithError(
+        "Kiyoko",
+        base::Unretained(interface_),
+        &TestInterface::Kiyoko);
+
+    signal_Update_ = itf->RegisterSignalOfType<SignalUpdateType>("Update");
+    signal_Mapping_ = itf->RegisterSignalOfType<SignalMappingType>("Mapping");
+
+    itf->AddProperty("CharacterName", &character_name_);
   }
-  void SendUpdateSignal(
-      ) {
-    signal_Update_.Send();
+
+  void SendUpdateSignal() {
+    auto signal = signal_Update_.lock();
+    if (signal)
+      signal->Send();
   }
   void SendMappingSignal(
-      const std::string& key,
-      const std::vector<dbus::ObjectPath>& _arg_1) {
-    signal_Mapping_.Send(key, _arg_1);
-  }
-  virtual ~TestInterfaceAdaptor() = default;
-  virtual void OnRegisterComplete(bool success) {}
-
-  std::string GetInterfaceName() const {
-    return interface_name_.GetValue().Get<std::string>();
-  }
-  void SetInterfaceName(
-      const std::string& interface_name) {
-    interface_name_.SetValue(interface_name);
+      const std::string& in_key,
+      const std::vector<dbus::ObjectPath>& in_2) {
+    auto signal = signal_Mapping_.lock();
+    if (signal)
+      signal->Send(in_key, in_2);
   }
 
- protected:
-  chromeos::dbus_utils::DBusInterface* dbus_interface() {
-    return dbus_interface_;
+  std::string GetCharacterName() const {
+    return character_name_.GetValue().Get<std::string>();
+  }
+  void SetCharacterName(const std::string& character_name) {
+    character_name_.SetValue(character_name);
   }
 
  private:
-  // Exported properties
+  using SignalUpdateType = chromeos::dbus_utils::DBusSignal<>;
+  std::weak_ptr<SignalUpdateType> signal_Update_;
+
+  using SignalMappingType = chromeos::dbus_utils::DBusSignal<
+      std::string /*key*/,
+      std::vector<dbus::ObjectPath>>;
+  std::weak_ptr<SignalMappingType> signal_Mapping_;
+
   chromeos::dbus_utils::ExportedProperty<std::string>
-      interface_name_;
-  MethodInterface* interface_;  // Owned by caller.
-  chromeos::dbus_utils::DBusObject dbus_object_;
-  chromeos::dbus_utils::DBusSignal<
-      > signal_Update_;
-  chromeos::dbus_utils::DBusSignal<
-      std::string /* key */,
-      std::vector<dbus::ObjectPath>> signal_Mapping_;
-  // Owned by |dbus_object_|.
-  chromeos::dbus_utils::DBusInterface* dbus_interface_;
-  DISALLOW_COPY_AND_ASSIGN(TestInterfaceAdaptor);
+      character_name_;
+
+  TestInterface* interface_;  // Owned by container of this adapter.
+
+  DISALLOW_COPY_AND_ASSIGN(TestAdaptor);
+};
+
+}  // namespace chromium
+}  // namespace org
+
+namespace org {
+namespace chromium {
+
+// Interface definition for org::chromium::Test2.
+class Test2Interface {
+ public:
+  virtual ~Test2Interface() = default;
+
+  virtual std::string Kaneda2(
+      const std::string& in_iwata) const = 0;
+  virtual void Tetsuo2(
+      scoped_ptr<chromeos::dbus_utils::DBusMethodResponse> response,
+      int32_t in_1) = 0;
+};
+
+// Interface adaptor for org::chromium::Test2.
+class Test2Adaptor {
+ public:
+  Test2Adaptor(Test2Interface* interface) : interface_(interface) {}
+
+  void RegisterWithDBusObject(chromeos::dbus_utils::DBusObject* object) {
+    chromeos::dbus_utils::DBusInterface* itf =
+        object->AddOrGetInterface("org.chromium.Test2");
+
+    itf->AddSimpleMethodHandler(
+        "Kaneda2",
+        base::Unretained(interface_),
+        &Test2Interface::Kaneda2);
+    itf->AddMethodHandler(
+        "Tetsuo2",
+        base::Unretained(interface_),
+        &Test2Interface::Tetsuo2);
+  }
+
+ private:
+
+  Test2Interface* interface_;  // Owned by container of this adapter.
+
+  DISALLOW_COPY_AND_ASSIGN(Test2Adaptor);
 };
 
 }  // namespace chromium
@@ -194,7 +241,7 @@
       kMethod3Name,
       vector<Interface::Argument>{},
       vector<Interface::Argument>{
-          {"", kMethod3Return0},
+          {kMethod3ReturnName0, kMethod3Return0},
           {"", kMethod3Return1}});
   // Signals generate helper methods to send them.
   interface.signals.emplace_back(
@@ -210,8 +257,24 @@
       kProperty0Type,
       kProperty0Access);
 
+  Interface interface2;
+  interface2.name = kInterfaceName2;
+  interface2.methods.emplace_back(
+      kMethod0Name2,
+      vector<Interface::Argument>{
+          {kMethod0ArgumentName0, kMethod0Argument0}},
+      vector<Interface::Argument>{{"", kMethod0Return}});
+  interface2.methods.back().is_const = true;
+  interface2.methods.back().kind = Interface::Method::Kind::kSimple;
+  interface2.methods.emplace_back(
+      kMethod1Name2,
+      vector<Interface::Argument>{{"", kMethod1Argument1}},
+      vector<Interface::Argument>{});
+  interface2.methods.back().kind = Interface::Method::Kind::kAsync;
+
   base::FilePath output_path = temp_dir_.path().Append("output.h");
-  EXPECT_TRUE(AdaptorGenerator::GenerateAdaptor(interface, output_path));
+  EXPECT_TRUE(AdaptorGenerator::GenerateAdaptors({interface, interface2},
+                                                 output_path));
   string contents;
   EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
   // The header guards contain the (temporary) filename, so we search for
diff --git a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
index 5bf35e1..c53168c 100644
--- a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
+++ b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc
@@ -14,6 +14,10 @@
 #include "chromeos-dbus-bindings/proxy_generator.h"
 #include "chromeos-dbus-bindings/xml_interface_parser.h"
 
+using chromeos_dbus_bindings::AdaptorGenerator;
+using chromeos_dbus_bindings::MethodNameGenerator;
+using chromeos_dbus_bindings::ProxyGenerator;
+
 namespace switches {
 
 static const char kHelp[] = "help";
@@ -65,20 +69,19 @@
     std::string method_name_file =
         cl->GetSwitchValueASCII(switches::kMethodNames);
     VLOG(1) << "Outputting method names to " << method_name_file;
-    if (!chromeos_dbus_bindings::MethodNameGenerator::GenerateMethodNames(
-            parser.interface(),
+    if (!MethodNameGenerator::GenerateMethodNames(
+            parser.interfaces(),
             base::FilePath(method_name_file))) {
       LOG(ERROR) << "Failed to output method names.";
       return 1;
-     }
+    }
   }
 
   if (cl->HasSwitch(switches::kAdaptor)) {
     std::string adaptor_file = cl->GetSwitchValueASCII(switches::kAdaptor);
     VLOG(1) << "Outputting adaptor to " << adaptor_file;
-    if (!chromeos_dbus_bindings::AdaptorGenerator::GenerateAdaptor(
-            parser.interface(),
-            base::FilePath(adaptor_file))) {
+    if (!AdaptorGenerator::GenerateAdaptors(parser.interfaces(),
+                                            base::FilePath(adaptor_file))) {
       LOG(ERROR) << "Failed to output adaptor.";
       return 1;
      }
@@ -87,9 +90,8 @@
   if (cl->HasSwitch(switches::kProxy)) {
     std::string proxy_file = cl->GetSwitchValueASCII(switches::kProxy);
     LOG(INFO) << "Outputting proxy to " << proxy_file;
-    if (!chromeos_dbus_bindings::ProxyGenerator::GenerateProxy(
-            parser.interface(),
-            base::FilePath(proxy_file))) {
+    if (!ProxyGenerator::GenerateProxy(parser.interfaces(),
+                                       base::FilePath(proxy_file))) {
       LOG(ERROR) << "Failed to output proxy.";
       return 1;
      }
diff --git a/chromeos-dbus-bindings/header_generator.cc b/chromeos-dbus-bindings/header_generator.cc
index c339c13..da4bc74 100644
--- a/chromeos-dbus-bindings/header_generator.cc
+++ b/chromeos-dbus-bindings/header_generator.cc
@@ -11,6 +11,7 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
 
 #include "chromeos-dbus-bindings/indented_text.h"
 
@@ -53,6 +54,14 @@
   return true;
 }
 
+std::string HeaderGenerator::GetFullClassName(
+    const std::vector<std::string>& namespaces,
+    const std::string& class_name) {
+  std::vector<std::string> parts = namespaces;
+  parts.push_back(class_name);
+  return chromeos::string_utils::Join("::", parts);
+}
+
 // static
 bool HeaderGenerator::IsIntegralType(const string& type) {
   return type.find("::") == std::string::npos;
@@ -71,4 +80,11 @@
   return true;
 }
 
+string HeaderGenerator::GetArgName(const char* prefix,
+                                   const string& arg_name,
+                                   int arg_index) {
+  string name = arg_name.empty() ? std::to_string(arg_index) : arg_name;
+  return base::StringPrintf("%s_%s", prefix, name.c_str());
+}
+
 }  // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/header_generator.h b/chromeos-dbus-bindings/header_generator.h
index 7be8d5b..b4f521d 100644
--- a/chromeos-dbus-bindings/header_generator.h
+++ b/chromeos-dbus-bindings/header_generator.h
@@ -27,11 +27,16 @@
   static std::string GenerateHeaderGuard(const base::FilePath& output_file,
                                          const std::string& interface_name);
 
-  // Returns a vector of nesting namepsaces.
+  // Returns a vector of nesting namespaces.
   static bool GetNamespacesAndClassName(const std::string& interface_name,
                                         std::vector<std::string>* namespaces,
                                         std::string* class_name);
 
+  // Returns a fully-qualified class name like "ns1::ns2::class_name".
+  static std::string GetFullClassName(
+      const std::vector<std::string>& namespaces,
+      const std::string& class_name);
+
   // Used to decide whether the argument should be a const reference.
   static bool IsIntegralType(const std::string& type);
 
@@ -39,6 +44,13 @@
   static bool WriteTextToFile(const base::FilePath& output_file,
                               const IndentedText& text);
 
+  // Generate a name of a method/signal argument based on the name provided in
+  // the XML file. If |arg_name| is empty, it generates a name using
+  // the |arg_index| counter.
+  static std::string GetArgName(const char* prefix,
+                                const std::string& arg_name,
+                                int arg_index);
+
   static const int kScopeOffset = 1;
   static const int kBlockOffset = 2;
   static const int kLineContinuationOffset = 4;
diff --git a/chromeos-dbus-bindings/interface.h b/chromeos-dbus-bindings/interface.h
index 627dce4..214511b 100644
--- a/chromeos-dbus-bindings/interface.h
+++ b/chromeos-dbus-bindings/interface.h
@@ -18,6 +18,12 @@
     std::string type;
   };
   struct Method {
+    enum class Kind {
+      kSimple,
+      kNormal,
+      kAsync,
+      kRaw
+    };
     Method(const std::string& name_in,
            const std::vector<Argument>& input_arguments_in,
            const std::vector<Argument>& output_arguments_in)
@@ -32,6 +38,8 @@
     std::string name;
     std::vector<Argument> input_arguments;
     std::vector<Argument> output_arguments;
+    Kind kind{Kind::kNormal};
+    bool is_const{false};
   };
   struct Signal {
     Signal(const std::string& name_in,
diff --git a/chromeos-dbus-bindings/method_name_generator.cc b/chromeos-dbus-bindings/method_name_generator.cc
index 4b4154e..d35f058 100644
--- a/chromeos-dbus-bindings/method_name_generator.cc
+++ b/chromeos-dbus-bindings/method_name_generator.cc
@@ -4,16 +4,14 @@
 
 #include "chromeos-dbus-bindings/method_name_generator.h"
 
-#include <string>
-
-#include <base/file_util.h>
 #include <base/files/file_path.h>
-#include <base/logging.h>
 #include <base/strings/stringprintf.h>
 
+#include "chromeos-dbus-bindings/indented_text.h"
 #include "chromeos-dbus-bindings/interface.h"
 
 using std::string;
+using std::vector;
 
 namespace chromeos_dbus_bindings {
 
@@ -25,23 +23,23 @@
 
 // static
 bool MethodNameGenerator::GenerateMethodNames(
-    const Interface& interface,
+    const vector<Interface>& interfaces,
     const base::FilePath& output_file) {
   string contents;
-  for (const auto& method : interface.methods) {
-    contents.append(
-        base::StringPrintf("const char %s[] = \"%s\";\n",
-                           GenerateMethodNameConstant(method.name).c_str(),
-                           method.name.c_str()));
+  IndentedText text;
+  for (const auto& interface : interfaces) {
+    text.AddBlankLine();
+    text.AddLine(base::StringPrintf("namespace %s {", interface.name.c_str()));
+    for (const auto& method : interface.methods) {
+      text.AddLine(
+        base::StringPrintf("const char %s[] = \"%s\";",
+                            GenerateMethodNameConstant(method.name).c_str(),
+                            method.name.c_str()));
+    }
+    text.AddLine(base::StringPrintf("}  // namespace %s",
+                                    interface.name.c_str()));
   }
-
-  int expected_write_return = contents.size();
-  if (base::WriteFile(output_file, contents.c_str(), contents.size()) !=
-      expected_write_return) {
-    LOG(ERROR) << "Failed to write file " << output_file.value();
-    return false;
-  }
-  return true;
+  return HeaderGenerator::WriteTextToFile(output_file, text);
 }
 
 }  // namespace chromeos_dbus_bindings
diff --git a/chromeos-dbus-bindings/method_name_generator.h b/chromeos-dbus-bindings/method_name_generator.h
index 59ca890..24a3dc1 100644
--- a/chromeos-dbus-bindings/method_name_generator.h
+++ b/chromeos-dbus-bindings/method_name_generator.h
@@ -6,9 +6,12 @@
 #define CHROMEOS_DBUS_BINDINGS_METHOD_NAME_GENERATOR_H_
 
 #include <string>
+#include <vector>
 
 #include <base/macros.h>
 
+#include "chromeos-dbus-bindings/header_generator.h"
+
 namespace base {
 
 class FilePath;
@@ -19,9 +22,9 @@
 
 struct Interface;
 
-class MethodNameGenerator {
+class MethodNameGenerator : public HeaderGenerator {
  public:
-  static bool GenerateMethodNames(const Interface &interface,
+  static bool GenerateMethodNames(const std::vector<Interface>& interfaces,
                                   const base::FilePath& output_file);
   static std::string GenerateMethodNameConstant(const std::string& method_name);
 
diff --git a/chromeos-dbus-bindings/method_name_generator_unittest.cc b/chromeos-dbus-bindings/method_name_generator_unittest.cc
index dc461d6..d237350 100644
--- a/chromeos-dbus-bindings/method_name_generator_unittest.cc
+++ b/chromeos-dbus-bindings/method_name_generator_unittest.cc
@@ -5,6 +5,7 @@
 #include "chromeos-dbus-bindings/method_name_generator.h"
 
 #include <string>
+#include <vector>
 
 #include <base/file_util.h>
 #include <base/files/file_path.h>
@@ -23,10 +24,14 @@
 const char kMethodName0[] = "Zircon";
 const char kMethodName1[] = "Encrusted";
 const char kMethodName2[] = "Tweezers";
-const char kExpectedOutput[] =
-    "const char kZirconMethod[] = \"Zircon\";\n"
-    "const char kEncrustedMethod[] = \"Encrusted\";\n"
-    "const char kTweezersMethod[] = \"Tweezers\";\n";
+const char kExpectedOutput[] = R"(
+namespace MyInterface {
+const char kZirconMethod[] = "Zircon";
+const char kEncrustedMethod[] = "Encrusted";
+const char kTweezersMethod[] = "Tweezers";
+}  // namespace MyInterface
+)";
+
 }  // namespace
 
 class MethodNameGeneratorTest : public Test {
@@ -49,11 +54,13 @@
 
 TEST_F(MethodNameGeneratorTest, GnerateMethodNames) {
   Interface interface;
+  interface.name = "MyInterface";
   interface.methods.emplace_back(kMethodName0);
   interface.methods.emplace_back(kMethodName1);
   interface.methods.emplace_back(kMethodName2);
   base::FilePath output_path = temp_dir_.path().Append("output.h");
-  EXPECT_TRUE(MethodNameGenerator::GenerateMethodNames(interface, output_path));
+  EXPECT_TRUE(MethodNameGenerator::GenerateMethodNames({interface},
+                                                       output_path));
   string contents;
   EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
   EXPECT_STREQ(kExpectedOutput, contents.c_str());
diff --git a/chromeos-dbus-bindings/proxy_generator.cc b/chromeos-dbus-bindings/proxy_generator.cc
index 34b44c0..67cba22 100644
--- a/chromeos-dbus-bindings/proxy_generator.cc
+++ b/chromeos-dbus-bindings/proxy_generator.cc
@@ -6,6 +6,7 @@
 
 #include <base/logging.h>
 #include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
 
 #include "chromeos-dbus-bindings/dbus_signature.h"
 #include "chromeos-dbus-bindings/indented_text.h"
@@ -18,12 +19,22 @@
 
 // static
 bool ProxyGenerator::GenerateProxy(
-    const Interface& interface,
+    const std::vector<Interface>& interfaces,
     const base::FilePath& output_file) {
   IndentedText text;
-  text.AddLine(StringPrintf("// Automatic generation of interface for %s",
-                            interface.name.c_str()));
-  string header_guard = GenerateHeaderGuard(output_file, interface.name);
+  CHECK(!interfaces.empty()) << "At least one interface must be provided";
+  vector<string> namespaces;
+  string proxy_name;
+  CHECK(GetNamespacesAndClassName(interfaces.front().name,
+                                  &namespaces,
+                                  &proxy_name));
+  proxy_name += "Proxy";
+
+  text.AddLine("// Automatic generation of D-Bus interfaces:");
+  for (const auto& interface : interfaces) {
+    text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
+  }
+  string header_guard = GenerateHeaderGuard(output_file, proxy_name);
   text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
   text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
   text.AddLine("#include <string>");
@@ -44,24 +55,22 @@
   text.AddLine("#include <dbus/object_proxy.h>");
   text.AddBlankLine();
 
-  vector<string> namespaces;
-  string class_name;
-  CHECK(GetNamespacesAndClassName(interface.name, &namespaces, &class_name));
   for (const auto& space : namespaces) {
     text.AddLine(StringPrintf("namespace %s {", space.c_str()));
   }
   text.AddBlankLine();
 
-  string proxy_name = StringPrintf("%sProxy", class_name.c_str());
   text.AddLine(StringPrintf("class %s {", proxy_name.c_str()));
   text.AddLineWithOffset("public:", kScopeOffset);
   text.PushOffset(kBlockOffset);
-  AddMethodInterface(interface, &text);
-  AddConstructor(interface, proxy_name, &text);
+  AddSignalReceiver(interfaces, &text);
+  AddConstructor(interfaces, proxy_name, &text);
   AddDestructor(proxy_name, &text);
   AddSignalConnectedCallback(&text);
-  for (const auto& method : interface.methods) {
-    AddMethodProxy(method, interface.name, &text);
+  for (const auto& interface : interfaces) {
+    for (const auto& method : interface.methods) {
+      AddMethodProxy(method, interface.name, &text);
+    }
   }
 
   text.PopOffset();
@@ -91,9 +100,9 @@
 }
 
 // static
-void ProxyGenerator::AddConstructor(const Interface& interface,
-                                      const string& class_name,
-                                      IndentedText* text) {
+void ProxyGenerator::AddConstructor(const vector<Interface>& interfaces,
+                                    const string& class_name,
+                                    IndentedText* text) {
   IndentedText block;
   block.AddLine(StringPrintf("%s(", class_name.c_str()));
   block.PushOffset(kLineContinuationOffset);
@@ -112,26 +121,28 @@
   block.PopOffset();
   block.PopOffset();
   block.PushOffset(kBlockOffset);
-  for (const auto& signal : interface.signals) {
-    block.AddLine("chromeos::dbus_utils::ConnectToSignal(");
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine("dbus_object_proxy_,");
-    block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
-    block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
-    block.AddLine("base::Bind(");
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine(StringPrintf(
+  for (const auto& interface : interfaces) {
+    for (const auto& signal : interface.signals) {
+      block.AddLine("chromeos::dbus_utils::ConnectToSignal(");
+      block.PushOffset(kLineContinuationOffset);
+      block.AddLine("dbus_object_proxy_,");
+      block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
+      block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
+      block.AddLine("base::Bind(");
+      block.PushOffset(kLineContinuationOffset);
+      block.AddLine(StringPrintf(
         "&SignalReceiver::%s,",
         GetHandlerNameForSignal(signal.name).c_str()));
-    block.AddLine("base::Unretained(signal_receiver)),");
-    block.PopOffset();
-    block.AddLine("base::Bind(");
-    block.PushOffset(kLineContinuationOffset);
-    block.AddLine(StringPrintf(
+      block.AddLine("base::Unretained(signal_receiver)),");
+      block.PopOffset();
+      block.AddLine("base::Bind(");
+      block.PushOffset(kLineContinuationOffset);
+      block.AddLine(StringPrintf(
         "&%s::OnDBusSignalConnected,", class_name.c_str()));
-    block.AddLine("base::Unretained(this)));");
-    block.PopOffset();
-    block.PopOffset();
+      block.AddLine("base::Unretained(this)));");
+      block.PopOffset();
+      block.PopOffset();
+    }
   }
   block.PopOffset();
   block.AddLine("}");
@@ -179,35 +190,44 @@
 }
 
 // static
-void ProxyGenerator::AddMethodInterface(const Interface& interface,
-                                        IndentedText* text) {
+void ProxyGenerator::AddSignalReceiver(const vector<Interface>& interfaces,
+                                       IndentedText* text) {
   IndentedText block;
   block.AddLine("class SignalReceiver {");
   block.AddLineWithOffset("public:", kScopeOffset);
   block.PushOffset(kBlockOffset);
   DbusSignature signature;
-  for (const auto& signal : interface.signals) {
-    if (signal.arguments.empty()) {
-      block.AddLine(StringPrintf("virtual void %s() {}",
-                                 GetHandlerNameForSignal(signal.name).c_str()));
-      continue;
-    }
-    block.AddLine(StringPrintf("virtual void %s(",
-                               GetHandlerNameForSignal(signal.name).c_str()));
-    block.PushOffset(kLineContinuationOffset);
-    string last_argument;
-    vector<string> argument_types;
-    for (const auto& argument : signal.arguments) {
-      if (!last_argument.empty()) {
-         block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+  for (const auto& interface : interfaces) {
+    for (const auto& signal : interface.signals) {
+      string signal_begin = StringPrintf(
+          "virtual void %s(",
+          GetHandlerNameForSignal(signal.name).c_str());
+      string signal_end = ") {}";
+
+      if (signal.arguments.empty()) {
+        block.AddLine(signal_begin + signal_end);
+        continue;
       }
-      CHECK(signature.Parse(argument.type, &last_argument));
-      if (!IsIntegralType(last_argument)) {
-        last_argument = StringPrintf("const %s&", last_argument.c_str());
+      block.AddLine(signal_begin);
+      block.PushOffset(kLineContinuationOffset);
+      string last_argument;
+      vector<string> argument_types;
+      for (const auto& argument : signal.arguments) {
+        if (!last_argument.empty()) {
+           block.AddLine(StringPrintf("%s,", last_argument.c_str()));
+        }
+        CHECK(signature.Parse(argument.type, &last_argument));
+        if (!IsIntegralType(last_argument)) {
+          last_argument = StringPrintf("const %s&", last_argument.c_str());
+        }
+        if (!argument.name.empty()) {
+          last_argument += ' ';
+          last_argument += argument.name;
+        }
       }
+      block.AddLine(last_argument + signal_end);
+      block.PopOffset();
     }
-    block.AddLine(StringPrintf("%s) {}", last_argument.c_str()));
-    block.PopOffset();
   }
   block.PopOffset();
   block.AddLine("};");
@@ -220,21 +240,8 @@
                                     const string& interface_name,
                                     IndentedText* text) {
   IndentedText block;
-  string return_type("void");
-  bool is_void_method = true;
   DbusSignature signature;
-  if (!method.output_arguments.empty()) {
-    if (method.output_arguments.size() > 1) {
-      LOG(WARNING) << "Method " << method.name << " has "
-                   << method.output_arguments.size()
-                   << " output arguments which is unsupported.";
-      return;
-    }
-    CHECK(signature.Parse(method.output_arguments[0].type, &return_type));
-    is_void_method = false;
-  }
-  block.AddLine(StringPrintf("virtual %s %s(",
-                             return_type.c_str(), method.name.c_str()));
+  block.AddLine(StringPrintf("virtual bool %s(", method.name.c_str()));
   block.PushOffset(kLineContinuationOffset);
   vector<string> argument_names;
   int argument_number = 0;
@@ -244,15 +251,20 @@
     if (!IsIntegralType(argument_type)) {
       argument_type = StringPrintf("const %s&", argument_type.c_str());
     }
-    ++argument_number;
-    string argument_prefix(argument.name.empty() ?
-                           StringPrintf("argument%d", argument_number) :
-                           argument.name);
-    string argument_name(StringPrintf("%s_in", argument_prefix.c_str()));
+    string argument_name = GetArgName("in", argument.name, ++argument_number);
     argument_names.push_back(argument_name);
     block.AddLine(StringPrintf(
         "%s %s,", argument_type.c_str(), argument_name.c_str()));
   }
+  vector<string> out_param_names{"response.get()", "error"};
+  for (const auto& argument : method.output_arguments) {
+    string argument_type;
+    CHECK(signature.Parse(argument.type, &argument_type));
+    string argument_name = GetArgName("out", argument.name, ++argument_number);
+    out_param_names.push_back(argument_name);
+    block.AddLine(StringPrintf(
+        "%s* %s,", argument_type.c_str(), argument_name.c_str()));
+  }
   block.AddLine("chromeos::ErrorPtr* error) {");
   block.PopOffset();
   block.PushOffset(kBlockOffset);
@@ -270,21 +282,11 @@
   block.AddLine(StringPrintf("%s);", last_argument.c_str()));
   block.PopOffset();
 
-  if (!is_void_method) {
-    block.AddLine(StringPrintf("%s result{};", return_type.c_str()));
-  }
-  block.AddLine("if (!response) {");
-  block.AddLineWithOffset(StringPrintf(
-      "return%s;", is_void_method ? "" : " result"), kBlockOffset);
-  block.AddLine("}");
-  block.AddLine("chromeos::dbus_utils::ExtractMethodCallResults(");
-  block.AddLineWithOffset(StringPrintf("response.get(), error%s);",
-                                       is_void_method ? "" : ", &result"),
-                          kLineContinuationOffset);
-  if (!is_void_method) {
-    block.AddLine("return result;");
-  }
-
+  block.AddLine("return response && "
+                "chromeos::dbus_utils::ExtractMethodCallResults(");
+  block.PushOffset(kLineContinuationOffset);
+  block.AddLine(chromeos::string_utils::Join(", ", out_param_names) + ");");
+  block.PopOffset();
   block.PopOffset();
   block.AddLine("}");
 
diff --git a/chromeos-dbus-bindings/proxy_generator.h b/chromeos-dbus-bindings/proxy_generator.h
index 66dd8ba..b8f7e7b 100644
--- a/chromeos-dbus-bindings/proxy_generator.h
+++ b/chromeos-dbus-bindings/proxy_generator.h
@@ -27,14 +27,14 @@
 
 class ProxyGenerator : public HeaderGenerator {
  public:
-  static bool GenerateProxy(const Interface& interface,
+  static bool GenerateProxy(const std::vector<Interface>& interfaces,
                             const base::FilePath& output_file);
 
  private:
   friend class ProxyGeneratorTest;
 
   // Generates the constructor and destructor for the proxy.
-  static void AddConstructor(const Interface& interface,
+  static void AddConstructor(const std::vector<Interface>& interfaces,
                              const std::string& class_name,
                              IndentedText* text);
   static void AddDestructor(const std::string& class_name,
@@ -44,8 +44,8 @@
   static void AddSignalConnectedCallback(IndentedText *text);
 
   // Generates the method signatures for signal receivers.
-  static void AddMethodInterface(const Interface& interface,
-                                 IndentedText* text);
+  static void AddSignalReceiver(const std::vector<Interface>& interfaces,
+                                IndentedText* text);
 
   // Generates a native C++ method which calls a D-Bus method on the proxy.
   static void AddMethodProxy(const Interface::Method& interface,
diff --git a/chromeos-dbus-bindings/proxy_generator_unittest.cc b/chromeos-dbus-bindings/proxy_generator_unittest.cc
index 1149de9..7bb1ab9 100644
--- a/chromeos-dbus-bindings/proxy_generator_unittest.cc
+++ b/chromeos-dbus-bindings/proxy_generator_unittest.cc
@@ -23,6 +23,7 @@
 namespace {
 
 const char kInterfaceName[] = "org.chromium.TestInterface";
+const char kInterfaceName2[] = "org.chromium.TestInterface2";
 const char kMethod1Name[] = "Elements";
 const char kMethod1Return[] = "s";
 const char kMethod1Argument1[] = "s";
@@ -34,6 +35,11 @@
 const char kMethod3Name[] = "NiceWeatherForDucks";
 const char kMethod3Argument1[] = "b";
 const char kMethod4Name[] = "ExperimentNumberSix";
+const char kMethod5Name[] = "GetPersonInfo";
+const char kMethod5Argument1[] = "s";
+const char kMethod5ArgumentName1[] = "name";
+const char kMethod5Argument2[] = "i";
+const char kMethod5ArgumentName2[] = "age";
 const char kSignal1Name[] = "Closer";
 const char kSignal2Name[] = "TheCurseOfKaZar";
 const char kSignal2Argument1[] = "as";
@@ -114,68 +120,66 @@
           << object_path_.value();
     }
   }
-  virtual std::string Elements(
-      const std::string& space_walk_in,
-      const std::vector<dbus::ObjectPath>& ramblin_man_in,
+  virtual bool Elements(
+      const std::string& in_space_walk,
+      const std::vector<dbus::ObjectPath>& in_ramblin_man,
+      std::string* out_3,
       chromeos::ErrorPtr* error) {
     auto response = chromeos::dbus_utils::CallMethodAndBlock(
         dbus_object_proxy_,
         "org.chromium.TestInterface",
         "Elements",
         error,
-        space_walk_in,
-        ramblin_man_in);
-    std::string result{};
-    if (!response) {
-      return result;
-    }
-    chromeos::dbus_utils::ExtractMethodCallResults(
-        response.get(), error, &result);
-    return result;
+        in_space_walk,
+        in_ramblin_man);
+    return response && chromeos::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_3);
   }
-  virtual int64_t ReturnToPatagonia(
+  virtual bool ReturnToPatagonia(
+      int64_t* out_1,
       chromeos::ErrorPtr* error) {
     auto response = chromeos::dbus_utils::CallMethodAndBlock(
         dbus_object_proxy_,
         "org.chromium.TestInterface",
         "ReturnToPatagonia",
         error);
-    int64_t result{};
-    if (!response) {
-      return result;
-    }
-    chromeos::dbus_utils::ExtractMethodCallResults(
-        response.get(), error, &result);
-    return result;
+    return response && chromeos::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_1);
   }
-  virtual void NiceWeatherForDucks(
-      bool argument1_in,
+  virtual bool NiceWeatherForDucks(
+      bool in_1,
       chromeos::ErrorPtr* error) {
     auto response = chromeos::dbus_utils::CallMethodAndBlock(
         dbus_object_proxy_,
         "org.chromium.TestInterface",
         "NiceWeatherForDucks",
         error,
-        argument1_in);
-    if (!response) {
-      return;
-    }
-    chromeos::dbus_utils::ExtractMethodCallResults(
+        in_1);
+    return response && chromeos::dbus_utils::ExtractMethodCallResults(
         response.get(), error);
   }
-  virtual void ExperimentNumberSix(
+  virtual bool ExperimentNumberSix(
       chromeos::ErrorPtr* error) {
     auto response = chromeos::dbus_utils::CallMethodAndBlock(
         dbus_object_proxy_,
         "org.chromium.TestInterface",
         "ExperimentNumberSix",
         error);
-    if (!response) {
-      return;
-    }
-    chromeos::dbus_utils::ExtractMethodCallResults(
+    return response && chromeos::dbus_utils::ExtractMethodCallResults(
         response.get(), error);
   }
+  virtual bool GetPersonInfo(
+      std::string* out_name,
+      int32_t* out_age,
+      chromeos::ErrorPtr* error) {
+    auto response = chromeos::dbus_utils::CallMethodAndBlock(
+        dbus_object_proxy_,
+        "org.chromium.TestInterface2",
+        "GetPersonInfo",
+        error);
+    return response && chromeos::dbus_utils::ExtractMethodCallResults(
+        response.get(), error, out_name, out_age);
+  }
 
  private:
   scoped_refptr<dbus::Bus> bus_;
@@ -234,8 +238,17 @@
       vector<Interface::Argument>{
           {"", kSignal2Argument1},
           {"", kSignal2Argument2}});
+  Interface interface2;
+  interface2.name = kInterfaceName2;
+  interface2.methods.emplace_back(
+      kMethod5Name,
+      vector<Interface::Argument>{},
+      vector<Interface::Argument>{
+          {kMethod5ArgumentName1, kMethod5Argument1},
+          {kMethod5ArgumentName2, kMethod5Argument2}});
+  vector<Interface> interfaces{interface, interface2};
   base::FilePath output_path = temp_dir_.path().Append("output.h");
-  EXPECT_TRUE(ProxyGenerator::GenerateProxy(interface, output_path));
+  EXPECT_TRUE(ProxyGenerator::GenerateProxy(interfaces, output_path));
   string contents;
   EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
   // The header guards contain the (temporary) filename, so we search for
diff --git a/chromeos-dbus-bindings/xml_interface_parser.cc b/chromeos-dbus-bindings/xml_interface_parser.cc
index 7189ec4..3baef84 100644
--- a/chromeos-dbus-bindings/xml_interface_parser.cc
+++ b/chromeos-dbus-bindings/xml_interface_parser.cc
@@ -23,13 +23,27 @@
 const char XmlInterfaceParser::kNodeTag[] = "node";
 const char XmlInterfaceParser::kSignalTag[] = "signal";
 const char XmlInterfaceParser::kPropertyTag[] = "property";
+const char XmlInterfaceParser::kAnnotationTag[] = "annotation";
 const char XmlInterfaceParser::kNameAttribute[] = "name";
 const char XmlInterfaceParser::kTypeAttribute[] = "type";
+const char XmlInterfaceParser::kValueAttribute[] = "value";
 const char XmlInterfaceParser::kDirectionAttribute[] = "direction";
 const char XmlInterfaceParser::kAccessAttribute[] = "access";
 const char XmlInterfaceParser::kArgumentDirectionIn[] = "in";
 const char XmlInterfaceParser::kArgumentDirectionOut[] = "out";
 
+const char XmlInterfaceParser::kTrue[] = "true";
+const char XmlInterfaceParser::kFalse[] = "false";
+
+const char XmlInterfaceParser::kMethodConst[] =
+    "org.chromium.DBus.Method.Const";
+
+const char XmlInterfaceParser::kMethodKind[] = "org.chromium.DBus.Method.Kind";
+const char XmlInterfaceParser::kMethodKindSimple[] = "simple";
+const char XmlInterfaceParser::kMethodKindNormal[] = "normal";
+const char XmlInterfaceParser::kMethodKindAsync[] = "async";
+const char XmlInterfaceParser::kMethodKindRaw[] = "raw";
+
 bool XmlInterfaceParser::ParseXmlInterfaceFile(
     const base::FilePath& interface_file) {
   string contents;
@@ -62,36 +76,84 @@
 
 void XmlInterfaceParser::OnOpenElement(
     const string& element_name, const XmlAttributeMap& attributes) {
+  string prev_element;
+  if (!element_path_.empty())
+    prev_element = element_path_.back();
   element_path_.push_back(element_name);
-  if (element_path_ == vector<string> { kNodeTag, kInterfaceTag }) {
+  if (element_name == kNodeTag) {
+    CHECK(prev_element.empty())
+        << "Unexpected tag " << element_name << " inside " << prev_element;
+  } else if (element_name == kInterfaceTag) {
+    CHECK_EQ(kNodeTag, prev_element)
+        << "Unexpected tag " << element_name << " inside " << prev_element;
     string interface_name = GetValidatedElementName(attributes, kInterfaceTag);
-    CHECK(interface_.name.empty())
-        << "Found a second interface named " << interface_name << ". "
-        << "Interface " << interface_.name << " has already been parsed.";
-    interface_.name = interface_name;
-  } else if (element_path_ == vector<string> {
-                 kNodeTag, kInterfaceTag, kMethodTag }) {
-    interface_.methods.push_back(
+    interfaces_.emplace_back(interface_name,
+                             std::vector<Interface::Method>{},
+                             std::vector<Interface::Signal>{},
+                             std::vector<Interface::Property>{});
+  } else if (element_name == kMethodTag) {
+    CHECK_EQ(kInterfaceTag, prev_element)
+        << "Unexpected tag " << element_name << " inside " << prev_element;
+    interfaces_.back().methods.push_back(
         Interface::Method(GetValidatedElementName(attributes, kMethodTag)));
-  } else if (element_path_ == vector<string> {
-                 kNodeTag, kInterfaceTag, kMethodTag, kArgumentTag }) {
-    AddMethodArgument(attributes);
-  } else if (element_path_ == vector<string> {
-                 kNodeTag, kInterfaceTag, kSignalTag }) {
-    interface_.signals.push_back(
+  } else if (element_name == kSignalTag) {
+    CHECK_EQ(kInterfaceTag, prev_element)
+        << "Unexpected tag " << element_name << " inside " << prev_element;
+    interfaces_.back().signals.push_back(
         Interface::Signal(GetValidatedElementName(attributes, kSignalTag)));
-  } else if (element_path_ == vector<string> {
-                 kNodeTag, kInterfaceTag, kSignalTag, kArgumentTag }) {
-    AddSignalArgument(attributes);
-  } else if (element_path_ == vector<string> {
-                 kNodeTag, kInterfaceTag, kPropertyTag }) {
-    interface_.properties.push_back(ParseProperty(attributes));
+  } else if (element_name == kPropertyTag) {
+    CHECK_EQ(kInterfaceTag, prev_element)
+        << "Unexpected tag " << element_name << " inside " << prev_element;
+    interfaces_.back().properties.push_back(ParseProperty(attributes));
+  } else if (element_name == kArgumentTag) {
+    if (prev_element == kMethodTag) {
+      AddMethodArgument(attributes);
+    } else if (prev_element == kSignalTag) {
+      AddSignalArgument(attributes);
+    } else {
+      LOG(FATAL) << "Unexpected tag " << element_name
+                 << " inside " << prev_element;
+    }
+  } else if (element_name == kAnnotationTag) {
+    string element_path = prev_element + " " + element_name;
+    string name = GetValidatedElementAttribute(attributes, element_path,
+                                               kNameAttribute);
+    // Value is optional. Default to empty string if omitted.
+    string value;
+    GetElementAttribute(attributes, element_path, kValueAttribute, &value);
+    if (prev_element == kInterfaceTag) {
+      // Parse interface annotations...
+    } else if (prev_element == kMethodTag) {
+      // Parse method annotations...
+      Interface::Method& method = interfaces_.back().methods.back();
+      if (name == kMethodConst) {
+        CHECK(value == kTrue || value == kFalse);
+        method.is_const = (value == kTrue);
+      } else if (name == kMethodKind) {
+        if (value == kMethodKindSimple) {
+          method.kind = Interface::Method::Kind::kSimple;
+        } else if (value == kMethodKindNormal) {
+          method.kind = Interface::Method::Kind::kNormal;
+        } else if (value == kMethodKindAsync) {
+          method.kind = Interface::Method::Kind::kAsync;
+        } else if (value == kMethodKindRaw) {
+          method.kind = Interface::Method::Kind::kRaw;
+        } else {
+          LOG(FATAL) << "Invalid method kind: " << value;
+        }
+      }
+    } else if (prev_element == kSignalTag) {
+      // Parse signal annotations...
+    } else if (prev_element == kPropertyTag) {
+      // Parse property annotations...
+    } else {
+      LOG(FATAL) << "Unexpected tag " << element_name
+                 << " inside " << prev_element;
+    }
   }
 }
 
 void XmlInterfaceParser::AddMethodArgument(const XmlAttributeMap& attributes) {
-  CHECK(!interface_.methods.empty())
-      << " we have a method argument but the interface has no methods";
   string argument_direction;
   bool is_direction_paramter_present = GetElementAttribute(
       attributes,
@@ -101,9 +163,9 @@
   vector<Interface::Argument>* argument_list = nullptr;
   if (!is_direction_paramter_present ||
       argument_direction == kArgumentDirectionIn) {
-    argument_list = &interface_.methods.back().input_arguments;
+    argument_list = &interfaces_.back().methods.back().input_arguments;
   } else if (argument_direction == kArgumentDirectionOut) {
-    argument_list = &interface_.methods.back().output_arguments;
+    argument_list = &interfaces_.back().methods.back().output_arguments;
   } else {
     LOG(FATAL) << "Unknown method argument direction " << argument_direction;
   }
@@ -111,9 +173,7 @@
 }
 
 void XmlInterfaceParser::AddSignalArgument(const XmlAttributeMap& attributes) {
-  CHECK(interface_.signals.size())
-      << " we have a signal argument but the interface has no signals";
-  interface_.signals.back().arguments.push_back(
+  interfaces_.back().signals.back().arguments.push_back(
       ParseArgument(attributes, kSignalTag));
 }
 
diff --git a/chromeos-dbus-bindings/xml_interface_parser.h b/chromeos-dbus-bindings/xml_interface_parser.h
index f890806..059f1d6 100644
--- a/chromeos-dbus-bindings/xml_interface_parser.h
+++ b/chromeos-dbus-bindings/xml_interface_parser.h
@@ -31,7 +31,7 @@
   virtual ~XmlInterfaceParser() = default;
 
   virtual bool ParseXmlInterfaceFile(const base::FilePath& interface_file);
-  const Interface& interface() const { return interface_; }
+  const std::vector<Interface>& interfaces() const { return interfaces_; }
 
  private:
   friend class XmlInterfaceParserTest;
@@ -43,17 +43,31 @@
   static const char kNodeTag[];
   static const char kSignalTag[];
   static const char kPropertyTag[];
+  static const char kAnnotationTag[];
 
   // XML attribute names.
   static const char kNameAttribute[];
   static const char kTypeAttribute[];
   static const char kDirectionAttribute[];
   static const char kAccessAttribute[];
+  static const char kValueAttribute[];
 
   // XML argument directions.
   static const char kArgumentDirectionIn[];
   static const char kArgumentDirectionOut[];
 
+  // XML annotations.
+  static const char kTrue[];
+  static const char kFalse[];
+
+  static const char kMethodConst[];
+
+  static const char kMethodKind[];
+  static const char kMethodKindSimple[];
+  static const char kMethodKindNormal[];
+  static const char kMethodKindAsync[];
+  static const char kMethodKindRaw[];
+
   // Element callbacks on |this| called by HandleElementStart() and
   // HandleElementEnd(), respectively.
   void OnOpenElement(const std::string& element_name,
@@ -78,7 +92,7 @@
       const std::string& element_type,
       const std::string& element_key);
 
-  // Calls GetValidatedElementAttribute() for for the "name" property.
+  // Calls GetValidatedElementAttribute() for the "name" property.
   static std::string GetValidatedElementName(
       const XmlAttributeMap& attributes,
       const std::string& element_type);
@@ -97,7 +111,7 @@
   static void HandleElementEnd(void* user_data, const XML_Char* element);
 
   // The output of the parse.
-  Interface interface_;
+  std::vector<Interface> interfaces_;
 
   // Tracks where in the element traversal our parse has taken us.
   std::vector<std::string> element_path_;
diff --git a/chromeos-dbus-bindings/xml_interface_parser_unittest.cc b/chromeos-dbus-bindings/xml_interface_parser_unittest.cc
index 9c61253..7f861e3 100644
--- a/chromeos-dbus-bindings/xml_interface_parser_unittest.cc
+++ b/chromeos-dbus-bindings/xml_interface_parser_unittest.cc
@@ -12,6 +12,7 @@
 #include "chromeos-dbus-bindings/interface.h"
 
 using std::string;
+using std::vector;
 using testing::Test;
 
 namespace chromeos_dbus_bindings {
@@ -20,22 +21,25 @@
 
 const char kBadInterfaceFileContents0[] = "This has no resemblance to XML";
 const char kBadInterfaceFileContents1[] = "<node>";
-const char kGoodInterfaceFileContents[] =
-    "<node>\n"
-    "  <interface name=\"fi.w1.wpa_supplicant1.Interface\">\n"
-    "    <method name=\"Scan\">\n"
-    "      <arg name=\"args\" type=\"a{sv}\" direction=\"in\"/>\n"
-    "    </method>\n"
-    "    <method name=\"GetBlob\">\n"
-    "      <arg name=\"name\" type=\"s\"/>\n"
-    "      <arg name=\"data\" type=\"ay\" direction=\"out\"/>\n"
-    "    </method>\n"
-    "    <property name=\"Capabilities\" type=\"a{sv}\" access=\"read\"/>\n"
-    "    <signal name=\"BSSRemoved\">\n"
-    "      <arg name=\"BSS\" type=\"o\"/>\n"
-    "    </signal>\n"
-    "  </interface>\n"
-    "</node>\n";
+const char kGoodInterfaceFileContents[] = R"literal_string(
+<node>
+  <interface name="fi.w1.wpa_supplicant1.Interface">
+    <method name="Scan">
+      <arg name="args" type="a{sv}" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
+    </method>
+    <method name="GetBlob">
+      <arg name="name" type="s"/>
+      <arg name="data" type="ay" direction="out"/>
+      <annotation name="org.chromium.DBus.Method.Const" value="true"/>
+    </method>
+    <property name="Capabilities" type="a{sv}" access="read"/>
+    <signal name="BSSRemoved">
+      <arg name="BSS" type="o"/>
+    </signal>
+  </interface>
+</node>
+)literal_string";
 const char kInterfaceName[] = "fi.w1.wpa_supplicant1.Interface";
 const char kScanMethod[] = "Scan";
 const char kArgsArgument[] = "args";
@@ -77,13 +81,18 @@
 
 TEST_F(XmlInterfaceParserTest, GoodInputFile) {
   EXPECT_TRUE(ParseXmlContents(kGoodInterfaceFileContents));
-  const Interface& interface = parser_.interface();
+  const vector<Interface>& interfaces = parser_.interfaces();
+  ASSERT_EQ(1, interfaces.size());
+  const Interface& interface = interfaces.back();
+
   EXPECT_EQ(kInterfaceName, interface.name);
   ASSERT_EQ(2, interface.methods.size());
   ASSERT_EQ(1, interface.signals.size());
 
   // <method name="Scan">
   EXPECT_EQ(kScanMethod, interface.methods[0].name);
+  EXPECT_EQ(Interface::Method::Kind::kAsync, interface.methods[0].kind);
+  EXPECT_FALSE(interface.methods[0].is_const);
   ASSERT_EQ(1, interface.methods[0].input_arguments.size());
 
   // <arg name="args" type="a{sv}" direction="in"/>
@@ -94,10 +103,12 @@
 
   // <method name="GetBlob">
   EXPECT_EQ(kGetBlobMethod, interface.methods[1].name);
+  EXPECT_EQ(Interface::Method::Kind::kNormal, interface.methods[1].kind);
+  EXPECT_TRUE(interface.methods[1].is_const);
   EXPECT_EQ(1, interface.methods[1].input_arguments.size());
   EXPECT_EQ(1, interface.methods[1].output_arguments.size());
 
-  // <arg name="name" type="s"/>  (direciton="in" is implicit)
+  // <arg name="name" type="s"/>  (direction="in" is implicit)
   EXPECT_EQ(kNameArgument, interface.methods[1].input_arguments[0].name);
   EXPECT_EQ(kStringType, interface.methods[1].input_arguments[0].type);