diff options
Diffstat (limited to 'dbus/bus.cc')
-rw-r--r-- | dbus/bus.cc | 180 |
1 files changed, 179 insertions, 1 deletions
diff --git a/dbus/bus.cc b/dbus/bus.cc index 1ddeeb8..c830f06 100644 --- a/dbus/bus.cc +++ b/dbus/bus.cc @@ -9,10 +9,12 @@ #include "base/message_loop.h" #include "base/message_loop/message_loop_proxy.h" #include "base/stl_util.h" +#include "base/stringprintf.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "base/time.h" #include "dbus/exported_object.h" +#include "dbus/message.h" #include "dbus/object_manager.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" @@ -27,6 +29,15 @@ const char kDisconnectedMatchRule[] = "type='signal', path='/org/freedesktop/DBus/Local'," "interface='org.freedesktop.DBus.Local', member='Disconnected'"; +// The NameOwnerChanged member in org.freedesktop.DBus +const char kNameOwnerChangedSignal[] = "NameOwnerChanged"; + +// The match rule used to filter for changes to a given service name owner. +const char kServiceNameOwnerChangeMatchRule[] = + "type='signal',interface='org.freedesktop.DBus'," + "member='NameOwnerChanged',path='/org/freedesktop/DBus'," + "sender='org.freedesktop.DBus',arg0='%s'"; + // The class is used for watching the file descriptor used for D-Bus // communication. class Watch : public base::MessagePumpLibevent::Watcher { @@ -908,6 +919,112 @@ void Bus::GetServiceOwnerInternal(const std::string& service_name, PostTaskToOriginThread(FROM_HERE, base::Bind(callback, service_owner)); } +void Bus::ListenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnOriginThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + PostTaskToDBusThread(FROM_HERE, + base::Bind(&Bus::ListenForServiceOwnerChangeInternal, + this, service_name, callback)); +} + +void Bus::ListenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnDBusThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + if (!Connect() || !SetUpAsyncOperations()) + return; + + if (service_owner_changed_listener_map_.empty()) { + bool filter_added = + AddFilterFunction(Bus::OnServiceOwnerChangedFilter, this); + DCHECK(filter_added); + } + + ServiceOwnerChangedListenerMap::iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) { + // Add a match rule for the new service name. + const std::string name_owner_changed_match_rule = + base::StringPrintf(kServiceNameOwnerChangeMatchRule, + service_name.c_str()); + ScopedDBusError error; + AddMatch(name_owner_changed_match_rule, error.get()); + if (error.is_set()) { + LOG(ERROR) << "Failed to add match rule for " << service_name + << ". Got " << error.name() << ": " << error.message(); + return; + } + + service_owner_changed_listener_map_[service_name].push_back(callback); + return; + } + + // Check if the callback has already been added. + std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + if (callbacks[i].Equals(callback)) + return; + } + callbacks.push_back(callback); +} + +void Bus::UnlistenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnOriginThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + PostTaskToDBusThread(FROM_HERE, + base::Bind(&Bus::UnlistenForServiceOwnerChangeInternal, + this, service_name, callback)); +} + +void Bus::UnlistenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnDBusThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + ServiceOwnerChangedListenerMap::iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) + return; + + std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + if (callbacks[i].Equals(callback)) { + callbacks.erase(callbacks.begin() + i); + break; // There can be only one. + } + } + if (!callbacks.empty()) + return; + + // Last callback for |service_name| has been removed, remove match rule. + const std::string name_owner_changed_match_rule = + base::StringPrintf(kServiceNameOwnerChangeMatchRule, + service_name.c_str()); + ScopedDBusError error; + RemoveMatch(name_owner_changed_match_rule, error.get()); + // And remove |service_owner_changed_listener_map_| entry. + service_owner_changed_listener_map_.erase(it); + + if (service_owner_changed_listener_map_.empty()) { + bool filter_removed = + RemoveFilterFunction(Bus::OnServiceOwnerChangedFilter, this); + DCHECK(filter_removed); + } +} + dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { AssertOnDBusThread(); @@ -1000,36 +1117,81 @@ void Bus::OnConnectionDisconnected(DBusConnection* connection) { ShutdownAndBlock(); } +void Bus::OnServiceOwnerChanged(DBusMessage* message) { + DCHECK(message); + AssertOnDBusThread(); + + // |message| will be unrefed on exit of the function. Increment the + // reference so we can use it in Signal::FromRawMessage() below. + dbus_message_ref(message); + scoped_ptr<Signal> signal(Signal::FromRawMessage(message)); + + // Confirm the validity of the NameOwnerChanged signal. + if (signal->GetMember() != kNameOwnerChangedSignal || + signal->GetInterface() != DBUS_INTERFACE_DBUS || + signal->GetSender() != DBUS_SERVICE_DBUS) { + return; + } + + MessageReader reader(signal.get()); + std::string service_name; + std::string old_owner; + std::string new_owner; + if (!reader.PopString(&service_name) || + !reader.PopString(&old_owner) || + !reader.PopString(&new_owner)) { + return; + } + + ServiceOwnerChangedListenerMap::const_iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) + return; + + const std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + PostTaskToOriginThread(FROM_HERE, + base::Bind(callbacks[i], new_owner)); + } +} + +// static dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); return self->OnAddWatch(raw_watch); } +// static void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); self->OnRemoveWatch(raw_watch); } +// static void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); self->OnToggleWatch(raw_watch); } +// static dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); return self->OnAddTimeout(raw_timeout); } +// static void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); self->OnRemoveTimeout(raw_timeout); } +// static void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); self->OnToggleTimeout(raw_timeout); } +// static void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection, DBusDispatchStatus status, void* data) { @@ -1037,6 +1199,7 @@ void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection, self->OnDispatchStatusChanged(connection, status); } +// static DBusHandlerResult Bus::OnConnectionDisconnectedFilter( DBusConnection* connection, DBusMessage* message, @@ -1045,11 +1208,26 @@ DBusHandlerResult Bus::OnConnectionDisconnectedFilter( DBUS_INTERFACE_LOCAL, kDisconnectedSignal)) { Bus* self = static_cast<Bus*>(data); - self->AssertOnDBusThread(); self->OnConnectionDisconnected(connection); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +// static +DBusHandlerResult Bus::OnServiceOwnerChangedFilter( + DBusConnection* connection, + DBusMessage* message, + void* data) { + if (dbus_message_is_signal(message, + DBUS_INTERFACE_DBUS, + kNameOwnerChangedSignal)) { + Bus* self = static_cast<Bus*>(data); + self->OnServiceOwnerChanged(message); + } + // Always return unhandled to let others, e.g. ObjectProxies, handle the same + // signal. + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + } // namespace dbus |