buffet: Add ObjectManager interface to /org/chromium/Buffet

BUG=chromium:359190
TEST=`buffet_client GetManagedObjects` returns
message_type: MESSAGE_METHOD_RETURN
destination: :1.35
sender: :1.31
signature: a{oa{sa{sv}}}
serial: 10
reply_serial: 3

array [
  dict entry {
    object_path "/org/chromium/Buffet/Manager"
    array [
      dict entry {
        string "org.chromium.Buffet.Manager"
        array [
          dict entry {
            string "State"
            variant               string "{}"
          }
        ]
      }
    ]
  }
]

Done.

Change-Id: I2bbcc9a3f71c7ec6ab76cb4600dad7efe1a8bb0a
Reviewed-on: https://chromium-review.googlesource.com/198963
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Ilja Friedel <ihf@chromium.org>
diff --git a/buffet/async_event_sequencer.cc b/buffet/async_event_sequencer.cc
index bf08d14..1dca62c 100644
--- a/buffet/async_event_sequencer.cc
+++ b/buffet/async_event_sequencer.cc
@@ -40,6 +40,16 @@
   PossiblyRunCompletionActions();
 }
 
+namespace {
+void IgnoreSuccess(const AsyncEventSequencer::CompletionTask& task,
+                   bool /*success*/) { task.Run(); }
+}  // namespace
+
+AsyncEventSequencer::CompletionAction AsyncEventSequencer::WrapCompletionTask(
+    const CompletionTask& task) {
+  return base::Bind(&IgnoreSuccess, task);
+}
+
 void AsyncEventSequencer::HandleFinish(int registration_number,
                                        const std::string& error_message,
                                        bool failure_is_fatal, bool success) {
diff --git a/buffet/async_event_sequencer.h b/buffet/async_event_sequencer.h
index ab5bc1a..96adad7 100644
--- a/buffet/async_event_sequencer.h
+++ b/buffet/async_event_sequencer.h
@@ -41,24 +41,32 @@
                                const std::string& method_name,
                                bool success)> ExportHandler;
   typedef base::Callback<void(bool all_succeeded)> CompletionAction;
+  typedef base::Callback<void(void)> CompletionTask;
 
   AsyncEventSequencer();
+
   // Get a Finished handler callback.  Each callback is "unique" in the sense
   // that subsequent calls to GetHandler() will create new handlers
   // which will need to be called before completion actions are run.
   Handler GetHandler(const std::string& descriptive_message,
                      bool failure_is_fatal);
+
   // Like GetHandler except with a signature tailored to
   // ExportedObject's ExportMethod callback requirements.  Will also assert
   // that the passed interface/method names from ExportedObject are correct.
   ExportHandler GetExportHandler(
       const std::string& interface_name, const std::string& method_name,
       const std::string& descriptive_message, bool failure_is_fatal);
+
   // Once all handlers obtained via GetHandler have run,
   // we'll run each CompletionAction, then discard our references.
   // No more handlers may be obtained after this call.
   void OnAllTasksCompletedCall(std::vector<CompletionAction> actions);
 
+  // Wrap a CompletionTask with a function that discards the result.
+  // This CompletionTask retains no references to the AsyncEventSequencer.
+  CompletionAction WrapCompletionTask(const CompletionTask& task);
+
  private:
   // We'll partially bind this function before giving it back via
   // GetHandler.  Note that the returned callbacks have
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
index 34eb89f..a763e42 100644
--- a/buffet/buffet_client.cc
+++ b/buffet/buffet_client.cc
@@ -14,6 +14,7 @@
 #include <dbus/bus.h>
 #include <dbus/message.h>
 #include <dbus/object_proxy.h>
+#include <dbus/object_manager.h>
 #include <dbus/values_util.h>
 
 #include "buffet/dbus_constants.h"
@@ -34,6 +35,7 @@
   std::cerr << "  " << kManagerFinishRegisterDevice
                     << " device_id" << std::endl;
   std::cerr << "  " << kManagerUpdateStateMethod << std::endl;
