buffet: Add ExportedObjectManager delegate

This makes it easy to export an object manager.  We'll use this very
soon to implement the ObjectManager interface on the root Buffet
object.

BUG=chromium:359190
TEST=Unittests

Change-Id: I19d2da33b81557431c5787937c49a18e7d7bacb2
Reviewed-on: https://chromium-review.googlesource.com/196387
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
diff --git a/buffet/exported_object_manager.h b/buffet/exported_object_manager.h
new file mode 100644
index 0000000..14a0b1a
--- /dev/null
+++ b/buffet/exported_object_manager.h
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXPORTED_OBJECT_MANAGER_H_
+#define EXPORTED_OBJECT_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/exported_object.h>
+#include <dbus/object_path.h>
+
+#include "buffet/exported_property_set.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+// ExportedObjectManager is a delegate that implements the
+// org.freedesktop.DBus.ObjectManager interface on behalf of another
+// object. It handles sending signals when new interfaces are added.
+//
+// This class is very similar to the ExportedPropertySet class, except that
+// it allows objects to expose an object manager interface rather than the
+// properties interface.
+//
+//  Example usage:
+//
+//   class ExampleObjectManager {
+//    public:
+//     ExampleObjectManager(dbus::Bus* bus)
+//         : object_manager_(bus, "/my/objects/path") { }
+//
+//     void Init(const OnInitFinish& cb) { object_manager_.Init(cb); }
+//     void ClaimInterface(const dbus::ObjectPath& path,
+//                         const std::string& interface_name,
+//                         const PropertyWriter& writer) {
+//       object_manager_->ClaimInterface(...);
+//     }
+//     void ReleaseInterface(const dbus::ObjectPath& path,
+//                           const std::string& interface_name) {
+//       object_manager_->ReleaseInterface(...);
+//     }
+//
+//    private:
+//     ExportedObjectManager object_manager_;
+//   };
+//
+//   class MyObjectClaimingAnInterface {
+//    public:
+//     MyObjectClaimingAnInterface(ExampleObjectManager* object_manager)
+//       : object_manager_(object_manager) {}
+//
+//     void OnInitFinish(bool success) {
+//       if (!success) { /* handle that */ }
+//       object_manager_->ClaimInterface(
+//           my_path_, my_interface_, my_properties_.GetWriter());
+//     }
+//
+//    private:
+//     struct Properties : public ExportedPropertySet {
+//      public:
+//       /* Lots of interesting properties. */
+//     };
+//
+//     Properties my_properties_;
+//     ExampleObjectManager* object_manager_;
+//   };
+class ExportedObjectManager {
+ public:
+  // Writes a dictionary of property name to property value variants to writer.
+  typedef base::Callback<void(dbus::MessageWriter* writer)> PropertyWriter;
+  typedef base::Callback<void(bool success)> OnInitFinish;
+  typedef std::map<std::string, PropertyWriter> InterfaceProperties;
+
+  ExportedObjectManager(dbus::Bus* bus, const dbus::ObjectPath& path);
+
+  // Registers methods implementing the ObjectManager interface on the object
+  // exported on the path given in the constructor. Must be called on the
+  // origin thread.
+  void Init(const OnInitFinish& cb);
+
+  // Trigger a signal that |path| has added an interface |interface_name|
+  // with properties as given by |writer|.
+  void ClaimInterface(const dbus::ObjectPath& path,
+                      const std::string& interface_name,
+                      const PropertyWriter& writer);
+
+  // Trigger a signal that |path| has removed an interface |interface_name|.
+  void ReleaseInterface(const dbus::ObjectPath& path,
+                        const std::string& interface_name);
+
+ private:
+  void HandleGetManagedObjects(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender) const;
+
+  // Both |bus_| and |exported_object_| outlive *this.
+  dbus::Bus* const bus_;
+  dbus::ExportedObject* const exported_object_;
+  // Tracks all objects currently known to the ExportedObjectManager.
+  std::map<dbus::ObjectPath, InterfaceProperties> registered_objects_;
+
+  // We're going to register DBus callbacks that will outlive ourselves.
+  // These callbacks get scheduled on the origin thread.
+  base::WeakPtrFactory<ExportedObjectManager> weak_ptr_factory_;
+  friend class ExportedObjectManagerTest;
+  DISALLOW_COPY_AND_ASSIGN(ExportedObjectManager);
+};
+
+}  //  namespace dbus_utils
+
+}  //  namespace buffet
+
+#endif  // EXPORTED_OBJECT_MANAGER_H_