chromeos-dbus-bindings: add README

Using this library can be confusing because of the code generation,
so here's some documentation to explain what the code generator is
doing.

BUG=chromium:719077
TEST=md_browser

Change-Id: I5607360283685df694022d214253f48689c412d2
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2f14781
--- /dev/null
+++ b/README.md
@@ -0,0 +1,317 @@
+# chromeos-dbus-bindings
+
+**chromeos-dbus-bindings** was created to supplement [libbrillo] and
+simplify the implementation of [D-Bus] daemons and proxies. It generates C++
+classes from the XML specifications of the D-Bus interface. Instead of dealing
+directly with `MethodCall` objects and unpacking the arguments manually, the
+generated bindings take care of marshalling and unmarshalling D-Bus method
+call arguments for you.
+
+## Setting up chromeos-dbus-bindings
+
+The XML format defining objects and interfaces is the same format used in
+[the introspection API]. Method and signal handlers are generated from this XML
+file. If you were using dbus-c++ before, you are probably using `xml2cpp`
+to generate C++ bindings from the XML specification. If not, you may need to
+write an XML specification.
+
+After that, you will need to set up some actions in the `gyp` file for your
+service and its users. That will look something like this in your service:
+
+```python
+{
+  'target_name': 'frobinator-adaptors',
+  'type': 'none',
+  'variables': {
+    'dbus_service_config': 'dbus_bindings/dbus-service-config.json',
+    'dbus_adaptors_out_dir': 'include/frobinator/dbus_adaptors',
+  },
+  'sources': [
+    'dbus_bindings/service.name.of.Frobinator.xml',
+  ],
+  'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+},
+```
+
+and this in users of your service (or for a client library target):
+
+```python
+{
+  'target_name': 'frobinator-proxies',
+  'type': 'none',
+  'actions': [
+    {
+      'action_name': 'generate-frobinator-proxies',
+      'variables': {
+        'proxy_output_file': 'include/frobinator/dbus-proxies.h',
+      },
+      'sources': [
+        'path/to/frobinator/dbus_bindings/service.name.of.Frobinator.xml',
+      ],
+      'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+    },
+  ],
+},
+```
+
+The JSON service configuration file will look like this:
+
+```json
+{
+  "service_name": "service.name.of.Frobinator",
+  "object_manager": {
+    "object_path": "/object/path/to/Frobinator/ObjectManager"
+  }
+}
+```
+
+Then, in your service, you can
+`#include "frobinator/dbus_adaptors/service.name.of.Frobinator.h"` to get the
+interface and adaptor classes for Frobinator, and users can
+`#include <frobinator/dbus-proxies.h>` to get the proxy classes. Try to
+follow the [best practices] doc and only export one object for your service.
+The `ObjectManager` here will be used to integrate with Brillo's D-Bus
+daemons but otherwise should not affect your implementation of the bindings.
+
+## D-Bus types vs. C++ types
+
+D-Bus methods, signals and properties have [type signatures]. When generating
+bindings, `chromeos-dbus-bindings` will map D-Bus types to C++ types like
+so:
+
+| D-Bus type signature | C++ type                      |
+| -------------------- | ----------------------------- |
+| `y`                  | `uint8_t`                     |
+| `b`                  | `bool`                        |
+| `n`                  | `int16_t`                     |
+| `q`                  | `uint16_t`                    |
+| `i`                  | `int32_t`                     |
+| `u`                  | `uint32_t`                    |
+| `x`                  | `int64_t`                     |
+| `t`                  | `uint64_t`                    |
+| `d`                  | `double`                      |
+| `s`                  | `std::string`                 |
+| `h`                  | [dbus::FileDescriptor]        |
+| `o`                  | [dbus::ObjectPath]            |
+| `v` (variant)        | [brillo::Any]                 |
+| `(TU...)`            | `std::tuple<T, U, ...>`       |
+| `aT`                 | `std::vector<T>`              |
+| `a{TU}`              | `std::map<T, U>`              |
+| `a{sv}`              | [brillo::VariantDictionary]   |
+
+This type mapping is also recursive, i.e. an argument of
+type `a{s(io)}` will be mapped to
+`std::map<std::string, std::tuple<int32_t, dbus::ObjectPath>>`.
+
+## Method generation
+
+Suppose you have a service with the following XML specification:
+
+```xml
+<node name="/org/chromium/Frobinator">
+  <interface name="org.chromium.Frobinator">
+    <method name="Frobinate">
+      <arg name="foo" type="i" direction="in" />
+      <arg name="bar" type="a{sv}" direction="in" />
+      <arg name="baz" type="s" direction="out" />
+    </method>
+  </interface>
+</node>
+```
+
+The generator will generate a class `org::chromium::FrobinatorInterface` with
+the following C++ method signature:
+
+```c++
+bool Frobinate(brillo::ErrorPtr* error,
+               int32_t foo,
+               const brillo::VariantDictionary& bar,
+               std::string* baz);
+```
+
+This method can be implemented by inheriting
+`org::chromium::FrobinatorInterface` and can be called on proxy objects of type
+`org::chromium::FrobinatorProxy`. If the method fails, it should set the `error`
+to something descriptive and return false. If an arg has direction "in" and is
+not a simple numeric type, it will be passed in as `const &`.
+
+### Annotations
+
+The bindings generator also supports several method annotations. Marking your
+methods with these will change the generated bindings.
+
+`org.chromium.DBus.Method.Kind`:
+
+* `simple`: This method will not fail and no `brillo::ErrorPtr` argument is
+  given. If it has only one "out" argument, it is treated as a normal return
+  value. Otherwise, the method returns `void` and passes "out" arguments
+  back as pointers as usual.
+* `normal`: As stated above. Returns false and sets a `brillo::ErrorPtr` on
+  failure.
+* `async`: Instead of returning "out" arguments directly, the C++ method
+  will take a [DBusMethodResponse] argument templated on the types of the
+  "out" arguments. You can pass this object around and call its methods to
+  reply later.
+* `raw`: Takes a `dbus::MethodCall` and
+  `dbus::ExportedObject::ResponseSender` object directly. Use this if you
+  need to do your own message parsing. Protos are often passed as type `ay`
+  but Chrome's D-Bus bindings have special methods to handle them, and it
+  might make sense to take the `MethodCall` directly for these.
+
+These would have the following effect on the `Frobinate` method above:
+
+| Kind annotation | C++ method signature |
+| --------------- | -------------------- |
+| `simple`        | `std::string Frobinate(int32_t foo, const brillo::VariantDictionary& bar);` |
+| `normal`        | `bool Frobinate(brillo::ErrorPtr* error, int32_t foo, const brillo::VariantDictionary& bar, std::string* baz);` |
+| `async`         | `void Frobinate(std::unique_ptr<DBusMethodResponse<std::string>> response, int32_t foo, const brillo::VariantDictionary& bar);` |
+| `raw`           | `void Frobinate(dbus::MethodCall* method_call, ResponseSender sender);` |
+
+`org.chromium.DBus.Method.Const`: "true" adds `const` to the method signature
+
+`org.chromium.DBus.Method.IncludeDBusMessage`: passes the `dbus::Message*` as
+an argument to the generated adaptor method following the `brillo::ErrorPtr*`
+or `DBusMethodResponse`
+
+`org.freedesktop.DBus.GLib.Async`: same as setting `Kind` to `async`
+
+## Signal generation
+
+Unlike methods which are exported in the `FrobinatorInterface` class, signals
+are sent from the `FrobinatorAdaptor` class and received by the
+`FrobinatorProxy` class. Thus, they look different to the service and its
+users. Suppose our service has the following XML specification:
+
+```xml
+<node name="/org/chromium/Frobinator">
+  <interface name="org.chromium.Frobinator">
+    <signal name="FrobinationCompleted">
+      <arg name="foo" type="i" direction="out" />
+      <arg name="bar" type="a{sv}" direction="out" />
+    </method>
+  </interface>
+</node>
+```
+
+Our adaptor class will have a method:
+
+```c++
+void SendFrobinationCompletedSignal(int32_t foo,
+                                    const brillo::VariantDictionary& bar);
+```
+
+and our proxy class will have a method:
+
+```c++
+void RegisterFrobinationCompletedSignalHandler(
+    const base::Callback<void(int32_t, const brillo::VariantDictionary&)>& signal_callback,
+    dbus::ObjectProxy::OnConnectedCallback on_connected_callback);
+```
+
+Calling this function will call `on_connected_callback` with whether or not the
+registration succeeded, and if it did, `signal_callback` will be called when
+the service emits this signal.
+
+## On properties
+
+As stated the [best practices] doc, avoid using D-Bus properties because they
+won't transfer well to other IPC mechanisms if we need to switch in the future.
+Instead, get and set attributes on your service by using methods, and if you
+want users to be able to listen for changes in attributes, use signals.
+
+## Integrating with `DBusServiceDaemon`
+
+[brillo::DBusServiceDaemon] is a class which abstracts away some initialization
+tasks for D-Bus services and also ensures that all methods are exported before
+the service takes its proper name on the bus. This helps prevent races where
+users fail invoking methods on a service which claimed its name too early.
+
+`DBusServiceDaemon` has a virtual method `RegisterDBusObjectsAsync` which is
+where the adaptor can set up its D-Bus object and export it. Your adaptor
+implementation can inherit from `DBusServiceDaemon`, but it's clearer just
+to use containment instead here. A simple daemon could look like this:
+
+```c++
+class DBusAdaptor : public org::chromium::FrobinatorInterface,
+                    public org::chromium::FrobinatorAdaptor {
+ public:
+  explicit DBusAdaptor(
+      brillo::dbus_utils::ExportedObjectManager* object_manager)
+    : org::chromium::FrobinatorAdaptor(this),
+      dbus_object_(object_manager,
+                   object_manager->GetBus(),
+                   dbus::ObjectPath(kFrobinatorServicePath)) {
+
+  void RegisterAsync(
+      const brillo::dbus_utils::AsyncEventSequencer::CompletionAction& cb) {
+    RegisterWithDBusObject(&dbus_object_);
+    dbus_object_.RegisterAsync(cb);
+  }
+
+  // org::chromium::FrobinatorAdaptor overrides.
+  bool Frobinate(brillo::ErrorPtr* error,
+                 int32_t foo,
+                 const brillo::VariantDictionary& bar,
+                 std::string* baz) override;
+
+ private:
+  brillo::dbus_utils::DBusObject dbus_object_;
+
+  DISALLOW_COPY_AND_ASSIGN(DBusAdaptor);
+};
+
+class FrobinatorDaemon : public brillo::DBusServiceDaemon {
+ public:
+  FrobinatorDaemon()
+    : DBusServiceDaemon(kFrobinatorServiceName,
+                        dbus::ObjectPath(kObjectManagerPath)) {}
+
+ protected:
+  void RegisterDBusObjectsAsync(
+      brillo::dbus_utils::AsyncEventSequencer* sequencer) override {
+    adaptor_.reset(new DBusAdaptor(object_manager_.get()));
+    adaptor_->RegisterAsync(sequencer->GetHandler("RegisterAsync() failed",
+                                                  true));
+  }
+
+ private:
+  std::unique_ptr<DBusAdaptor> adaptor_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrobinatorDaemon);
+};
+
+int main(int argc, char** argv) {
+  return FrobinatorDaemon().Run();
+}
+```
+
+When the `DBusServiceDaemon` is ready to register objects, it calls your
+`RegisterDBusObjectsAsync` method. Here we use the `RegisterWithDBusObject`
+method from the generated adaptor class to export the methods, and then
+call `RegisterAsync` on the `DBusObject` to grab the name and interfaces
+for the D-Bus service later. The `AsyncEventSequencer` that the base daemon
+code passes us ensures that we'll do things in the right order.
+
+Your service should now appear on the bus and you should be able to call
+methods using `dbus-send` or create `org::chromium::FrobinatorProxy` objects
+to interact with it:
+
+```c++
+dbus::Bus::Options options;
+options.bus_type = dbus::Bus::SYSTEM;
+scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
+auto frobinator = base::MakeUnique<org::chromium::FrobinatorProxy>(bus);
+frobinator.Frobinate(42, {{ "qux", brillo::Any("squawk") }});
+```
+
+[D-Bus]: https://www.freedesktop.org/wiki/Software/dbus/
+[libbrillo]: https://android.googlesource.com/platform/external/libbrillo/+/master/brillo/dbus/
+[the introspection API]: https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
+[best practices]: https://chromium.googlesource.com/chromiumos/docs/+/master/dbus_best_practices.md
+[type signatures]: https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
+[dbus::FileDescriptor]: https://android.googlesource.com/platform/external/libchrome/+/master/dbus/file_descriptor.h
+[dbus::ObjectPath]: https://cs.chromium.org/chromium/src/dbus/object_path.h
+[brillo::Any]: https://android.googlesource.com/platform/external/libbrillo/+/master/brillo/any.h
+[brillo::VariantDictionary]: https://android.googlesource.com/platform/external/libbrillo/+/master/brillo/variant_dictionary.h
+[DBusMethodResponse]: https://android.googlesource.com/platform/external/libbrillo/+/master/brillo/dbus/dbus_method_response.h
+[brillo::DBusServiceDaemon]: https://android.googlesource.com/platform/external/libbrillo/+/master/brillo/daemons/dbus_daemon.h