+  std::cerr << "  " << dbus::kObjectManagerGetManagedObjects << std::endl;
 }
 
 class BuffetHelperProxy {
@@ -45,6 +47,9 @@
     manager_proxy_ = bus_->GetObjectProxy(
         kServiceName,
         dbus::ObjectPath(kManagerServicePath));
+    root_proxy_ = bus_->GetObjectProxy(
+        kServiceName,
+        dbus::ObjectPath(kRootServicePath));
     return EX_OK;
   }
 
@@ -226,9 +231,29 @@
     return EX_OK;
   }
 
+  int CallRootGetManagedObjects(const CommandLine::StringVector& args) {
+    if (!args.empty()) {
+      std::cerr << "Invalid number of arguments for "
+                << dbus::kObjectManagerGetManagedObjects << std::endl;
+      usage();
+      return EX_USAGE;
+    }
+    dbus::MethodCall method_call(
+        dbus::kObjectManagerInterface, dbus::kObjectManagerGetManagedObjects);
+    scoped_ptr<dbus::Response> response(
+        root_proxy_->CallMethodAndBlock(&method_call, default_timeout_ms));
+    if (!response) {
+      std::cout << "Failed to receive a response." << std::endl;
+      return EX_UNAVAILABLE;
+    }
+    std::cout << response->ToString() << std::endl;
+    return EX_OK;
+  }
+
  private:
   scoped_refptr<dbus::Bus> bus_;
   dbus::ObjectProxy* manager_proxy_{nullptr};  // NOLINT - initializer list
+  dbus::ObjectProxy* root_proxy_{nullptr};  // NOLINT - initializer list
 };
 
 }  // namespace
