123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- // 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 <stddef.h>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/metrics/histogram.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/task_runner_util.h"
- #include "dbus/bus.h"
- #include "dbus/dbus_statistics.h"
- #include "dbus/message.h"
- #include "dbus/object_proxy.h"
- #include "dbus/property.h"
- #include "dbus/scoped_dbus_error.h"
- #include "dbus/util.h"
- namespace dbus {
- ObjectManager::Object::Object()
- : object_proxy(nullptr) {
- }
- ObjectManager::Object::~Object() = default;
- scoped_refptr<ObjectManager> ObjectManager::Create(
- Bus* bus,
- const std::string& service_name,
- const ObjectPath& object_path) {
- auto object_manager =
- base::WrapRefCounted(new ObjectManager(bus, service_name, object_path));
- // Set up a match rule and a filter function to handle PropertiesChanged
- // signals from the service. This is important to avoid any race conditions
- // that might cause us to miss PropertiesChanged signals once all objects are
- // initialized via GetManagedObjects.
- base::PostTaskAndReplyWithResult(
- bus->GetDBusTaskRunner(), FROM_HERE,
- base::BindOnce(&ObjectManager::SetupMatchRuleAndFilter, object_manager),
- base::BindOnce(&ObjectManager::OnSetupMatchRuleAndFilterComplete,
- object_manager));
- return object_manager;
- }
- ObjectManager::ObjectManager(Bus* bus,
- const std::string& service_name,
- const ObjectPath& object_path)
- : bus_(bus),
- service_name_(service_name),
- object_path_(object_path),
- setup_success_(false),
- cleanup_called_(false) {
- LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
- DVLOG(1) << "Creating ObjectManager for " << service_name_
- << " " << object_path_.value();
- DCHECK(bus_);
- bus_->AssertOnOriginThread();
- object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
- object_proxy_->SetNameOwnerChangedCallback(base::BindRepeating(
- &ObjectManager::NameOwnerChanged, weak_ptr_factory_.GetWeakPtr()));
- }
- 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);
- }
- bool ObjectManager::IsInterfaceRegisteredForTesting(
- const std::string& interface_name) const {
- return interface_map_.count(interface_name);
- }
- 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 nullptr;
- 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 nullptr;
- Object* object = iter->second;
- Object::PropertiesMap::iterator piter =
- object->properties_map.find(interface_name);
- if (piter == object->properties_map.end())
- return nullptr;
- return piter->second;
- }
- void ObjectManager::GetManagedObjects() {
- MethodCall method_call(kObjectManagerInterface,
- kObjectManagerGetManagedObjects);
- object_proxy_->CallMethod(&method_call, ObjectProxy::TIMEOUT_USE_DEFAULT,
- base::BindOnce(&ObjectManager::OnGetManagedObjects,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void ObjectManager::CleanUp() {
- DCHECK(bus_);
- bus_->AssertOnDBusThread();
- DCHECK(!cleanup_called_);
- cleanup_called_ = true;
- if (!setup_success_)
- return;
- bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
- ScopedDBusError error;
- bus_->RemoveMatch(match_rule_, error.get());
- if (error.is_set())
- LOG(ERROR) << "Failed to remove match rule: " << match_rule_;
- match_rule_.clear();
- }
- bool ObjectManager::SetupMatchRuleAndFilter() {
- DCHECK(bus_);
- DCHECK(!setup_success_);
- bus_->AssertOnDBusThread();
- if (cleanup_called_)
- return false;
- if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
- return false;
- // Try to get |service_name_owner_| from dbus if we haven't received any
- // NameOwnerChanged signals.
- if (service_name_owner_.empty()) {
- service_name_owner_ =
- bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
- }
- const std::string match_rule =
- base::StringPrintf(
- "type='signal', sender='%s', interface='%s', member='%s'",
- service_name_.c_str(),
- kPropertiesInterface,
- kPropertiesChanged);
- bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this);
- ScopedDBusError error;
- bus_->AddMatch(match_rule, error.get());
- if (error.is_set()) {
- LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule
- << "\". Got " << error.name() << ": " << error.message();
- bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
- return false;
- }
- match_rule_ = match_rule;
- setup_success_ = true;
- return true;
- }
- void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
- if (!success) {
- LOG(WARNING) << service_name_ << " " << object_path_.value()
- << ": Failed to set up match rule.";
- return;
- }
- DCHECK(bus_);
- DCHECK(object_proxy_);
- DCHECK(setup_success_);
- bus_->AssertOnOriginThread();
- // |object_proxy_| is no longer valid if the Bus was shut down before this
- // call. Don't initiate any other action from the origin thread.
- if (cleanup_called_)
- return;
- object_proxy_->ConnectToSignal(
- kObjectManagerInterface, kObjectManagerInterfacesAdded,
- base::BindRepeating(&ObjectManager::InterfacesAddedReceived,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&ObjectManager::InterfacesAddedConnected,
- weak_ptr_factory_.GetWeakPtr()));
- object_proxy_->ConnectToSignal(
- kObjectManagerInterface, kObjectManagerInterfacesRemoved,
- base::BindRepeating(&ObjectManager::InterfacesRemovedReceived,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&ObjectManager::InterfacesRemovedConnected,
- weak_ptr_factory_.GetWeakPtr()));
- if (!service_name_owner_.empty())
- GetManagedObjects();
- }
- // static
- DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection,
- DBusMessage* raw_message,
- void* user_data) {
- ObjectManager* self = reinterpret_cast<ObjectManager*>(user_data);
- return self->HandleMessage(connection, raw_message);
- }
- DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection,
- DBusMessage* raw_message) {
- DCHECK(bus_);
- bus_->AssertOnDBusThread();
- // Handle the message only if it is a signal.
- // Note that the match rule in SetupMatchRuleAndFilter() is configured to
- // only accept signals, but we check here just in case.
- if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- // raw_message will be unrefed on exit of the function. Increment the
- // reference so we can use it in Signal.
- dbus_message_ref(raw_message);
- std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
- const std::string interface = signal->GetInterface();
- const std::string member = signal->GetMember();
- statistics::AddReceivedSignal(service_name_, interface, member);
- // Handle the signal only if it is PropertiesChanged.
- // Note that the match rule in SetupMatchRuleAndFilter() is configured to
- // only accept PropertiesChanged signals, but we check here just in case.
- const std::string absolute_signal_name =
- GetAbsoluteMemberName(interface, member);
- const std::string properties_changed_signal_name =
- GetAbsoluteMemberName(kPropertiesInterface, kPropertiesChanged);
- if (absolute_signal_name != properties_changed_signal_name)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- VLOG(1) << "Signal received: " << signal->ToString();
- // Handle the signal only if it is from the service that the ObjectManager
- // instance is interested in.
- // Note that the match rule in SetupMatchRuleAndFilter() is configured to
- // only accept messages from the service name of our interest. However, the
- // service='...' filter does not work as intended. See crbug.com/507206#14
- // and #15 for details, hence it's necessary to check the sender here.
- std::string sender = signal->GetSender();
- if (service_name_owner_ != sender)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- const ObjectPath path = signal->GetPath();
- if (bus_->HasDBusThread()) {
- // Post a task to run the method in the origin thread. Transfer ownership of
- // |signal| to NotifyPropertiesChanged, which will handle the clean up.
- Signal* released_signal = signal.release();
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ObjectManager::NotifyPropertiesChanged, this,
- path, released_signal));
- } else {
- // If the D-Bus thread is not used, just call the callback on the
- // current thread. Transfer the ownership of |signal| to
- // NotifyPropertiesChanged.
- NotifyPropertiesChanged(path, signal.release());
- }
- // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
- // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- void ObjectManager::NotifyPropertiesChanged(
- const dbus::ObjectPath object_path,
- Signal* signal) {
- DCHECK(bus_);
- bus_->AssertOnOriginThread();
- NotifyPropertiesChangedHelper(object_path, signal);
- // Delete the message on the D-Bus thread. See comments in HandleMessage.
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&base::DeletePointer<Signal>, signal));
- }
- void ObjectManager::NotifyPropertiesChangedHelper(
- const dbus::ObjectPath object_path,
- Signal* signal) {
- DCHECK(bus_);
- bus_->AssertOnOriginThread();
- MessageReader reader(signal);
- std::string interface;
- if (!reader.PopString(&interface)) {
- LOG(WARNING) << "Property changed signal has wrong parameters: "
- << "expected interface name: " << signal->ToString();
- return;
- }
- PropertySet* properties = GetProperties(object_path, interface);
- if (properties)
- properties->ChangedReceived(signal);
- }
- void ObjectManager::OnGetManagedObjects(Response* response) {
- if (response != nullptr) {
- MessageReader reader(response);
- MessageReader array_reader(nullptr);
- if (!reader.PopArray(&array_reader))
- return;
- while (array_reader.HasMoreData()) {
- MessageReader dict_entry_reader(nullptr);
- 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(nullptr);
- if (!reader->PopArray(&array_reader))
- return;
- while (array_reader.HasMoreData()) {
- MessageReader dict_entry_reader(nullptr);
- 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);
- } 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);
- }
- delete piter->second;
- object->properties_map.erase(piter);
- if (object->properties_map.empty()) {
- object_map_.erase(oiter);
- delete object;
- }
- }
- void ObjectManager::UpdateServiceNameOwner(const std::string& new_owner) {
- bus_->AssertOnDBusThread();
- service_name_owner_ = new_owner;
- }
- void ObjectManager::NameOwnerChanged(const std::string& old_owner,
- const std::string& new_owner) {
- bus_->AssertOnOriginThread();
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ObjectManager::UpdateServiceNameOwner, this, 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
|