Commit 9cc40cb2 authored by keybuk@chromium.org's avatar keybuk@chromium.org

Support D-Bus Object Manager

Object Manager is a new standard D-Bus interface, closely related to
the Properties interface. It is used by BlueZ 5.x thus the need to
implement it now.

The intended use is that Chrome D-Bus Client singletons set up a link
to an object manager in their constructor and register themselves to
handle their particular interface.

BUG=220951
TEST=dbus_unittests

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190440 0039d316-1c4b-4281-b951-d872f2087c98
parent dbc220a0
......@@ -138,6 +138,10 @@ class DBusThreadManagerImpl : public DBusThreadManager {
// initialize it after the main list of clients.
power_policy_controller_.reset(
new PowerPolicyController(this, power_manager_client_.get()));
// This must be called after the list of clients so they've each had a
// chance to register with their object managers.
system_bus_->GetManagedObjects();
}
virtual ~DBusThreadManagerImpl() {
......
......@@ -13,6 +13,8 @@
#include "base/threading/thread_restrictions.h"
#include "base/time.h"
#include "dbus/exported_object.h"
#include "dbus/object_manager.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "dbus/scoped_dbus_error.h"
......@@ -322,6 +324,44 @@ void Bus::UnregisterExportedObjectInternal(
exported_object->Unregister();
}
ObjectManager* Bus::GetObjectManager(const std::string& service_name,
const ObjectPath& object_path) {
AssertOnOriginThread();
// Check if we already have the requested object manager.
const ObjectManagerTable::key_type key(service_name + object_path.value());
ObjectManagerTable::iterator iter = object_manager_table_.find(key);
if (iter != object_manager_table_.end()) {
return iter->second;
}
scoped_refptr<ObjectManager> object_manager =
new ObjectManager(this, service_name, object_path);
object_manager_table_[key] = object_manager;
return object_manager.get();
}
void Bus::RemoveObjectManager(const std::string& service_name,
const ObjectPath& object_path) {
AssertOnOriginThread();
const ObjectManagerTable::key_type key(service_name + object_path.value());
ObjectManagerTable::iterator iter = object_manager_table_.find(key);
if (iter == object_manager_table_.end())
return;
scoped_refptr<ObjectManager> object_manager = iter->second;
object_manager_table_.erase(iter);
}
void Bus::GetManagedObjects() {
for (ObjectManagerTable::iterator iter = object_manager_table_.begin();
iter != object_manager_table_.end(); ++iter) {
iter->second->GetManagedObjects();
}
}
bool Bus::Connect() {
// dbus_bus_get_private() and dbus_bus_get() are blocking calls.
AssertOnDBusThread();
......
......@@ -32,6 +32,7 @@ class Location;
namespace dbus {
class ExportedObject;
class ObjectManager;
class ObjectProxy;
// Bus is used to establish a connection with D-Bus, create object
......@@ -302,6 +303,37 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> {
// Must be called in the origin thread.
virtual void UnregisterExportedObject(const ObjectPath& object_path);
// Gets an object manager for the given remote object path |object_path|
// exported by the service |service_name|.
//
// Returns an existing object manager if the bus object already owns a
// matching object manager, never returns NULL.
//
// The caller must not delete the returned object, the bus retains ownership
// of all object managers.
//
// Must be called in the origin thread.
virtual ObjectManager* GetObjectManager(const std::string& service_name,
const ObjectPath& object_path);
// Unregisters the object manager for the given remote object path
// |object_path| exported by the srevice |service_name|.
//
// Getting an object manager for the same remote object after this call
// will return a new object, method calls on any remaining copies of the
// previous object are not permitted.
//
// Must be called in the origin thread.
virtual void RemoveObjectManager(const std::string& service_name,
const ObjectPath& object_path);
// Instructs all registered object managers to retrieve their set of managed
// objects from their respective remote objects. There is no need to call this
// manually, this is called automatically by the D-Bus thread manager once
// implementation classes are registered.
virtual void GetManagedObjects();
// Shuts down the bus and blocks until it's done. More specifically, this
// function does the following:
//
......@@ -608,6 +640,13 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> {
scoped_refptr<dbus::ExportedObject> > ExportedObjectTable;
ExportedObjectTable exported_object_table_;
// ObjectManagerTable is used to hold the object managers created by the
// bus object. Key is a concatenated string of service name + object path,
// like "org.chromium.TestService/org/chromium/TestObject".
typedef std::map<std::string,
scoped_refptr<dbus::ObjectManager> > ObjectManagerTable;
ObjectManagerTable object_manager_table_;
bool async_operations_set_up_;
bool shutdown_completed_;
......
......@@ -33,6 +33,8 @@
'file_descriptor.h',
'message.cc',
'message.h',
'object_manager.cc',
'object_manager.h',
'object_path.cc',
'object_path.h',
'object_proxy.cc',
......@@ -71,6 +73,8 @@
'mock_bus.h',
'mock_exported_object.cc',
'mock_exported_object.h',
'mock_object_manager.cc',
'mock_object_manager.h',
'mock_object_proxy.cc',
'mock_object_proxy.h',
],
......@@ -98,6 +102,7 @@
'end_to_end_sync_unittest.cc',
'message_unittest.cc',
'mock_unittest.cc',
'object_manager_unittest.cc',
'property_unittest.cc',
'signal_sender_verification_unittest.cc',
'string_util_unittest.cc',
......
......@@ -26,6 +26,8 @@ class MockBus : public Bus {
int options));
MOCK_METHOD1(GetExportedObject, ExportedObject*(
const ObjectPath& object_path));
MOCK_METHOD2(GetObjectManager, ObjectManager*(const std::string&,
const ObjectPath&));
MOCK_METHOD0(ShutdownAndBlock, void());
MOCK_METHOD0(ShutdownOnDBusThreadAndBlock, void());
MOCK_METHOD0(Connect, bool());
......
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dbus/mock_object_manager.h"
namespace dbus {
MockObjectManager::MockObjectManager(Bus* bus,
const std::string& service_name,
const ObjectPath& object_path)
: ObjectManager(bus, service_name, object_path) {
}
MockObjectManager::~MockObjectManager() {
}
} // namespace dbus
// Copyright (c) 2013 The Chromium 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 DBUS_MOCK_OBJECT_MANAGER_H_
#define DBUS_MOCK_OBJECT_MANAGER_H_
#include <string>
#include "dbus/message.h"
#include "dbus/object_manager.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace dbus {
// Mock for ObjectManager.
class MockObjectManager : public ObjectManager {
public:
MockObjectManager(Bus* bus,
const std::string& service_name,
const ObjectPath& object_path);
MOCK_METHOD2(RegisterInterface, void(const std::string&,
Interface*));
MOCK_METHOD1(UnregisterInterface, void(const std::string&));
MOCK_METHOD0(GetObjects, std::vector<ObjectPath>());
MOCK_METHOD1(GetObjectsWithInterface,
std::vector<ObjectPath>(const std::string&));
MOCK_METHOD1(GetObjectProxy, ObjectProxy*(const ObjectPath&));
MOCK_METHOD2(GetProperties, PropertySet*(const ObjectPath&,
const std::string&));
MOCK_METHOD0(GetManagedObjects, void());
protected:
virtual ~MockObjectManager();
};
} // namespace dbus
#endif // DBUS_MOCK_OBJECT_MANAGER_H_
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "dbus/object_manager.h"
#include "base/bind.h"
#include "base/logging.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "dbus/property.h"
namespace dbus {
ObjectManager::Object::Object()
: object_proxy(NULL) {
}
ObjectManager::Object::~Object() {
}
ObjectManager::ObjectManager(Bus* bus,
const std::string& service_name,
const ObjectPath& object_path)
: bus_(bus),
service_name_(service_name),
object_path_(object_path),
weak_ptr_factory_(this) {
DVLOG(1) << "Creating ObjectManager for " << service_name_
<< " " << object_path_.value();
DCHECK(bus_);
object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
object_proxy_->ConnectToSignal(
kObjectManagerInterface,
kObjectManagerInterfacesAdded,
base::Bind(&ObjectManager::InterfacesAddedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&ObjectManager::InterfacesAddedConnected,
weak_ptr_factory_.GetWeakPtr()));
object_proxy_->ConnectToSignal(
kObjectManagerInterface,
kObjectManagerInterfacesRemoved,
base::Bind(&ObjectManager::InterfacesRemovedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&ObjectManager::InterfacesRemovedConnected,
weak_ptr_factory_.GetWeakPtr()));
GetManagedObjects();
}
ObjectManager::~ObjectManager() {
// Clean up Object structures
for (ObjectMap::iterator iter = object_map_.begin();
iter != object_map_.end(); ++iter) {
Object* object = iter->second;
for (Object::PropertiesMap::iterator piter = object->properties_map.begin();
piter != object->properties_map.end(); ++piter) {
PropertySet* properties = piter->second;
delete properties;
}
delete object;
}
}
void ObjectManager::RegisterInterface(const std::string& interface_name,
Interface* interface) {
interface_map_[interface_name] = interface;
}
void ObjectManager::UnregisterInterface(const std::string& interface_name) {
InterfaceMap::iterator iter = interface_map_.find(interface_name);
if (iter != interface_map_.end())
interface_map_.erase(iter);
}
std::vector<ObjectPath> ObjectManager::GetObjects() {
std::vector<ObjectPath> object_paths;
for (ObjectMap::iterator iter = object_map_.begin();
iter != object_map_.end(); ++iter)
object_paths.push_back(iter->first);
return object_paths;
}
std::vector<ObjectPath> ObjectManager::GetObjectsWithInterface(
const std::string& interface_name) {
std::vector<ObjectPath> object_paths;
for (ObjectMap::iterator oiter = object_map_.begin();
oiter != object_map_.end(); ++oiter) {
Object* object = oiter->second;
Object::PropertiesMap::iterator piter =
object->properties_map.find(interface_name);
if (piter != object->properties_map.end())
object_paths.push_back(oiter->first);
}
return object_paths;
}
ObjectProxy* ObjectManager::GetObjectProxy(const ObjectPath& object_path) {
ObjectMap::iterator iter = object_map_.find(object_path);
if (iter == object_map_.end())
return NULL;
Object* object = iter->second;
return object->object_proxy;
}
PropertySet* ObjectManager::GetProperties(const ObjectPath& object_path,
const std::string& interface_name) {
ObjectMap::iterator iter = object_map_.find(object_path);
if (iter == object_map_.end())
return NULL;
Object* object = iter->second;
Object::PropertiesMap::iterator piter =
object->properties_map.find(interface_name);
if (piter == object->properties_map.end())
return NULL;
return piter->second;
}
void ObjectManager::GetManagedObjects() {
MethodCall method_call(kObjectManagerInterface,
kObjectManagerGetManagedObjects);
object_proxy_->CallMethod(
&method_call,
ObjectProxy::TIMEOUT_USE_DEFAULT,
base::Bind(&ObjectManager::OnGetManagedObjects,
weak_ptr_factory_.GetWeakPtr()));
}
void ObjectManager::OnGetManagedObjects(Response* response) {
if (response != NULL) {
MessageReader reader(response);
MessageReader array_reader(NULL);
if (!reader.PopArray(&array_reader))
return;
while (array_reader.HasMoreData()) {
MessageReader dict_entry_reader(NULL);
ObjectPath object_path;
if (!array_reader.PopDictEntry(&dict_entry_reader) ||
!dict_entry_reader.PopObjectPath(&object_path))
continue;
UpdateObject(object_path, &dict_entry_reader);
}
} else {
LOG(WARNING) << service_name_ << " " << object_path_.value()
<< ": Failed to get managed objects";
}
}
void ObjectManager::InterfacesAddedReceived(Signal* signal) {
DCHECK(signal);
MessageReader reader(signal);
ObjectPath object_path;
if (!reader.PopObjectPath(&object_path)) {
LOG(WARNING) << service_name_ << " " << object_path_.value()
<< ": InterfacesAdded signal has incorrect parameters: "
<< signal->ToString();
return;
}
UpdateObject(object_path, &reader);
}
void ObjectManager::InterfacesAddedConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
<< ": Failed to connect to InterfacesAdded signal.";
}
void ObjectManager::InterfacesRemovedReceived(Signal* signal) {
DCHECK(signal);
MessageReader reader(signal);
ObjectPath object_path;
std::vector<std::string> interface_names;
if (!reader.PopObjectPath(&object_path) ||
!reader.PopArrayOfStrings(&interface_names)) {
LOG(WARNING) << service_name_ << " " << object_path_.value()
<< ": InterfacesRemoved signal has incorrect parameters: "
<< signal->ToString();
return;
}
for (size_t i = 0; i < interface_names.size(); ++i)
RemoveInterface(object_path, interface_names[i]);
}
void ObjectManager::InterfacesRemovedConnected(
const std::string& interface_name,
const std::string& signal_name,
bool success) {
LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
<< ": Failed to connect to "
<< "InterfacesRemoved signal.";
}
void ObjectManager::UpdateObject(const ObjectPath& object_path,
MessageReader* reader) {
DCHECK(reader);
MessageReader array_reader(NULL);
if (!reader->PopArray(&array_reader))
return;
while (array_reader.HasMoreData()) {
MessageReader dict_entry_reader(NULL);
std::string interface_name;
if (!array_reader.PopDictEntry(&dict_entry_reader) ||
!dict_entry_reader.PopString(&interface_name))
continue;
AddInterface(object_path, interface_name, &dict_entry_reader);
}
}
void ObjectManager::AddInterface(const ObjectPath& object_path,
const std::string& interface_name,
MessageReader* reader) {
InterfaceMap::iterator iiter = interface_map_.find(interface_name);
if (iiter == interface_map_.end())
return;
Interface* interface = iiter->second;
ObjectMap::iterator oiter = object_map_.find(object_path);
Object* object;
if (oiter == object_map_.end()) {
object = object_map_[object_path] = new Object;
object->object_proxy = bus_->GetObjectProxy(service_name_, object_path);
} else
object = oiter->second;
Object::PropertiesMap::iterator piter =
object->properties_map.find(interface_name);
PropertySet* property_set;
const bool interface_added = (piter == object->properties_map.end());
if (interface_added) {
property_set = object->properties_map[interface_name] =
interface->CreateProperties(object->object_proxy,
object_path, interface_name);
property_set->ConnectSignals();
} else
property_set = piter->second;
property_set->UpdatePropertiesFromReader(reader);
if (interface_added)
interface->ObjectAdded(object_path, interface_name);
}
void ObjectManager::RemoveInterface(const ObjectPath& object_path,
const std::string& interface_name) {
ObjectMap::iterator oiter = object_map_.find(object_path);
if (oiter == object_map_.end())
return;
Object* object = oiter->second;
Object::PropertiesMap::iterator piter =
object->properties_map.find(interface_name);
if (piter == object->properties_map.end())
return;
// Inform the interface before removing the properties structure or object
// in case it needs details from them to make its own decisions.
InterfaceMap::iterator iiter = interface_map_.find(interface_name);
if (iiter != interface_map_.end()) {
Interface* interface = iiter->second;
interface->ObjectRemoved(object_path, interface_name);
}
object->properties_map.erase(piter);
if (object->properties_map.empty()) {
object_map_.erase(oiter);
delete object;
}
}
} // namespace dbus
This diff is collapsed.
This diff is collapsed.
......@@ -35,7 +35,7 @@ class PropertyTest : public testing::Test {
Properties(dbus::ObjectProxy* object_proxy,
PropertyChangedCallback property_changed_callback)
: dbus::PropertySet(object_proxy,
"org.chromium.TestService",
"org.chromium.TestInterface",
property_changed_callback) {
RegisterProperty("Name", &name);
RegisterProperty("Version", &version);
......
This diff is collapsed.
......@@ -19,6 +19,7 @@ namespace dbus {
class Bus;
class MethodCall;
class MessageWriter;
class Response;
// The test service is used for end-to-end tests. The service runs in a
......@@ -134,6 +135,25 @@ class TestService : public base::Thread {
void SetProperty(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Performs an action for testing.
void PerformAction(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Object Manager: returns the set of objects and properties.
void GetManagedObjects(MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Add a properties dictionary to a message writer.
void AddPropertiesToWriter(MessageWriter* writer);
// Add a new object to the manager.
void AddObject(const dbus::ObjectPath& object_path);
void AddObjectInternal(const dbus::ObjectPath& object_path);
// Remove an object from the manager.
void RemoveObject(const dbus::ObjectPath& object_path);
void RemoveObjectInternal(const dbus::ObjectPath& object_path);
// Sends a property changed signal for the name property.
void SendPropertyChangedSignal(const std::string& name);
......@@ -153,6 +173,7 @@ class TestService : public base::Thread {
scoped_refptr<Bus> bus_;
ExportedObject* exported_object_;
ExportedObject* exported_object_manager_;
};
} // namespace dbus
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment