- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / dbus.cc
1 /*
2  * libjingle
3  * Copyright 2004--2011, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #ifdef HAVE_DBUS_GLIB
29
30 #include "talk/base/dbus.h"
31
32 #include <glib.h>
33
34 #include "talk/base/logging.h"
35 #include "talk/base/thread.h"
36
37 namespace talk_base {
38
39 // Avoid static object construction/destruction on startup/shutdown.
40 static pthread_once_t g_dbus_init_once = PTHREAD_ONCE_INIT;
41 static LibDBusGlibSymbolTable *g_dbus_symbol = NULL;
42
43 // Releases DBus-Glib symbols.
44 static void ReleaseDBusGlibSymbol() {
45   if (g_dbus_symbol != NULL) {
46     delete g_dbus_symbol;
47     g_dbus_symbol = NULL;
48   }
49 }
50
51 // Loads DBus-Glib symbols.
52 static void InitializeDBusGlibSymbol() {
53   // This is thread safe.
54   if (NULL == g_dbus_symbol) {
55     g_dbus_symbol = new LibDBusGlibSymbolTable();
56
57     // Loads dbus-glib
58     if (NULL == g_dbus_symbol || !g_dbus_symbol->Load()) {
59       LOG(LS_WARNING) << "Failed to load dbus-glib symbol table.";
60       ReleaseDBusGlibSymbol();
61     } else {
62       // Nothing we can do if atexit() failed. Just ignore its returned value.
63       atexit(ReleaseDBusGlibSymbol);
64     }
65   }
66 }
67
68 inline static LibDBusGlibSymbolTable *GetSymbols() {
69   return DBusMonitor::GetDBusGlibSymbolTable();
70 }
71
72 // Implementation of class DBusSigMessageData
73 DBusSigMessageData::DBusSigMessageData(DBusMessage *message)
74     : TypedMessageData<DBusMessage *>(message) {
75   GetSymbols()->dbus_message_ref()(data());
76 }
77
78 DBusSigMessageData::~DBusSigMessageData() {
79   GetSymbols()->dbus_message_unref()(data());
80 }
81
82 // Implementation of class DBusSigFilter
83
84 // Builds a DBus filter string from given DBus path, interface and member.
85 std::string DBusSigFilter::BuildFilterString(const std::string &path,
86                                              const std::string &interface,
87                                              const std::string &member) {
88   std::string ret(DBUS_TYPE "='" DBUS_SIGNAL "'");
89   if (!path.empty()) {
90     ret += ("," DBUS_PATH "='");
91     ret += path;
92     ret += "'";
93   }
94   if (!interface.empty()) {
95     ret += ("," DBUS_INTERFACE "='");
96     ret += interface;
97     ret += "'";
98   }
99   if (!member.empty()) {
100     ret += ("," DBUS_MEMBER "='");
101     ret += member;
102     ret += "'";
103   }
104   return ret;
105 }
106
107 // Forwards the message to the given instance.
108 DBusHandlerResult DBusSigFilter::DBusCallback(DBusConnection *dbus_conn,
109                                               DBusMessage *message,
110                                               void *instance) {
111   ASSERT(instance);
112   if (instance) {
113     return static_cast<DBusSigFilter *>(instance)->Callback(message);
114   }
115   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
116 }
117
118 // Posts a message to caller thread.
119 DBusHandlerResult DBusSigFilter::Callback(DBusMessage *message) {
120   if (caller_thread_) {
121     caller_thread_->Post(this, DSM_SIGNAL, new DBusSigMessageData(message));
122   }
123   // Don't "eat" the message here. Let it pop up.
124   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
125 }
126
127 // From MessageHandler.
128 void DBusSigFilter::OnMessage(Message *message) {
129   if (message != NULL && DSM_SIGNAL == message->message_id) {
130     DBusSigMessageData *msg =
131         static_cast<DBusSigMessageData *>(message->pdata);
132     if (msg) {
133       ProcessSignal(msg->data());
134       delete msg;
135     }
136   }
137 }
138
139 // Definition of private class DBusMonitoringThread.
140 // It creates a worker-thread to listen signals on DBus. The worker-thread will
141 // be running in a priate GMainLoop forever until either Stop() has been invoked
142 // or it hits an error.
143 class DBusMonitor::DBusMonitoringThread : public talk_base::Thread {
144  public:
145   explicit DBusMonitoringThread(DBusMonitor *monitor,
146                                 GMainContext *context,
147                                 GMainLoop *mainloop,
148                                 std::vector<DBusSigFilter *> *filter_list)
149       : monitor_(monitor),
150         context_(context),
151         mainloop_(mainloop),
152         connection_(NULL),
153         idle_source_(NULL),
154         filter_list_(filter_list) {
155     ASSERT(monitor_);
156     ASSERT(context_);
157     ASSERT(mainloop_);
158     ASSERT(filter_list_);
159   }
160
161   virtual ~DBusMonitoringThread() {
162     Stop();
163   }
164
165   // Override virtual method of Thread. Context: worker-thread.
166   virtual void Run() {
167     ASSERT(NULL == connection_);
168
169     // Setup DBus connection and start monitoring.
170     monitor_->OnMonitoringStatusChanged(DMS_INITIALIZING);
171     if (!Setup()) {
172       LOG(LS_ERROR) << "DBus monitoring setup failed.";
173       monitor_->OnMonitoringStatusChanged(DMS_FAILED);
174       CleanUp();
175       return;
176     }
177     monitor_->OnMonitoringStatusChanged(DMS_RUNNING);
178     g_main_loop_run(mainloop_);
179     monitor_->OnMonitoringStatusChanged(DMS_STOPPED);
180
181     // Done normally. Clean up DBus connection.
182     CleanUp();
183     return;
184   }
185
186   // Override virtual method of Thread. Context: caller-thread.
187   virtual void Stop() {
188     ASSERT(NULL == idle_source_);
189     // Add an idle source and let the gmainloop quit on idle.
190     idle_source_ = g_idle_source_new();
191     if (idle_source_) {
192       g_source_set_callback(idle_source_, &Idle, this, NULL);
193       g_source_attach(idle_source_, context_);
194     } else {
195       LOG(LS_ERROR) << "g_idle_source_new() failed.";
196       QuitGMainloop();  // Try to quit anyway.
197     }
198
199     Thread::Stop();  // Wait for the thread.
200   }
201
202  private:
203   // Registers all DBus filters.
204   void RegisterAllFilters() {
205     ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()(
206         connection_));
207
208     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
209          it != filter_list_->end(); ++it) {
210       DBusSigFilter *filter = (*it);
211       if (!filter) {
212         LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
213         continue;
214       }
215
216       GetSymbols()->dbus_bus_add_match()(
217           GetSymbols()->dbus_g_connection_get_connection()(connection_),
218           filter->filter().c_str(), NULL);
219
220       if (!GetSymbols()->dbus_connection_add_filter()(
221               GetSymbols()->dbus_g_connection_get_connection()(connection_),
222               &DBusSigFilter::DBusCallback, filter, NULL)) {
223         LOG(LS_ERROR) << "dbus_connection_add_filter() failed."
224                       << "Filter: " << filter->filter();
225         continue;
226       }
227     }
228   }
229
230   // Unregisters all DBus filters.
231   void UnRegisterAllFilters() {
232     ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()(
233         connection_));
234
235     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin();
236          it != filter_list_->end(); ++it) {
237       DBusSigFilter *filter = (*it);
238       if (!filter) {
239         LOG(LS_ERROR) << "DBusSigFilter list corrupted.";
240         continue;
241       }
242       GetSymbols()->dbus_connection_remove_filter()(
243           GetSymbols()->dbus_g_connection_get_connection()(connection_),
244           &DBusSigFilter::DBusCallback, filter);
245     }
246   }
247
248   // Sets up the monitoring thread.
249   bool Setup() {
250     g_main_context_push_thread_default(context_);
251
252     // Start connection to dbus.
253     // If dbus daemon is not running, returns false immediately.
254     connection_ = GetSymbols()->dbus_g_bus_get_private()(monitor_->type_,
255         context_, NULL);
256     if (NULL == connection_) {
257       LOG(LS_ERROR) << "dbus_g_bus_get_private() unable to get connection.";
258       return false;
259     }
260     if (NULL == GetSymbols()->dbus_g_connection_get_connection()(connection_)) {
261       LOG(LS_ERROR) << "dbus_g_connection_get_connection() returns NULL. "
262                     << "DBus daemon is probably not running.";
263       return false;
264     }
265
266     // Application don't exit if DBus daemon die.
267     GetSymbols()->dbus_connection_set_exit_on_disconnect()(
268         GetSymbols()->dbus_g_connection_get_connection()(connection_), FALSE);
269
270     // Connect all filters.
271     RegisterAllFilters();
272
273     return true;
274   }
275
276   // Cleans up the monitoring thread.
277   void CleanUp() {
278     if (idle_source_) {
279       // We did an attach() with the GSource, so we need to destroy() it.
280       g_source_destroy(idle_source_);
281       // We need to unref() the GSource to end the last reference we got.
282       g_source_unref(idle_source_);
283       idle_source_ = NULL;
284     }
285     if (connection_) {
286       if (GetSymbols()->dbus_g_connection_get_connection()(connection_)) {
287         UnRegisterAllFilters();
288         GetSymbols()->dbus_connection_close()(
289             GetSymbols()->dbus_g_connection_get_connection()(connection_));
290       }
291       GetSymbols()->dbus_g_connection_unref()(connection_);
292       connection_ = NULL;
293     }
294     g_main_loop_unref(mainloop_);
295     mainloop_ = NULL;
296     g_main_context_unref(context_);
297     context_ = NULL;
298   }
299
300   // Handles callback on Idle. We only add this source when ready to stop.
301   static gboolean Idle(gpointer data) {
302     static_cast<DBusMonitoringThread *>(data)->QuitGMainloop();
303     return TRUE;
304   }
305
306   // We only hit this when ready to quit.
307   void QuitGMainloop() {
308     g_main_loop_quit(mainloop_);
309   }
310
311   DBusMonitor *monitor_;
312
313   GMainContext *context_;
314   GMainLoop *mainloop_;
315   DBusGConnection *connection_;
316   GSource *idle_source_;
317
318   std::vector<DBusSigFilter *> *filter_list_;
319 };
320
321 // Implementation of class DBusMonitor
322
323 // Returns DBus-Glib symbol handle. Initialize it first if hasn't.
324 LibDBusGlibSymbolTable *DBusMonitor::GetDBusGlibSymbolTable() {
325   // This is multi-thread safe.
326   pthread_once(&g_dbus_init_once, InitializeDBusGlibSymbol);
327
328   return g_dbus_symbol;
329 };
330
331 // Creates an instance of DBusMonitor
332 DBusMonitor *DBusMonitor::Create(DBusBusType type) {
333   if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) {
334     return NULL;
335   }
336   return new DBusMonitor(type);
337 }
338
339 DBusMonitor::DBusMonitor(DBusBusType type)
340     : type_(type),
341       status_(DMS_NOT_INITIALIZED),
342       monitoring_thread_(NULL) {
343   ASSERT(type_ == DBUS_BUS_SYSTEM || type_ == DBUS_BUS_SESSION);
344 }
345
346 DBusMonitor::~DBusMonitor() {
347   StopMonitoring();
348 }
349
350 bool DBusMonitor::AddFilter(DBusSigFilter *filter) {
351   if (monitoring_thread_) {
352     return false;
353   }
354   if (!filter) {
355     return false;
356   }
357   filter_list_.push_back(filter);
358   return true;
359 }
360
361 bool DBusMonitor::StartMonitoring() {
362   if (!monitoring_thread_) {
363     g_type_init();
364     g_thread_init(NULL);
365     GetSymbols()->dbus_g_thread_init()();
366
367     GMainContext *context = g_main_context_new();
368     if (NULL == context) {
369       LOG(LS_ERROR) << "g_main_context_new() failed.";
370       return false;
371     }
372
373     GMainLoop *mainloop = g_main_loop_new(context, FALSE);
374     if (NULL == mainloop) {
375       LOG(LS_ERROR) << "g_main_loop_new() failed.";
376       g_main_context_unref(context);
377       return false;
378     }
379
380     monitoring_thread_ = new DBusMonitoringThread(this, context, mainloop,
381                                                   &filter_list_);
382     if (monitoring_thread_ == NULL) {
383       LOG(LS_ERROR) << "Failed to create DBus monitoring thread.";
384       g_main_context_unref(context);
385       g_main_loop_unref(mainloop);
386       return false;
387     }
388     monitoring_thread_->Start();
389   }
390   return true;
391 }
392
393 bool DBusMonitor::StopMonitoring() {
394   if (monitoring_thread_) {
395     monitoring_thread_->Stop();
396     monitoring_thread_ = NULL;
397   }
398   return true;
399 }
400
401 DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() {
402   return status_;
403 }
404
405 void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) {
406   status_ = status;
407 }
408
409 #undef LATE
410
411 }  // namespace talk_base
412
413 #endif  // HAVE_DBUS_GLIB