dbus: allow unregistering of exported objects

Not all objects are permanent, some are transient and if we ever
re-use the object path, we want a new instance of the exported object
to be created rather than re-use an existing one.
    
BUG=chromium-os:21320
TEST=dbus_unittests and included change to agent service provider

Change-Id: I09882bbe2f70356182ac301c4260473051333424


Review URL: http://codereview.chromium.org/9691025

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126527 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: d7361fdcda06ab78b9777c923790e0c7b71b0a06
diff --git a/dbus/bus.cc b/dbus/bus.cc
index 9d4a436..91ba252 100644
--- a/dbus/bus.cc
+++ b/dbus/bus.cc
@@ -250,6 +250,35 @@
   return exported_object.get();
 }
 
+void Bus::UnregisterExportedObject(const ObjectPath& object_path) {
+  AssertOnOriginThread();
+
+  // Remove the registered object from the table first, to allow a new
+  // GetExportedObject() call to return a new object, rather than this one.
+  ExportedObjectTable::iterator iter = exported_object_table_.find(object_path);
+  if (iter == exported_object_table_.end())
+    return;
+
+  scoped_refptr<ExportedObject> exported_object = iter->second;
+  exported_object_table_.erase(iter);
+
+  // Post the task to perform the final unregistration to the D-Bus thread.
+  // Since the registration also happens on the D-Bus thread in
+  // TryRegisterObjectPath(), and the message loop proxy we post to is a
+  // MessageLoopProxy which inherits from SequencedTaskRunner, there is a
+  // guarantee that this will happen before any future registration call.
+  PostTaskToDBusThread(FROM_HERE, base::Bind(
+      &Bus::UnregisterExportedObjectInternal,
+      this, exported_object));
+}
+
+void Bus::UnregisterExportedObjectInternal(
+    scoped_refptr<dbus::ExportedObject> exported_object) {
+  AssertOnDBusThread();
+
+  exported_object->Unregister();
+}
+
 bool Bus::Connect() {
   // dbus_bus_get_private() and dbus_bus_get() are blocking calls.
   AssertOnDBusThread();
diff --git a/dbus/bus.h b/dbus/bus.h
index 2684dcd..8e3ceea 100644
--- a/dbus/bus.h
+++ b/dbus/bus.h
@@ -226,6 +226,15 @@
   // Must be called in the origin thread.
   virtual ExportedObject* GetExportedObject(const ObjectPath& object_path);
 
+  // Unregisters the exported object for the given object path |object_path|.
+  //
+  // Getting an exported object for the same object path after this call
+  // will return a new object, method calls on any remaining copies of the
+  // previous object will not be called.
+  //
+  // Must be called in the origin thread.
+  virtual void UnregisterExportedObject(const ObjectPath& object_path);
+
   // Shuts down the bus and blocks until it's done. More specifically, this
   // function does the following:
   //
@@ -420,6 +429,10 @@
  private:
   friend class base::RefCountedThreadSafe<Bus>;
 
+  // Helper function used for UnregisterExportedObject().
+  void UnregisterExportedObjectInternal(
+      scoped_refptr<dbus::ExportedObject> exported_object);
+
   // Helper function used for ShutdownOnDBusThreadAndBlock().
   void ShutdownOnDBusThreadAndBlockInternal();
 
diff --git a/dbus/bus_unittest.cc b/dbus/bus_unittest.cc
index 4e7e8fd..4011556 100644
--- a/dbus/bus_unittest.cc
+++ b/dbus/bus_unittest.cc
@@ -108,6 +108,37 @@
   bus->ShutdownAndBlock();
 }
 
+TEST(BusTest, UnregisterExportedObject) {
+  // Start the D-Bus thread.
+  base::Thread::Options thread_options;
+  thread_options.message_loop_type = MessageLoop::TYPE_IO;
+  base::Thread dbus_thread("D-Bus thread");
+  dbus_thread.StartWithOptions(thread_options);
+
+  // Create the bus.
+  dbus::Bus::Options options;
+  options.dbus_thread_message_loop_proxy = dbus_thread.message_loop_proxy();
+  scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
+  ASSERT_FALSE(bus->shutdown_completed());
+
+  dbus::ExportedObject* object_proxy1 =
+      bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy1);
+
+  bus->UnregisterExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
+
+  // This should return a new object.
+  dbus::ExportedObject* object_proxy2 =
+      bus->GetExportedObject(dbus::ObjectPath("/org/chromium/TestObject"));
+  ASSERT_TRUE(object_proxy2);
+  EXPECT_NE(object_proxy1, object_proxy2);
+
+  // Shut down synchronously.
+  bus->ShutdownOnDBusThreadAndBlock();
+  EXPECT_TRUE(bus->shutdown_completed());
+  dbus_thread.Stop();
+}
+
 TEST(BusTest, ShutdownAndBlock) {
   dbus::Bus::Options options;
   scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);