Back to index

unity  6.0.0
GLibDBusProxy.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2011 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
00018  */
00019 
00020 #include "GLibDBusProxy.h"
00021 
00022 #include <map>
00023 #include <memory>
00024 #include <NuxCore/Logger.h>
00025 #include <vector>
00026 
00027 #include "GLibWrapper.h"
00028 #include "GLibSignal.h"
00029 #include "GLibSource.h"
00030 
00031 namespace unity
00032 {
00033 namespace glib
00034 {
00035 
00036 namespace
00037 {
00038 nux::logging::Logger logger("unity.glib.dbusproxy");
00039 }
00040 
00041 using std::string;
00042 
00043 class DBusProxy::Impl
00044 {
00045 public:
00046   typedef std::vector<ReplyCallback> Callbacks;
00047   typedef std::map<string, Callbacks> SignalHandlers;
00048 
00049   Impl(DBusProxy* owner,
00050        string const& name,
00051        string const& object_path,
00052        string const& interface_name,
00053        GBusType bus_type,
00054        GDBusProxyFlags flags);
00055   ~Impl();
00056 
00057   void StartReconnectionTimeout();
00058   void Connect();
00059 
00060   void Call(string const& method_name,
00061             GVariant* parameters,
00062             ReplyCallback callback,
00063             GCancellable* cancellable,
00064             GDBusCallFlags flags,
00065             int timeout_msec);
00066 
00067   void Connect(string const& signal_name, ReplyCallback callback);
00068   bool IsConnected();
00069 
00070   void OnProxyNameOwnerChanged(GDBusProxy*, GParamSpec*);
00071   void OnProxySignal(GDBusProxy* proxy, char* sender_name, char* signal_name,
00072                      GVariant* parameters);
00073 
00074   static void OnProxyConnectCallback(GObject* source, GAsyncResult* res, gpointer impl);
00075   static void OnCallCallback(GObject* source, GAsyncResult* res, gpointer call_data);
00076 
00077   struct CallData
00078   {
00079     DBusProxy::ReplyCallback callback;
00080     DBusProxy::Impl* impl;
00081     std::string method_name;
00082   };
00083 
00084   DBusProxy* owner_;
00085   string name_;
00086   string object_path_;
00087   string interface_name_;
00088   GBusType bus_type_;
00089   GDBusProxyFlags flags_;
00090 
00091   glib::Object<GDBusProxy> proxy_;
00092   glib::Object<GCancellable> cancellable_;
00093   guint watcher_id_;
00094   bool connected_;
00095 
00096   glib::Signal<void, GDBusProxy*, char*, char*, GVariant*> g_signal_connection_;
00097   glib::Signal<void, GDBusProxy*, GParamSpec*> name_owner_signal_;
00098   glib::Source::UniquePtr reconnect_timeout_;
00099 
00100   SignalHandlers handlers_;
00101 };
00102 
00103 DBusProxy::Impl::Impl(DBusProxy* owner,
00104                       string const& name,
00105                       string const& object_path,
00106                       string const& interface_name,
00107                       GBusType bus_type,
00108                       GDBusProxyFlags flags)
00109   : owner_(owner)
00110   , name_(name)
00111   , object_path_(object_path)
00112   , interface_name_(interface_name)
00113   , bus_type_(bus_type)
00114   , flags_(flags)
00115   , cancellable_(g_cancellable_new())
00116   , watcher_id_(0)
00117   , connected_(false)
00118 {
00119   StartReconnectionTimeout();
00120 }
00121 
00122 DBusProxy::Impl::~Impl()
00123 {
00124   g_cancellable_cancel(cancellable_);
00125   if (watcher_id_)
00126     g_bus_unwatch_name(watcher_id_);
00127 }
00128 
00129 void DBusProxy::Impl::StartReconnectionTimeout()
00130 {
00131   LOG_DEBUG(logger) << "Starting reconnection timeout for " << name_;
00132 
00133   auto callback = [&]
00134   {
00135     if (!proxy_)
00136       Connect();
00137 
00138     return false;
00139   };
00140 
00141   reconnect_timeout_.reset(new glib::TimeoutSeconds(1, callback));
00142 }
00143 
00144 void DBusProxy::Impl::Connect()
00145 {
00146   LOG_DEBUG(logger) << "Attempting to connect to " << name_;
00147   g_dbus_proxy_new_for_bus(bus_type_,
00148                            flags_,
00149                            NULL,
00150                            name_.c_str(),
00151                            object_path_.c_str(),
00152                            interface_name_.c_str(),
00153                            cancellable_,
00154                            DBusProxy::Impl::OnProxyConnectCallback,
00155                            this);
00156 }
00157 
00158 bool DBusProxy::Impl::IsConnected()
00159 {
00160   return connected_;
00161 }
00162 
00163 void DBusProxy::Impl::OnProxyConnectCallback(GObject* source,
00164                                              GAsyncResult* res,
00165                                              gpointer impl)
00166 {
00167   DBusProxy::Impl* self = static_cast<DBusProxy::Impl*>(impl);
00168 
00169   glib::Error error;
00170   glib::Object<GDBusProxy> proxy(g_dbus_proxy_new_for_bus_finish(res, &error));
00171 
00172   // If the async operation was cancelled, this callback will still be called and
00173   // therefore we should deal with the error before touching the impl pointer
00174   if (!proxy || error)
00175   {
00176     LOG_WARNING(logger) << "Unable to connect to proxy: " << error;
00177     return;
00178   }
00179 
00180   LOG_DEBUG(logger) << "Sucessfully created proxy: " << self->object_path_;
00181 
00182   self->proxy_ = proxy;
00183   self->g_signal_connection_.Connect(self->proxy_, "g-signal",
00184                                      sigc::mem_fun(self, &Impl::OnProxySignal));
00185   self->name_owner_signal_.Connect(self->proxy_, "notify::g-name-owner",
00186                                    sigc::mem_fun(self, &Impl::OnProxyNameOwnerChanged));
00187 
00188   // If a proxy cannot autostart a service, it doesn't throw an error, but
00189   // sets name_owner to NULL
00190   if (glib::String(g_dbus_proxy_get_name_owner(proxy)))
00191   {
00192     self->connected_ = true;
00193     self->owner_->connected.emit();
00194   }
00195 }
00196 
00197 void DBusProxy::Impl::OnProxyNameOwnerChanged(GDBusProxy* proxy, GParamSpec* param)
00198 {
00199   glib::String name_owner(g_dbus_proxy_get_name_owner(proxy));
00200 
00201   if (name_owner)
00202   {
00203     if (!connected_)
00204     {
00205       LOG_DEBUG(logger) << name_ << " appeared";
00206 
00207       connected_ = true;
00208       owner_->connected.emit();
00209     }
00210   }
00211   else if (connected_)
00212   {
00213     LOG_DEBUG(logger) << name_ << " vanished";
00214 
00215     connected_ = false;
00216     owner_->disconnected.emit();
00217   }
00218 }
00219 
00220 void DBusProxy::Impl::OnProxySignal(GDBusProxy* proxy,
00221                                     char* sender_name,
00222                                     char* signal_name,
00223                                     GVariant* parameters)
00224 {
00225   LOG_DEBUG(logger) << "Signal Received for proxy (" << object_path_ << ") "
00226                     << "SenderName: " << sender_name << " "
00227                     << "SignalName: " << signal_name << " "
00228                     << "ParameterType: " << g_variant_get_type_string(parameters);
00229 
00230   for (ReplyCallback callback: handlers_[signal_name])
00231     callback(parameters);
00232 }
00233 
00234 void DBusProxy::Impl::Call(string const& method_name,
00235                            GVariant* parameters,
00236                            ReplyCallback callback,
00237                            GCancellable* cancellable,
00238                            GDBusCallFlags flags,
00239                            int timeout_msec)
00240 {
00241   if (proxy_)
00242   {
00243     CallData* data = new CallData();
00244     data->callback = callback;
00245     data->impl = this;
00246     data->method_name = method_name;
00247 
00248     g_dbus_proxy_call(proxy_,
00249                       method_name.c_str(),
00250                       parameters,
00251                       flags,
00252                       timeout_msec,
00253                       cancellable != NULL ? cancellable : cancellable_,
00254                       DBusProxy::Impl::OnCallCallback,
00255                       data);
00256   }
00257   else
00258   {
00259     LOG_WARNING(logger) << "Cannot call method " << method_name
00260                         << " proxy " << object_path_ << " does not exist";
00261   }
00262 }
00263 
00264 void DBusProxy::Impl::OnCallCallback(GObject* source, GAsyncResult* res, gpointer call_data)
00265 {
00266   glib::Error error;
00267   std::unique_ptr<CallData> data (static_cast<CallData*>(call_data));
00268   GVariant* result = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error);
00269 
00270   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
00271   {
00272     // silently ignore
00273   }
00274   else if (error)
00275   {
00276     // Do not touch the impl pointer as the operation may have been cancelled
00277     LOG_WARNING(logger) << "Calling method \"" << data->method_name
00278       << "\" on object path: \""
00279       << g_dbus_proxy_get_object_path (G_DBUS_PROXY (source))
00280       << "\" failed: " << error;
00281   }
00282   else
00283   {
00284     data->callback(result);
00285     g_variant_unref(result);
00286   }
00287 }
00288 
00289 void DBusProxy::Impl::Connect(std::string const& signal_name, ReplyCallback callback)
00290 {
00291   handlers_[signal_name].push_back(callback);
00292 }
00293 
00294 DBusProxy::DBusProxy(string const& name,
00295                      string const& object_path,
00296                      string const& interface_name,
00297                      GBusType bus_type,
00298                      GDBusProxyFlags flags)
00299   : pimpl(new Impl(this, name, object_path, interface_name, bus_type, flags))
00300 {}
00301 
00302 DBusProxy::~DBusProxy()
00303 {}
00304 
00305 void DBusProxy::Call(string const& method_name,
00306                      GVariant* parameters,
00307                      ReplyCallback callback,
00308                      GCancellable* cancellable,
00309                      GDBusCallFlags flags,
00310                      int timeout_msec)
00311 {
00312   pimpl->Call(method_name, parameters, callback, cancellable, flags,
00313               timeout_msec);
00314 }
00315 
00316 void DBusProxy::Connect(std::string const& signal_name, ReplyCallback callback)
00317 {
00318   pimpl->Connect(signal_name, callback);
00319 }
00320 
00321 bool DBusProxy::IsConnected()
00322 {
00323   return pimpl->IsConnected();
00324 }
00325 
00326 }
00327 }