Index: dbus/bus.cc |
=================================================================== |
--- dbus/bus.cc (revision 204929) |
+++ dbus/bus.cc (working copy) |
@@ -9,10 +9,12 @@ |
#include "base/message_loop.h" |
#include "base/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 @@ |
"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 @@ |
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 @@ |
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 @@ |
self->OnDispatchStatusChanged(connection, status); |
} |
+// static |
DBusHandlerResult Bus::OnConnectionDisconnectedFilter( |
DBusConnection* connection, |
DBusMessage* message, |
@@ -1045,11 +1208,26 @@ |
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 |