Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/multi_process_notification.h" | 5 #include "chrome/browser/multi_process_notification.h" |
| 6 | 6 |
| 7 #include <dbus/dbus.h> | |
| 8 #include <set> | |
| 9 | |
| 10 #include "base/basictypes.h" | |
| 11 #include "base/file_path.h" | |
| 7 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/message_loop_proxy.h" | |
| 14 #include "base/path_service.h" | |
| 15 #include "base/stringprintf.h" | |
| 16 #include "base/threading/simple_thread.h" | |
| 17 #include "chrome/common/chrome_paths.h" | |
| 8 | 18 |
| 9 namespace multi_process_notification { | 19 namespace multi_process_notification { |
| 10 | 20 |
| 11 bool Post(const std::string& name, Domain domain) { | 21 const char* kNotifyPath = "/modules/notify"; |
| 12 // TODO(dmaclach): Implement | 22 const char* kNotifyInterface = "org.chromium.Notify"; |
| 13 NOTIMPLEMENTED(); | 23 const char* kNotifySignalName = "Notify"; |
| 14 return false; | 24 |
| 15 } | 25 // A simple thread to run the DBus main loop. |
| 26 class ListenerThread : public base::SimpleThread { | |
| 27 public: | |
| 28 ListenerThread(); | |
| 29 | |
| 30 bool Init(); | |
| 31 | |
| 32 bool AddListener(ListenerImpl* listner); | |
| 33 bool RemoveListener(ListenerImpl* listner); | |
| 34 | |
| 35 // SimpleThread overrides | |
| 36 virtual void Run(); | |
| 37 | |
| 38 private: | |
| 39 DBusConnection* connection_; | |
| 40 | |
| 41 Lock listeners_lock_; | |
| 42 std::set<ListenerImpl*> listeners_; | |
| 43 }; | |
| 16 | 44 |
| 17 class ListenerImpl { | 45 class ListenerImpl { |
| 18 public: | 46 public: |
| 19 ListenerImpl(const std::string& name, | 47 ListenerImpl(const std::string& name, |
| 20 Domain domain, | 48 Domain domain, |
| 21 Listener::Delegate* delegate); | 49 Listener::Delegate* delegate); |
| 50 virtual ~ListenerImpl(); | |
| 22 | 51 |
| 23 bool Start(MessageLoop* io_loop_to_listen_on); | 52 bool Start(MessageLoop* io_loop_to_listen_on); |
| 53 void OnListen(const std::string& name); | |
| 24 | 54 |
| 25 std::string name() const { return name_; } | 55 std::string name() const { return name_; } |
| 26 Domain domain() const { return domain_; } | 56 Domain domain() const { return domain_; } |
| 27 | 57 |
| 28 private: | 58 private: |
| 59 void StartListener(); | |
| 60 | |
| 29 std::string name_; | 61 std::string name_; |
| 30 Domain domain_; | 62 Domain domain_; |
| 31 Listener::Delegate* delegate_; | 63 Listener::Delegate* delegate_; |
| 32 | 64 |
| 65 DBusError error_; | |
| 66 DBusConnection* connection_; | |
| 67 | |
| 68 static ListenerThread* g_thread_; | |
| 69 | |
| 70 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; | |
| 71 | |
| 33 DISALLOW_COPY_AND_ASSIGN(ListenerImpl); | 72 DISALLOW_COPY_AND_ASSIGN(ListenerImpl); |
| 34 }; | 73 }; |
| 35 | 74 |
| 75 ListenerThread* ListenerImpl::g_thread_ = NULL; | |
| 76 | |
| 77 std::string AddPrefixToNotification(const std::string& name, | |
| 78 Domain domain) { | |
| 79 std::string prefix; | |
| 80 switch (domain) { | |
| 81 case multi_process_notification::ProfileDomain: { | |
| 82 FilePath user_data_dir; | |
| 83 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) { | |
| 84 NOTREACHED(); | |
| 85 } | |
| 86 prefix = StringPrintf("user.%u.%s.", getuid(), | |
| 87 user_data_dir.value().c_str()); | |
| 88 break; | |
| 89 } | |
| 90 case multi_process_notification::UserDomain: | |
| 91 prefix = StringPrintf("user.%u.", getuid()); | |
| 92 break; | |
| 93 case multi_process_notification::SystemDomain: | |
| 94 prefix = ""; | |
| 95 break; | |
| 96 } | |
| 97 return prefix + name; | |
| 98 } | |
| 99 | |
| 100 bool Post(const std::string& name, Domain domain) { | |
| 101 bool success = true; | |
|
dmac
2011/01/21 19:20:01
move success decl down near where it is used.
garykac
2011/01/21 21:46:19
Done.
| |
| 102 | |
| 103 DBusError error; | |
| 104 dbus_error_init(&error); | |
|
dmac
2011/01/21 19:20:01
check error here?
garykac
2011/01/21 21:46:19
This is initializing the DBusError struct, so ther
| |
| 105 | |
| 106 // Get a connection to the DBus. | |
| 107 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error); | |
| 108 if (dbus_error_is_set(&error)) { | |
| 109 LOG(ERROR) << "Failed to create initial dbus connection: " << error.message; | |
| 110 dbus_error_free(&error); | |
| 111 return false; | |
| 112 } | |
| 113 if (!connection) { | |
| 114 LOG(ERROR) << "Failed to create initial dbus connection"; | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 // Construct the fullname for the signal by adding domain info as needed. | |
| 119 std::string fullname = AddPrefixToNotification(name, domain); | |
| 120 | |
| 121 // Create the Notify signal. | |
| 122 DBusMessage* message = dbus_message_new_signal(kNotifyPath, kNotifyInterface, | |
|
dmac
2011/01/21 19:20:01
create a wrapper class for this that does the unre
garykac
2011/01/21 21:46:19
There's no generic 'scoped_ptr' wrapper for these
| |
| 123 kNotifySignalName); | |
| 124 if (!message) { | |
| 125 LOG(ERROR) << "Failed to create message for signal."; | |
| 126 success = false; | |
| 127 } | |
| 128 | |
| 129 // Add the user-defined signal name as an argument to the Notify signal. | |
| 130 if (success) { | |
| 131 DBusMessageIter args; | |
| 132 const char* cname = fullname.c_str(); | |
| 133 dbus_message_iter_init_append(message, &args); | |
| 134 if (!dbus_message_iter_append_basic(&args, | |
| 135 DBUS_TYPE_STRING, &cname)) { | |
| 136 LOG(ERROR) << "Failed to set signal name."; | |
|
dmac
2011/01/21 19:20:01
maybe pass the name in along with the error?
garykac
2011/01/21 21:46:19
Done.
| |
| 137 success = false; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // Actually send the signal. | |
| 142 if (success) { | |
| 143 dbus_uint32_t serial = 0; | |
| 144 if (!dbus_connection_send(connection, message, &serial)) { | |
| 145 LOG(ERROR) << "Unable to send message."; | |
| 146 success = false; | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 if (success) { | |
| 151 dbus_connection_flush(connection); | |
| 152 } | |
| 153 dbus_message_unref(message); | |
| 154 | |
| 155 return success; | |
| 156 } | |
| 157 | |
| 158 ListenerThread::ListenerThread() | |
| 159 : base::SimpleThread("ListenerThread"), | |
| 160 connection_(NULL) { | |
| 161 } | |
| 162 | |
| 163 bool ListenerThread::Init() { | |
| 164 DBusError error; | |
| 165 dbus_error_init(&error); | |
|
dmac
2011/01/21 19:20:01
create a wrapper class for dbus_error handling as
garykac
2011/01/21 21:46:19
wifi_data_provider_linux.cc uses CheckError, and I
| |
| 166 | |
| 167 // Get a connection to the DBus. | |
| 168 connection_ = dbus_bus_get(DBUS_BUS_SESSION, &error); | |
| 169 if (dbus_error_is_set(&error)) { | |
| 170 LOG(ERROR) << "Failed to create initial dbus connection: " | |
| 171 << error.message; | |
| 172 dbus_error_free(&error); | |
| 173 return false; | |
| 174 } | |
| 175 if (!connection_) { | |
| 176 LOG(ERROR) << "Failed to create initial dbus connection"; | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 // Create matching rule for our signal type. | |
| 181 std::string match_rule = StringPrintf("type='signal',interface='%s'", | |
| 182 kNotifyInterface); | |
| 183 dbus_bus_add_match(connection_, match_rule.c_str(), &error); | |
| 184 dbus_connection_flush(connection_); | |
| 185 if (dbus_error_is_set(&error)) { | |
| 186 LOG(ERROR) << "Failed to add match rule: " | |
| 187 << error.message; | |
| 188 dbus_error_free(&error); | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 bool ListenerThread::AddListener(ListenerImpl* listener) { | |
| 196 base::AutoLock autolock(listeners_lock_); | |
| 197 listeners_.insert(listener); | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 bool ListenerThread::RemoveListener(ListenerImpl* listener) { | |
| 202 base::AutoLock autolock(listeners_lock_); | |
| 203 listeners_.erase(listener); | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 void ListenerThread::Run() { | |
| 208 bool done = false; | |
| 209 | |
| 210 while (!done) { | |
| 211 // Get next available message. | |
| 212 // Using -1 for a timeout to make this a blocking call blocks all dbus | |
| 213 // calls on this connection. Thus, we use a timeout of 0 and a sleep(). | |
| 214 // TODO(garykac): Ugh. Fix this to use file descriptors. | |
| 215 dbus_connection_read_write(connection_, 0); | |
| 216 | |
| 217 DBusMessage* message = dbus_connection_pop_message(connection_); | |
| 218 if (!message) { | |
| 219 sleep(1); // Yuck! | |
| 220 continue; | |
| 221 } | |
| 222 | |
| 223 // Process all queued up messages. | |
| 224 while (message) { | |
| 225 if (dbus_message_is_signal(message, | |
| 226 kNotifyInterface, kNotifySignalName)) { | |
| 227 // Get user-defined name from the signal arguments. | |
| 228 DBusMessageIter args; | |
| 229 if (!dbus_message_iter_init(message, &args)) { | |
| 230 LOG(ERROR) << "Params missing from dbus signal"; | |
| 231 } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { | |
| 232 LOG(ERROR) << "Dbus signal param is not string type"; | |
| 233 } else { | |
| 234 char* name; | |
| 235 dbus_message_iter_get_basic(&args, &name); | |
| 236 { // Scope for lock | |
| 237 base::AutoLock autolock(listeners_lock_); | |
| 238 std::set<ListenerImpl*>::iterator it; | |
| 239 // Note that this doesn't scale well to a large number of listeners. | |
| 240 // We should only have a couple active, so this isn't a problem. | |
| 241 for (it=listeners_.begin(); it!=listeners_.end(); it++) { | |
| 242 (*it)->OnListen(name); | |
| 243 } | |
| 244 } | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 dbus_message_unref(message); | |
| 249 message = dbus_connection_pop_message(connection_); | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 36 ListenerImpl::ListenerImpl(const std::string& name, | 254 ListenerImpl::ListenerImpl(const std::string& name, |
| 37 Domain domain, | 255 Domain domain, |
| 38 Listener::Delegate* delegate) | 256 Listener::Delegate* delegate) |
| 39 : name_(name), domain_(domain), delegate_(delegate) { | 257 : name_(name), |
| 258 domain_(domain), | |
| 259 delegate_(delegate), | |
| 260 connection_(NULL) { | |
| 261 } | |
| 262 | |
| 263 ListenerImpl::~ListenerImpl() { | |
| 264 if (g_thread_) { | |
| 265 g_thread_->RemoveListener(this); | |
| 266 } | |
| 40 } | 267 } |
| 41 | 268 |
| 42 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) { | 269 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) { |
| 43 // TODO(dmaclach): Implement | 270 if (io_loop_to_listen_on->type() != MessageLoop::TYPE_IO) { |
| 44 NOTIMPLEMENTED(); | 271 DLOG(ERROR) << "io_loop_to_listen_on must be TYPE_IO"; |
| 45 return false; | 272 return false; |
| 273 } | |
| 274 // Start the listener on the IO thread. | |
| 275 message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); | |
| 276 Task* task = NewRunnableMethod(this, &ListenerImpl::StartListener); | |
| 277 io_loop_to_listen_on->PostTask(FROM_HERE, task); | |
| 278 return true; | |
| 279 } | |
| 280 | |
| 281 void ListenerImpl::StartListener() { | |
| 282 DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()); | |
| 283 bool success = true; | |
| 284 | |
| 285 if (!g_thread_) { | |
|
dmac
2011/01/21 19:20:01
need a lock around this thread creation. Multiple
garykac
2011/01/21 21:46:19
Yikes! Done.
| |
| 286 g_thread_ = new ListenerThread(); | |
| 287 success = g_thread_->Init(); | |
| 288 if (success) { | |
| 289 g_thread_->Start(); | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 g_thread_->AddListener(this); | |
| 294 | |
| 295 // Send initialization success/fail status to delegate. | |
| 296 Task* task = new Listener::ListenerStartedTask(name_, domain_, delegate_, | |
| 297 success); | |
| 298 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task)); | |
| 299 } | |
| 300 | |
| 301 void ListenerImpl::OnListen(const std::string& name) { | |
| 302 std::string myname = AddPrefixToNotification(name_, domain_); | |
| 303 // Ignore the signal unless it matches our name. | |
| 304 if (name == myname) { | |
| 305 Task* task = | |
| 306 new Listener::NotificationReceivedTask(name_, domain_, delegate_); | |
| 307 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task)); | |
| 308 } | |
| 46 } | 309 } |
| 47 | 310 |
| 48 Listener::Listener(const std::string& name, | 311 Listener::Listener(const std::string& name, |
| 49 Domain domain, | 312 Domain domain, |
| 50 Listener::Delegate* delegate) | 313 Listener::Delegate* delegate) |
| 51 : impl_(new ListenerImpl(name, domain, delegate)) { | 314 : impl_(new ListenerImpl(name, domain, delegate)) { |
| 52 } | 315 } |
| 53 | 316 |
| 54 Listener::~Listener() { | 317 Listener::~Listener() { |
| 55 } | 318 } |
| 56 | 319 |
| 57 bool Listener::Start(MessageLoop* io_loop_to_listen_on) { | 320 bool Listener::Start(MessageLoop* io_loop_to_listen_on) { |
| 58 return impl_->Start(io_loop_to_listen_on); | 321 return impl_->Start(io_loop_to_listen_on); |
| 59 } | 322 } |
| 60 | 323 |
| 61 std::string Listener::name() const { | 324 std::string Listener::name() const { |
| 62 return impl_->name(); | 325 return impl_->name(); |
| 63 } | 326 } |
| 64 | 327 |
| 65 Domain Listener::domain() const { | 328 Domain Listener::domain() const { |
| 66 return impl_->domain(); | 329 return impl_->domain(); |
| 67 } | 330 } |
| 68 | 331 |
| 69 } // namespace multi_process_notification | 332 } // namespace multi_process_notification |
| 333 | |
| 334 DISABLE_RUNNABLE_METHOD_REFCOUNT(multi_process_notification::ListenerImpl); | |
| OLD | NEW |