dbus: Handle NameOwnerChanged in ObjectManager

Since the ObjectManager class managers the set of object proxies and
properties structures for remote service it needs to handle the
NameOwnerChanged signal, deleting the current set of objects when a
previously owned name changes to a new owner (or is released) and
re-requesting the set of managed objects when a name is claimed by
a new owner.

BUG=346975
TEST=dbus_unittests

Review URL: https://codereview.chromium.org/177703006

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


CrOS-Libchrome-Original-Commit: 043fb8c60d2274770d4229df4f2ba61936a16c7d
diff --git a/dbus/object_manager.cc b/dbus/object_manager.cc
index 0386228..d8eb569 100644
--- a/dbus/object_manager.cc
+++ b/dbus/object_manager.cc
@@ -32,6 +32,9 @@
 
   DCHECK(bus_);
   object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
+  object_proxy_->SetNameOwnerChangedCallback(
+      base::Bind(&ObjectManager::NameOwnerChanged,
+                 weak_ptr_factory_.GetWeakPtr()));
 
   object_proxy_->ConnectToSignal(
       kObjectManagerInterface,
@@ -292,4 +295,35 @@
   }
 }
 
+void ObjectManager::NameOwnerChanged(const std::string& old_owner,
+                                     const std::string& new_owner) {
+  if (!old_owner.empty()) {
+    ObjectMap::iterator iter = object_map_.begin();
+    while (iter != object_map_.end()) {
+      ObjectMap::iterator tmp = iter;
+      ++iter;
+
+      // PropertiesMap is mutated by RemoveInterface, and also Object is
+      // destroyed; easier to collect the object path and interface names
+      // and remove them safely.
+      const dbus::ObjectPath object_path = tmp->first;
+      Object* object = tmp->second;
+      std::vector<std::string> interfaces;
+
+      for (Object::PropertiesMap::iterator piter =
+              object->properties_map.begin();
+           piter != object->properties_map.end(); ++piter)
+        interfaces.push_back(piter->first);
+
+      for (std::vector<std::string>::iterator iiter = interfaces.begin();
+           iiter != interfaces.end(); ++iiter)
+        RemoveInterface(object_path, *iiter);
+    }
+
+  }
+
+  if (!new_owner.empty())
+    GetManagedObjects();
+}
+
 }  // namespace dbus
diff --git a/dbus/object_manager.h b/dbus/object_manager.h
index 333f69e..0d3ae5d 100644
--- a/dbus/object_manager.h
+++ b/dbus/object_manager.h
@@ -273,6 +273,12 @@
   void RemoveInterface(const ObjectPath& object_path,
                        const std::string& interface_name);
 
+  // Removes all objects and interfaces from the object manager when
+  // |old_owner| is not the empty string and/or re-requests the set of managed
+  // objects when |new_owner| is not the empty string.
+  void NameOwnerChanged(const std::string& old_owner,
+                        const std::string& new_owner);
+
   Bus* bus_;
   std::string service_name_;
   ObjectPath object_path_;
diff --git a/dbus/object_manager_unittest.cc b/dbus/object_manager_unittest.cc
index 595489f..3e53095 100644
--- a/dbus/object_manager_unittest.cc
+++ b/dbus/object_manager_unittest.cc
@@ -333,4 +333,21 @@
   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
 }
 
+TEST_F(ObjectManagerTest, OwnershipLost) {
+  PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
+  WaitForRemoveObject();
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(0U, object_paths.size());
+}
+
+TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
+  PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
+  WaitForRemoveObject();
+  WaitForObject();
+
+  std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
+  ASSERT_EQ(1U, object_paths.size());
+}
+
 }  // namespace dbus