Index: dbus/bus.cc |
=================================================================== |
--- dbus/bus.cc (revision 203218) |
+++ 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"; |
satorux1
2013/06/03 02:26:06
I think we have this in object_proxy.cc. While you
Lei Zhang
2013/06/04 00:35:53
I can do this in a later cleanup CL. I'm trying to
satorux1
2013/06/04 01:09:06
I see. That makes sense.
|
+ |
+// 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,100 @@ |
PostTaskToOriginThread(FROM_HERE, base::Bind(callback, service_owner)); |
} |
+void Bus::ListenForServiceOwnerChange( |
+ const std::string& service_name, |
+ const GetServiceOwnerCallback& callback) { |
+ AssertOnOriginThread(); |
+ |
+ PostTaskToDBusThread(FROM_HERE, |
+ base::Bind(&Bus::ListenForServiceOwnerChangeInternal, |
+ this, service_name, callback)); |
+} |
+ |
+void Bus::ListenForServiceOwnerChangeInternal( |
+ const std::string& service_name, |
+ const GetServiceOwnerCallback& callback) { |
+ AssertOnDBusThread(); |
+ |
+ if (!Connect() || !SetUpAsyncOperations()) |
+ return; |
+ |
+ if (service_owner_changed_listener_map_.size() == 0) |
+ CHECK(AddFilterFunction(Bus::OnServiceOwnerChangedFilter, this)); |
satorux1
2013/06/03 02:26:06
CHECK -> DCHECK. CHECK is rarely used.
Lei Zhang
2013/06/04 00:35:53
Done.
|
+ |
+ 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; |
+ } |
+ |
+ it = service_owner_changed_listener_map_.insert( |
+ std::make_pair(service_name, |
+ std::vector<GetServiceOwnerCallback>())).first; |
+ } |
+ |
+ // 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(); |
+ |
+ PostTaskToDBusThread(FROM_HERE, |
+ base::Bind(&Bus::UnlistenForServiceOwnerChangeInternal, |
+ this, service_name, callback)); |
+} |
+ |
+void Bus::UnlistenForServiceOwnerChangeInternal( |
+ const std::string& service_name, |
+ const GetServiceOwnerCallback& callback) { |
+ AssertOnDBusThread(); |
+ |
+ 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.size() > 0) |
+ 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_.size() == 0) { |
+ CHECK(RemoveFilterFunction(Bus::OnServiceOwnerChangedFilter, this)); |
+ } |
+} |
+ |
dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { |
AssertOnDBusThread(); |
@@ -1000,36 +1105,82 @@ |
ShutdownAndBlock(); |
} |
+DBusHandlerResult 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 DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
+ } |
+ |
+ 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 DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
+ } |
+ |
+ ServiceOwnerChangedListenerMap::const_iterator it = |
+ service_owner_changed_listener_map_.find(service_name); |
+ if (it == service_owner_changed_listener_map_.end()) |
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
+ |
+ 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)); |
+ } |
+ return DBUS_HANDLER_RESULT_HANDLED; |
+} |
+ |
+// 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 +1188,7 @@ |
self->OnDispatchStatusChanged(connection, status); |
} |
+// static |
DBusHandlerResult Bus::OnConnectionDisconnectedFilter( |
DBusConnection* connection, |
DBusMessage* message, |
@@ -1045,11 +1197,24 @@ |
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); |
+ return self->OnServiceOwnerChanged(message); |
+ } |
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
+} |
+ |
} // namespace dbus |