@@ -270,6 +295,8 @@
   } else if (command.compare(kManagerUpdateStateMethod) == 0 ||
              command.compare("us") == 0) {
     err = helper.CallManagerUpdateState(args);
+  } else if (command.compare(dbus::kObjectManagerGetManagedObjects) == 0) {
+    err = helper.CallRootGetManagedObjects(args);
   } else {
     std::cerr << "Unknown command: " << command << std::endl;
     usage();
diff --git a/buffet/exported_object_manager.cc b/buffet/exported_object_manager.cc
index 6458128..c1403ad 100644
--- a/buffet/exported_object_manager.cc
+++ b/buffet/exported_object_manager.cc
@@ -12,10 +12,9 @@
 
 namespace dbus_utils {
 
-ExportedObjectManager::ExportedObjectManager(dbus::Bus* bus,
+ExportedObjectManager::ExportedObjectManager(scoped_refptr<dbus::Bus> bus,
                                              const dbus::ObjectPath& path)
-    : bus_(bus), exported_object_(bus->GetExportedObject(path)),
-      weak_ptr_factory_(this) {}
+    : bus_(bus), exported_object_(bus->GetExportedObject(path)) {}
 
 void ExportedObjectManager::Init(const OnInitFinish& cb) {
   bus_->AssertOnOriginThread();
@@ -25,12 +24,13 @@
       dbus::kObjectManagerInterface,
       dbus::kObjectManagerGetManagedObjects,
       base::Bind(&ExportedObjectManager::HandleGetManagedObjects,
-                 weak_ptr_factory_.GetWeakPtr()),
+                 AsWeakPtr()),
       sequencer->GetExportHandler(
           dbus::kObjectManagerInterface,
           dbus::kObjectManagerGetManagedObjects,
           "Failed exporting GetManagedObjects method of ObjectManager",
           false));
+  sequencer->OnAllTasksCompletedCall({cb});
 }
 
 void ExportedObjectManager::ClaimInterface(
diff --git a/buffet/exported_object_manager.h b/buffet/exported_object_manager.h
index e1d8884..d483db0 100644
--- a/buffet/exported_object_manager.h
+++ b/buffet/exported_object_manager.h
@@ -69,14 +69,16 @@
 //     Properties my_properties_;
 //     ExampleObjectManager* object_manager_;
 //   };
-class ExportedObjectManager {
+class ExportedObjectManager
+    : public base::SupportsWeakPtr<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);
+  ExportedObjectManager(scoped_refptr<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
@@ -104,9 +106,6 @@
   // 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);
 };
diff --git a/buffet/exported_property_set.cc b/buffet/exported_property_set.cc
index ba6b2c7..b1b5633 100644
--- a/buffet/exported_property_set.cc
+++ b/buffet/exported_property_set.cc
@@ -51,7 +51,12 @@
   sequencer->OnAllTasksCompletedCall({cb});
 }
 
-ExportedPropertySet::~ExportedPropertySet() { }
+ExportedPropertySet::PropertyWriter ExportedPropertySet::GetPropertyWriter(
+    const std::string& interface) {
+  return base::Bind(&ExportedPropertySet::WritePropertiesDictToMessage,
+                    weak_ptr_factory_.GetWeakPtr(),
+                    interface);
+}
 
 void ExportedPropertySet::RegisterProperty(
     const std::string& interface_name,
diff --git a/buffet/exported_property_set.h b/buffet/exported_property_set.h
index 3d7abc5..405ed7d 100644
--- a/buffet/exported_property_set.h
+++ b/buffet/exported_property_set.h
@@ -95,9 +95,10 @@
 class ExportedPropertySet {
  public:
   typedef base::Callback<void(bool success)> OnInitFinish;
+  typedef base::Callback<void(dbus::MessageWriter* writer)> PropertyWriter;
 
   ExportedPropertySet(dbus::Bus* bus, const dbus::ObjectPath& path);
-  virtual ~ExportedPropertySet();
+  virtual ~ExportedPropertySet() = default;
 
   // Claims the method associated with the org.freedesktop.DBus.Properties
   // interface.  This needs to be done after all properties are initialized to
@@ -105,8 +106,11 @@
   // are exported to the DBus object.  |cb| will be called on the origin
   // thread.
   void Init(const OnInitFinish& cb);
-  base::Callback<void(dbus::MessageWriter* writer)> GetPropertyWriter(
-      const std::string& interface);
+
+  // Return a callback that knows how to write this property set's properties
+  // to a message.  This writer retains a weak pointer to this, and must
+  // only be invoked on the same thread as the rest of ExportedPropertySet.
+  PropertyWriter GetPropertyWriter(const std::string& interface);
 
  protected:
   void RegisterProperty(const std::string& interface_name,
diff --git a/buffet/main.cc b/buffet/main.cc
index e48a1b0..43ba4ab 100644
--- a/buffet/main.cc
+++ b/buffet/main.cc
@@ -16,9 +16,12 @@
 #include <sysexits.h>
 
 #include "buffet/async_event_sequencer.h"
+#include "buffet/dbus_constants.h"
+#include "buffet/exported_object_manager.h"
 #include "buffet/manager.h"
 
 using buffet::dbus_utils::AsyncEventSequencer;
+using buffet::dbus_utils::ExportedObjectManager;
 
 namespace {
 
@@ -87,7 +90,11 @@
 void EnterMainLoop(base::MessageLoopForIO* message_loop,
                    scoped_refptr<dbus::Bus> bus) {
   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
-  buffet::Manager manager(bus.get());
+  ExportedObjectManager object_manager(
+      bus, dbus::ObjectPath(buffet::dbus_constants::kRootServicePath));
+  buffet::Manager manager(bus, object_manager.AsWeakPtr());
+  object_manager.Init(
+      sequencer->GetHandler("ObjectManager.Init() failed.", true));
   manager.Init(sequencer->GetHandler("Manager.Init() failed.", true));
   sequencer->OnAllTasksCompletedCall(
       {base::Bind(&TakeServiceOwnership, bus)});
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 09985b3..3639803 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -18,18 +18,26 @@
 #include "buffet/dbus_constants.h"
 #include "buffet/dbus_utils.h"
 #include "buffet/error.h"
+#include "buffet/exported_object_manager.h"
 
+using buffet::dbus_utils::AsyncEventSequencer;
 using buffet::dbus_utils::GetBadArgsError;
 using buffet::dbus_utils::GetDBusError;
 
 namespace buffet {
 
-Manager::Manager(dbus::Bus* bus)
+Manager::Manager(
+    scoped_refptr<dbus::Bus> bus,
+    base::WeakPtr<dbus_utils::ExportedObjectManager> object_manager)
     : bus_(bus),
       exported_object_(bus->GetExportedObject(
-          dbus::ObjectPath(dbus_constants::kManagerServicePath))) { }
+          dbus::ObjectPath(dbus_constants::kManagerServicePath))),
+      object_manager_(object_manager) { }
 
 Manager::~Manager() {
+  object_manager_->ReleaseInterface(
+      dbus::ObjectPath(dbus_constants::kManagerServicePath),
+      dbus_constants::kManagerInterface);
   // Prevent the properties object from making calls to the exported object.
   properties_.reset(nullptr);
   // Unregister ourselves from the Bus.  This prevents the bus from calling
@@ -41,8 +49,8 @@
 }
 
 void Manager::Init(const OnInitFinish& cb) {
-  scoped_refptr<dbus_utils::AsyncEventSequencer> sequencer(
-      new dbus_utils::AsyncEventSequencer());
+  scoped_refptr<AsyncEventSequencer> sequencer(
+      new AsyncEventSequencer());
   exported_object_->ExportMethod(
       dbus_constants::kManagerInterface,
       dbus_constants::kManagerCheckDeviceRegistered,
@@ -112,7 +120,14 @@
   properties_->state_.SetValue("{}");
   properties_->Init(
       sequencer->GetHandler("Manager properties export failed.", true));
-  sequencer->OnAllTasksCompletedCall({cb});
+  auto claim_interface_task = sequencer->WrapCompletionTask(
+      base::Bind(&dbus_utils::ExportedObjectManager::ClaimInterface,
+                 object_manager_->AsWeakPtr(),
+                 dbus::ObjectPath(dbus_constants::kManagerServicePath),
+                 dbus_constants::kManagerInterface,
+                 properties_->GetPropertyWriter(
+                     dbus_constants::kManagerInterface)));
+  sequencer->OnAllTasksCompletedCall({claim_interface_task, cb});
   device_info_.Load();
 }
 
diff --git a/buffet/manager.h b/buffet/manager.h
index a508d81..4498c50 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -10,6 +10,7 @@
 
 #include <base/basictypes.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
 #include <base/values.h>
 #include <dbus/message.h>
 #include <dbus/object_path.h>
@@ -20,7 +21,9 @@
 
 namespace buffet {
 
-class DBusManager;
+namespace dbus_utils {
+class ExportedObjectManager;
+}  // namespace dbus_utils
 
 // The Manager is responsible for global state of Buffet.  It exposes
 // interfaces which affect the entire device such as device registration and
@@ -29,7 +32,8 @@
  public:
   typedef base::Callback<void(bool success)> OnInitFinish;
 
-  explicit Manager(dbus::Bus* bus);
+  Manager(scoped_refptr<dbus::Bus> bus,
+          base::WeakPtr<dbus_utils::ExportedObjectManager> object_manager);
   ~Manager();
   void Init(const OnInitFinish& cb);
 
@@ -64,8 +68,9 @@
   scoped_ptr<::dbus::Response> HandleTestMethod(
       ::dbus::MethodCall* method_call);
 
-  dbus::Bus* bus_;
+  scoped_refptr<dbus::Bus> bus_;
   dbus::ExportedObject* exported_object_;  // weak; owned by the Bus object.
+  base::WeakPtr<dbus_utils::ExportedObjectManager> object_manager_;
   scoped_ptr<Properties> properties_;
 
   DeviceRegistrationInfo device_info_;