| /* | 
 |  *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #ifdef HAVE_DBUS_GLIB | 
 |  | 
 | #include "webrtc/base/dbus.h" | 
 |  | 
 | #include <glib.h> | 
 |  | 
 | #include "webrtc/base/logging.h" | 
 | #include "webrtc/base/thread.h" | 
 |  | 
 | namespace rtc { | 
 |  | 
 | // Avoid static object construction/destruction on startup/shutdown. | 
 | static pthread_once_t g_dbus_init_once = PTHREAD_ONCE_INIT; | 
 | static LibDBusGlibSymbolTable *g_dbus_symbol = NULL; | 
 |  | 
 | // Releases DBus-Glib symbols. | 
 | static void ReleaseDBusGlibSymbol() { | 
 |   if (g_dbus_symbol != NULL) { | 
 |     delete g_dbus_symbol; | 
 |     g_dbus_symbol = NULL; | 
 |   } | 
 | } | 
 |  | 
 | // Loads DBus-Glib symbols. | 
 | static void InitializeDBusGlibSymbol() { | 
 |   // This is thread safe. | 
 |   if (NULL == g_dbus_symbol) { | 
 |     g_dbus_symbol = new LibDBusGlibSymbolTable(); | 
 |  | 
 |     // Loads dbus-glib | 
 |     if (NULL == g_dbus_symbol || !g_dbus_symbol->Load()) { | 
 |       LOG(LS_WARNING) << "Failed to load dbus-glib symbol table."; | 
 |       ReleaseDBusGlibSymbol(); | 
 |     } else { | 
 |       // Nothing we can do if atexit() failed. Just ignore its returned value. | 
 |       atexit(ReleaseDBusGlibSymbol); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | inline static LibDBusGlibSymbolTable *GetSymbols() { | 
 |   return DBusMonitor::GetDBusGlibSymbolTable(); | 
 | } | 
 |  | 
 | // Implementation of class DBusSigMessageData | 
 | DBusSigMessageData::DBusSigMessageData(DBusMessage *message) | 
 |     : TypedMessageData<DBusMessage *>(message) { | 
 |   GetSymbols()->dbus_message_ref()(data()); | 
 | } | 
 |  | 
 | DBusSigMessageData::~DBusSigMessageData() { | 
 |   GetSymbols()->dbus_message_unref()(data()); | 
 | } | 
 |  | 
 | // Implementation of class DBusSigFilter | 
 |  | 
 | // Builds a DBus filter string from given DBus path, interface and member. | 
 | std::string DBusSigFilter::BuildFilterString(const std::string &path, | 
 |                                              const std::string &interface, | 
 |                                              const std::string &member) { | 
 |   std::string ret(DBUS_TYPE "='" DBUS_SIGNAL "'"); | 
 |   if (!path.empty()) { | 
 |     ret += ("," DBUS_PATH "='"); | 
 |     ret += path; | 
 |     ret += "'"; | 
 |   } | 
 |   if (!interface.empty()) { | 
 |     ret += ("," DBUS_INTERFACE "='"); | 
 |     ret += interface; | 
 |     ret += "'"; | 
 |   } | 
 |   if (!member.empty()) { | 
 |     ret += ("," DBUS_MEMBER "='"); | 
 |     ret += member; | 
 |     ret += "'"; | 
 |   } | 
 |   return ret; | 
 | } | 
 |  | 
 | // Forwards the message to the given instance. | 
 | DBusHandlerResult DBusSigFilter::DBusCallback(DBusConnection *dbus_conn, | 
 |                                               DBusMessage *message, | 
 |                                               void *instance) { | 
 |   ASSERT(instance); | 
 |   if (instance) { | 
 |     return static_cast<DBusSigFilter *>(instance)->Callback(message); | 
 |   } | 
 |   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; | 
 | } | 
 |  | 
 | // Posts a message to caller thread. | 
 | DBusHandlerResult DBusSigFilter::Callback(DBusMessage *message) { | 
 |   if (caller_thread_) { | 
 |     caller_thread_->Post(this, DSM_SIGNAL, new DBusSigMessageData(message)); | 
 |   } | 
 |   // Don't "eat" the message here. Let it pop up. | 
 |   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; | 
 | } | 
 |  | 
 | // From MessageHandler. | 
 | void DBusSigFilter::OnMessage(Message *message) { | 
 |   if (message != NULL && DSM_SIGNAL == message->message_id) { | 
 |     DBusSigMessageData *msg = | 
 |         static_cast<DBusSigMessageData *>(message->pdata); | 
 |     if (msg) { | 
 |       ProcessSignal(msg->data()); | 
 |       delete msg; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // Definition of private class DBusMonitoringThread. | 
 | // It creates a worker-thread to listen signals on DBus. The worker-thread will | 
 | // be running in a priate GMainLoop forever until either Stop() has been invoked | 
 | // or it hits an error. | 
 | class DBusMonitor::DBusMonitoringThread : public rtc::Thread { | 
 |  public: | 
 |   explicit DBusMonitoringThread(DBusMonitor *monitor, | 
 |                                 GMainContext *context, | 
 |                                 GMainLoop *mainloop, | 
 |                                 std::vector<DBusSigFilter *> *filter_list) | 
 |       : monitor_(monitor), | 
 |         context_(context), | 
 |         mainloop_(mainloop), | 
 |         connection_(NULL), | 
 |         idle_source_(NULL), | 
 |         filter_list_(filter_list) { | 
 |     ASSERT(monitor_); | 
 |     ASSERT(context_); | 
 |     ASSERT(mainloop_); | 
 |     ASSERT(filter_list_); | 
 |   } | 
 |  | 
 |   virtual ~DBusMonitoringThread() { | 
 |     Stop(); | 
 |   } | 
 |  | 
 |   // Override virtual method of Thread. Context: worker-thread. | 
 |   virtual void Run() { | 
 |     ASSERT(NULL == connection_); | 
 |  | 
 |     // Setup DBus connection and start monitoring. | 
 |     monitor_->OnMonitoringStatusChanged(DMS_INITIALIZING); | 
 |     if (!Setup()) { | 
 |       LOG(LS_ERROR) << "DBus monitoring setup failed."; | 
 |       monitor_->OnMonitoringStatusChanged(DMS_FAILED); | 
 |       CleanUp(); | 
 |       return; | 
 |     } | 
 |     monitor_->OnMonitoringStatusChanged(DMS_RUNNING); | 
 |     g_main_loop_run(mainloop_); | 
 |     monitor_->OnMonitoringStatusChanged(DMS_STOPPED); | 
 |  | 
 |     // Done normally. Clean up DBus connection. | 
 |     CleanUp(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Override virtual method of Thread. Context: caller-thread. | 
 |   virtual void Stop() { | 
 |     ASSERT(NULL == idle_source_); | 
 |     // Add an idle source and let the gmainloop quit on idle. | 
 |     idle_source_ = g_idle_source_new(); | 
 |     if (idle_source_) { | 
 |       g_source_set_callback(idle_source_, &Idle, this, NULL); | 
 |       g_source_attach(idle_source_, context_); | 
 |     } else { | 
 |       LOG(LS_ERROR) << "g_idle_source_new() failed."; | 
 |       QuitGMainloop();  // Try to quit anyway. | 
 |     } | 
 |  | 
 |     Thread::Stop();  // Wait for the thread. | 
 |   } | 
 |  | 
 |  private: | 
 |   // Registers all DBus filters. | 
 |   void RegisterAllFilters() { | 
 |     ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()( | 
 |         connection_)); | 
 |  | 
 |     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin(); | 
 |          it != filter_list_->end(); ++it) { | 
 |       DBusSigFilter *filter = (*it); | 
 |       if (!filter) { | 
 |         LOG(LS_ERROR) << "DBusSigFilter list corrupted."; | 
 |         continue; | 
 |       } | 
 |  | 
 |       GetSymbols()->dbus_bus_add_match()( | 
 |           GetSymbols()->dbus_g_connection_get_connection()(connection_), | 
 |           filter->filter().c_str(), NULL); | 
 |  | 
 |       if (!GetSymbols()->dbus_connection_add_filter()( | 
 |               GetSymbols()->dbus_g_connection_get_connection()(connection_), | 
 |               &DBusSigFilter::DBusCallback, filter, NULL)) { | 
 |         LOG(LS_ERROR) << "dbus_connection_add_filter() failed." | 
 |                       << "Filter: " << filter->filter(); | 
 |         continue; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Unregisters all DBus filters. | 
 |   void UnRegisterAllFilters() { | 
 |     ASSERT(NULL != GetSymbols()->dbus_g_connection_get_connection()( | 
 |         connection_)); | 
 |  | 
 |     for (std::vector<DBusSigFilter *>::iterator it = filter_list_->begin(); | 
 |          it != filter_list_->end(); ++it) { | 
 |       DBusSigFilter *filter = (*it); | 
 |       if (!filter) { | 
 |         LOG(LS_ERROR) << "DBusSigFilter list corrupted."; | 
 |         continue; | 
 |       } | 
 |       GetSymbols()->dbus_connection_remove_filter()( | 
 |           GetSymbols()->dbus_g_connection_get_connection()(connection_), | 
 |           &DBusSigFilter::DBusCallback, filter); | 
 |     } | 
 |   } | 
 |  | 
 |   // Sets up the monitoring thread. | 
 |   bool Setup() { | 
 |     g_main_context_push_thread_default(context_); | 
 |  | 
 |     // Start connection to dbus. | 
 |     // If dbus daemon is not running, returns false immediately. | 
 |     connection_ = GetSymbols()->dbus_g_bus_get_private()(monitor_->type_, | 
 |         context_, NULL); | 
 |     if (NULL == connection_) { | 
 |       LOG(LS_ERROR) << "dbus_g_bus_get_private() unable to get connection."; | 
 |       return false; | 
 |     } | 
 |     if (NULL == GetSymbols()->dbus_g_connection_get_connection()(connection_)) { | 
 |       LOG(LS_ERROR) << "dbus_g_connection_get_connection() returns NULL. " | 
 |                     << "DBus daemon is probably not running."; | 
 |       return false; | 
 |     } | 
 |  | 
 |     // Application don't exit if DBus daemon die. | 
 |     GetSymbols()->dbus_connection_set_exit_on_disconnect()( | 
 |         GetSymbols()->dbus_g_connection_get_connection()(connection_), FALSE); | 
 |  | 
 |     // Connect all filters. | 
 |     RegisterAllFilters(); | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 |   // Cleans up the monitoring thread. | 
 |   void CleanUp() { | 
 |     if (idle_source_) { | 
 |       // We did an attach() with the GSource, so we need to destroy() it. | 
 |       g_source_destroy(idle_source_); | 
 |       // We need to unref() the GSource to end the last reference we got. | 
 |       g_source_unref(idle_source_); | 
 |       idle_source_ = NULL; | 
 |     } | 
 |     if (connection_) { | 
 |       if (GetSymbols()->dbus_g_connection_get_connection()(connection_)) { | 
 |         UnRegisterAllFilters(); | 
 |         GetSymbols()->dbus_connection_close()( | 
 |             GetSymbols()->dbus_g_connection_get_connection()(connection_)); | 
 |       } | 
 |       GetSymbols()->dbus_g_connection_unref()(connection_); | 
 |       connection_ = NULL; | 
 |     } | 
 |     g_main_loop_unref(mainloop_); | 
 |     mainloop_ = NULL; | 
 |     g_main_context_unref(context_); | 
 |     context_ = NULL; | 
 |   } | 
 |  | 
 |   // Handles callback on Idle. We only add this source when ready to stop. | 
 |   static gboolean Idle(gpointer data) { | 
 |     static_cast<DBusMonitoringThread *>(data)->QuitGMainloop(); | 
 |     return TRUE; | 
 |   } | 
 |  | 
 |   // We only hit this when ready to quit. | 
 |   void QuitGMainloop() { | 
 |     g_main_loop_quit(mainloop_); | 
 |   } | 
 |  | 
 |   DBusMonitor *monitor_; | 
 |  | 
 |   GMainContext *context_; | 
 |   GMainLoop *mainloop_; | 
 |   DBusGConnection *connection_; | 
 |   GSource *idle_source_; | 
 |  | 
 |   std::vector<DBusSigFilter *> *filter_list_; | 
 | }; | 
 |  | 
 | // Implementation of class DBusMonitor | 
 |  | 
 | // Returns DBus-Glib symbol handle. Initialize it first if hasn't. | 
 | LibDBusGlibSymbolTable *DBusMonitor::GetDBusGlibSymbolTable() { | 
 |   // This is multi-thread safe. | 
 |   pthread_once(&g_dbus_init_once, InitializeDBusGlibSymbol); | 
 |  | 
 |   return g_dbus_symbol; | 
 | }; | 
 |  | 
 | // Creates an instance of DBusMonitor | 
 | DBusMonitor *DBusMonitor::Create(DBusBusType type) { | 
 |   if (NULL == DBusMonitor::GetDBusGlibSymbolTable()) { | 
 |     return NULL; | 
 |   } | 
 |   return new DBusMonitor(type); | 
 | } | 
 |  | 
 | DBusMonitor::DBusMonitor(DBusBusType type) | 
 |     : type_(type), | 
 |       status_(DMS_NOT_INITIALIZED), | 
 |       monitoring_thread_(NULL) { | 
 |   ASSERT(type_ == DBUS_BUS_SYSTEM || type_ == DBUS_BUS_SESSION); | 
 | } | 
 |  | 
 | DBusMonitor::~DBusMonitor() { | 
 |   StopMonitoring(); | 
 | } | 
 |  | 
 | bool DBusMonitor::AddFilter(DBusSigFilter *filter) { | 
 |   if (monitoring_thread_) { | 
 |     return false; | 
 |   } | 
 |   if (!filter) { | 
 |     return false; | 
 |   } | 
 |   filter_list_.push_back(filter); | 
 |   return true; | 
 | } | 
 |  | 
 | bool DBusMonitor::StartMonitoring() { | 
 |   if (!monitoring_thread_) { | 
 |     g_type_init(); | 
 |     // g_thread_init API is deprecated since glib 2.31.0, see release note: | 
 |     // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html | 
 | #if !GLIB_CHECK_VERSION(2, 31, 0) | 
 |     g_thread_init(NULL); | 
 | #endif | 
 |     GetSymbols()->dbus_g_thread_init()(); | 
 |  | 
 |     GMainContext *context = g_main_context_new(); | 
 |     if (NULL == context) { | 
 |       LOG(LS_ERROR) << "g_main_context_new() failed."; | 
 |       return false; | 
 |     } | 
 |  | 
 |     GMainLoop *mainloop = g_main_loop_new(context, FALSE); | 
 |     if (NULL == mainloop) { | 
 |       LOG(LS_ERROR) << "g_main_loop_new() failed."; | 
 |       g_main_context_unref(context); | 
 |       return false; | 
 |     } | 
 |  | 
 |     monitoring_thread_ = new DBusMonitoringThread(this, context, mainloop, | 
 |                                                   &filter_list_); | 
 |     if (monitoring_thread_ == NULL) { | 
 |       LOG(LS_ERROR) << "Failed to create DBus monitoring thread."; | 
 |       g_main_context_unref(context); | 
 |       g_main_loop_unref(mainloop); | 
 |       return false; | 
 |     } | 
 |     monitoring_thread_->Start(); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool DBusMonitor::StopMonitoring() { | 
 |   if (monitoring_thread_) { | 
 |     monitoring_thread_->Stop(); | 
 |     monitoring_thread_ = NULL; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | DBusMonitor::DBusMonitorStatus DBusMonitor::GetStatus() { | 
 |   return status_; | 
 | } | 
 |  | 
 | void DBusMonitor::OnMonitoringStatusChanged(DBusMonitorStatus status) { | 
 |   status_ = status; | 
 | } | 
 |  | 
 | #undef LATE | 
 |  | 
 | }  // namespace rtc | 
 |  | 
 | #endif  // HAVE_DBUS_GLIB |