Chromium Code Reviews| 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 |