Implemented event reporting to settings daemon clients.
authorOssama Othman <ossama.othman@intel.com>
Mon, 7 Oct 2013 23:28:54 +0000 (16:28 -0700)
committerOssama Othman <ossama.othman@intel.com>
Mon, 7 Oct 2013 23:28:54 +0000 (16:28 -0700)
Signed-off-by: Ossama Othman <ossama.othman@intel.com>
32 files changed:
doc/settings-protocol.js
include/settingsd/event_callback.hpp
include/settingsd/response_callback.hpp
include/settingsd/send_callback.hpp
lib/Makefile.am
lib/event_callback.cpp
lib/loader.cpp
lib/manager.cpp
lib/manager.hpp
lib/response_callback.cpp
lib/send_callback.cpp
plugins/connman/Makefile.am
plugins/connman/bluetooth.cpp
plugins/connman/bluetooth.hpp
plugins/connman/clock.cpp
plugins/connman/clock.hpp
plugins/connman/connman.cpp
plugins/connman/connman.hpp
plugins/connman/connman_manager.cpp
plugins/connman/connman_manager.hpp
plugins/connman/ethernet.cpp
plugins/connman/ethernet.hpp
plugins/connman/registration.cpp
plugins/connman/service.cpp
plugins/connman/service.hpp
plugins/connman/signal_callback.cpp [new file with mode: 0644]
plugins/connman/signal_callback.hpp [new file with mode: 0644]
plugins/connman/technology.cpp
plugins/connman/technology.hpp
plugins/connman/wifi.cpp
plugins/connman/wifi.hpp
src/websocket_server.cpp

index 6fd0ea7..444f685 100644 (file)
@@ -30,6 +30,15 @@ var response =
        "reason": "<some error message>"
     }
 
+// General form of an event
+var event =
+    {
+       "object_path":"<object path>" or null,
+       "interface_name":"<interface name>" or null,
+       "name":"<event name>",
+       "value": <setting specific value>
+    }
+
 // --------------------------------------------------------------
 //                           Bluetooth
 // --------------------------------------------------------------
@@ -256,6 +265,15 @@ var response =
        ]
     }
 
+// WiFi "PropertyChanged" event
+var event =
+    {
+       "object_path":"/net/connman/technology/wifi",
+       "interface_name":"net.connman.Technology",
+       "name":"PropertyChanged",
+       "value":["Powered",false]
+    }
+
 // --------------------------------------------------------------
 //                           Clock
 // --------------------------------------------------------------
@@ -313,3 +331,66 @@ var response =
        "result": "succeeded"
        "value": true or false
     }
+
+
+// --------------------------------------------------------------
+//                         Global Events
+// --------------------------------------------------------------
+
+// "ServicesChanged" event from Connman Manager object
+var event =
+    {
+       "object_path":"/",
+       "interface_name":"net.connman.Manager"
+       "name": "ServicesChanged",
+       "value": [
+            [
+               [
+                    "/net/connman/service/ethernet_002564e81cc7_cable",
+                    {}
+               ],
+               [
+                    "/net/connman/service/wifi_50465dae486e_574f537769666932472d4150_managed_wep",
+                    {
+                       "AutoConnect": false,
+                       "Domains": [],
+                       "Domains.Configuration": [],
+                       "Ethernet": {
+                            "Address": "20:36:5D:AF:28:9E",
+                            "Interface": "wlan0",
+                            "MTU": 1500,
+                            "Method": "auto"
+                       },
+                       "Favorite": false,
+                       "IPv4": {},
+                       "IPv4.Configuration": {
+                            "Method": "dhcp"
+                       },
+                       "IPv6": {},
+                       "IPv6.Configuration": {
+                            "Method": "auto",
+                            "Privacy": "prefered"
+                       },
+                       "Immutable": false,
+                       "Name": "WOSwifi2G-AP",
+                       "Nameservers": [],
+                       "Nameservers.Configuration": [],
+                       "Provider": {},
+                       "Proxy": {},
+                       "Proxy.Configuration": {},
+                       "Security": [
+                            "wep"
+                       ],
+                       "State": "idle",
+                       "Strength": 73,
+                       "Timeservers": [],
+                       "Timeservers.Configuration": [],
+                       "Type": "wifi"
+                    }
+               ]
+            ],
+            [
+               "/net/connman/service/wifi_50465dae486e_hidden_managed_wep"
+           ]
+       ]
+    }
index 8c02918..e3e5154 100644 (file)
@@ -31,7 +31,6 @@
 #include <settingsd/settings_api.hpp>
 #include <settingsd/send_callback.hpp>
 
-#include <string>
 #include <functional>
 
 
@@ -40,6 +39,7 @@ namespace ivi
   namespace settings
   {
     template<typename T> class smart_ptr;
+    class manager;
 
     /**
      * @class event_callback event_callback.hpp <settingsd/event_callback.hpp>
@@ -55,7 +55,7 @@ namespace ivi
     public:
 
       /// Constructor
-      event_callback(libwebsocket * wsi);
+      event_callback(manager & m);
 
       /**
        * Send event to Settings app.
@@ -71,11 +71,15 @@ namespace ivi
        * this class.
        *
        * @param[in] type          Setting type, e.g. @c "wifi".
+       * @param[in] event_name    Event name, e.g.
+       *                          @c "PropertyChanged".
        * @param[in] event_builder Callback function that appends JSON
        *                          formatted event data.
        */
       bool send_event(
-        std::string const & type,
+        char const * object_path,
+        char const * interface_name,
+        char const * event_name,
         std::function<void(JsonBuilder *)> event_builder);
 
     private:
@@ -86,10 +90,10 @@ namespace ivi
        *
        * The appropriate "header" information will be prepended to the
        * event.
-       *
-       * @param[in] result @c "succeeded" or @c "failed"
        */
-      smart_ptr<JsonBuilder> begin_event(std::string const & type);
+      smart_ptr<JsonBuilder> begin_event(char const * object_path,
+                                         char const * interface_name,
+                                         char const * event_name);
 
       /**
        * End the JSON formatted event to the Settings app request.
@@ -98,8 +102,11 @@ namespace ivi
 
     private:
 
-      /// Object used to send event to client over WebSocket.
-      send_callback writer_;
+      /**
+       * Settings manager used to send event to clients over
+       * corresponding WebSockets.
+       */
+      manager & manager_;
 
     };
 
index 083bd54..9019aa4 100644 (file)
@@ -59,6 +59,9 @@ namespace ivi
                         std::string type,
                         std::string transaction_id);
 
+      /// Copy constructor
+      response_callback(response_callback const & other);
+
       /**
        * Send (successful) response to Settings app.
        *
index 599358d..bb0de5c 100644 (file)
@@ -56,6 +56,12 @@ namespace ivi
       /// Constructor
       send_callback(libwebsocket * wsi);
 
+      /// Copy constructor.
+      send_callback(send_callback const & other);
+
+      /// Assignment operator.
+      send_callback & operator=(send_callback const & other);
+
       /**
        * Send the JSON formatted payload to the Settings app
        * over the corresponding websocket.
@@ -68,13 +74,23 @@ namespace ivi
       bool send_payload(char const * send_type,
                         smart_ptr<JsonBuilder> const & builder);
 
+      /**
+       * Check if the WebSocket instance @a wsi corresponds to this
+       * @c send_callback.
+       */
+      bool
+      operator==(libwebsocket const * wsi) const
+      {
+        return wsi_ == wsi;
+      }
+
     private:
 
       /**
        * Pointer to the websocket through which the payload will be
        * sent to the Settings app.
        */
-      libwebsocket * const wsi_;
+      libwebsocket * wsi_;
 
     };
 
index 940386c..f0247e1 100644 (file)
@@ -16,7 +16,7 @@
 ## Boston, MA  02110-1301  USA
 
 ## ---------------------------------------------------------
-##                settings plugin library
+##               settingsd plugin library
 ## ---------------------------------------------------------
 lib_LTLIBRARIES    = libsettings.la
 
index f07a77f..d0ffd50 100644 (file)
 #include <settingsd/event_callback.hpp>
 #include <settingsd/json_glib_traits.hpp>
 #include <settingsd/smart_ptr.hpp>
+#include "manager.hpp"
 
 
-ivi::settings::event_callback::event_callback(libwebsocket * wsi)
-  : writer_(wsi)
+ivi::settings::event_callback::event_callback(manager & mgr)
+  : manager_(mgr)
 {
 }
 
 bool
 ivi::settings::event_callback::send_event(
-  std::string const & type,
+  char const * object_path,
+  char const * interface_name,
+  char const * event_name,
   std::function<void(JsonBuilder *)> event_builder)
 {
-  smart_ptr<JsonBuilder> const builder = begin_event(type);
+  smart_ptr<JsonBuilder> const builder =
+    begin_event(object_path, interface_name, event_name);
 
   // Append settings type-specific JSON formatted events.
   event_builder(builder.get());
 
   end_event(builder);
 
-  bool const success = writer_.send_payload("event", builder);
+  bool const success = manager_.send_event(builder);
 
   if (!success)
-    g_critical("Unable to send %s event", type.c_str());
+    g_critical("Unable to send %s event", event_name);
 
   return success;
 }
 
 ivi::settings::smart_ptr<JsonBuilder>
-ivi::settings::event_callback::begin_event(std::string const & type)
+ivi::settings::event_callback::begin_event(char const * object_path,
+                                           char const * interface_name,
+                                           char const * event_name)
 {
   // Construct JSON event string.
   smart_ptr<JsonBuilder> safe_builder(json_builder_new());
@@ -63,11 +69,20 @@ ivi::settings::event_callback::begin_event(std::string const & type)
 
   json_builder_begin_object(builder);
 
-  json_builder_set_member_name(builder, "type");
-  if (type.empty())
+  json_builder_set_member_name(builder, "object_path");
+  if (object_path == nullptr)
     json_builder_add_null_value(builder);
   else
-    json_builder_add_string_value(builder, type.c_str());
+    json_builder_add_string_value(builder, object_path);
+
+  json_builder_set_member_name(builder, "interface_name");
+  if (interface_name == nullptr)
+    json_builder_add_null_value(builder);
+  else
+    json_builder_add_string_value(builder, interface_name);
+
+  json_builder_set_member_name(builder, "name");
+  json_builder_add_string_value(builder, event_name);
 
   return safe_builder;
 }
index 6423686..b017879 100644 (file)
@@ -29,6 +29,7 @@
 #include "manager.hpp"
 #include <settingsd/plugin.hpp>
 #include <settingsd/registrar.hpp>
+#include <settingsd/event_callback.hpp>
 
 #include <stdexcept>
 #include <dlfcn.h>
@@ -56,7 +57,7 @@ ivi::settings::loader::loader(std::string const & plugin_name,
   dlerror();
 
   // Retrieve the settings factory and destroyer functions.
-  typedef bool(*factory_type)(registrar &);
+  typedef bool(*factory_type)(registrar &, event_callback const &);
   factory_type const register_settings =
     IVI_SETTINGS_EXTENSION reinterpret_cast<factory_type>(
       dlsym(handle_,
@@ -68,7 +69,8 @@ ivi::settings::loader::loader(std::string const & plugin_name,
   // Now create the underlying settings implementation.
 
   registrar r(mgr);
-  if (!register_settings(r))
+  event_callback e(mgr);
+  if (!register_settings(r, e))
     throw std::runtime_error("Unable to make \""
                              + plugin_name
                              + "\" settings plugin");
index 45539bd..92be5b1 100644 (file)
@@ -40,6 +40,8 @@
 ivi::settings::manager::manager(std::string const & dir)
   : loaders_()
   , settings_()
+  , mutex_()
+  , send_callbacks_()
 {
   load_settings(dir);
 }
@@ -189,16 +191,35 @@ ivi::settings::manager::dispatch(std::string request,
 void
 ivi::settings::manager::subscribe_client(libwebsocket * wsi)
 {
-  clients_.push_back(wsi);
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  send_callbacks_.emplace_back(wsi);
 }
 
 void
 ivi::settings::manager::unsubscribe_client(libwebsocket * wsi)
 {
-  auto const end = clients_.end();
-  for (auto i = clients_.begin; i != end; ++i)
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  auto const end = send_callbacks_.end();
+  for (auto i = send_callbacks_.begin(); i != end; ++i)
     if (*i == wsi)
-      clients_.erase(i);
+      send_callbacks_.erase(i);
+}
+
+bool
+ivi::settings::manager::send_event(
+  smart_ptr<JsonBuilder> const & builder)
+{
+  bool success = true;
+
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  for (auto c : send_callbacks_)
+    if (!c.send_payload("event", builder))
+      success = false;
+
+  return success;
 }
 
 
index 0d644aa..373e097 100644 (file)
@@ -32,6 +32,7 @@
 #include "loader.hpp"
 
 #include <settingsd/plugin.hpp>
+#include <settingsd/send_callback.hpp>
 
 #include <libwebsockets.h>
 
@@ -39,6 +40,7 @@
 #include <map>
 #include <vector>
 #include <memory>
+#include <mutex>
 
 
 namespace ivi
@@ -93,6 +95,16 @@ namespace ivi
        */
       void unsubscribe_client(libwebsocket * wsi);
 
+      /**
+       * Send JSON formatted event to all clients.
+       *
+       * @param[in] builder Object used to generate the JSON formatted
+       *                    event.
+       *
+       * @return @c true on success.
+       */
+      bool send_event(smart_ptr<JsonBuilder> const & builder);
+
     private:
 
       /**
@@ -137,11 +149,14 @@ namespace ivi
       /// Map of settings name to @c settings instance.
       map_type settings_;
 
+      /// Mutex used to synchronize access to the send_callback list.
+      std::mutex mutex_;
+
       /**
        * List of client WebSockets to be used for sending events to
        * the client.
        */
-      std::vector<libwebsocket *> clients_;
+      std::vector<send_callback> send_callbacks_;
 
     };
 
index 0d56aa8..4d2ef40 100644 (file)
@@ -39,6 +39,15 @@ ivi::settings::response_callback::response_callback(
 {
 }
 
+ivi::settings::response_callback::response_callback(
+  response_callback const & other)
+  : writer_(other.writer_)
+  , type_(other.type_)
+  , transaction_id_(other.transaction_id_)
+{
+}
+
+
 bool
 ivi::settings::response_callback::send_response(
   std::function<void(JsonBuilder *)> response_builder)
index 878f3ac..eec4025 100644 (file)
@@ -39,10 +39,24 @@ ivi::settings::send_callback::send_callback(libwebsocket * wsi)
 {
 }
 
+ivi::settings::send_callback::send_callback(
+  send_callback const & other)
+  : wsi_(other.wsi_)
+{
+}
+
+ivi::settings::send_callback &
+ivi::settings::send_callback::operator=(send_callback const & other)
+{
+  wsi_ = other.wsi_;
+
+  return *this;
+}
+
 bool
 ivi::settings::send_callback::send_payload(
   char const * send_type,
-  ivi::settings::smart_ptr<JsonBuilder> const & builder)
+  smart_ptr<JsonBuilder> const & builder)
 {
   smart_ptr<JsonGenerator> const generator(json_generator_new());
   smart_ptr<JsonNode> const root(json_builder_get_root(builder.get()));
@@ -91,7 +105,6 @@ ivi::settings::send_callback::send_payload(
 }
 
 
-
 // Local Variables:
 // mode:c++
 // c-basic-offset:2
index 2f6b3b6..922f614 100644 (file)
@@ -39,7 +39,8 @@ connman_la_SOURCES =          \
        clock.cpp               \
        ethernet.cpp            \
        wifi.cpp                \
-       registration.cpp
+       registration.cpp        \
+       signal_callback.cpp
 connman_la_CXXFLAGS = \
        $(IVI_SETTINGS_PLUGIN_CXXFLAGS) \
        $(GIO_CFLAGS)                   \
@@ -61,4 +62,5 @@ noinst_HEADERS =              \
        bluetooth.hpp           \
        clock.hpp               \
        ethernet.hpp            \
-       wifi.hpp
+       wifi.hpp                \
+       signal_callback.hpp
index 576eb4e..8265b33 100644 (file)
@@ -36,8 +36,8 @@ namespace
 
 // ----------------------------------------------------------------------
 
-ivi::settings::bluetooth::bluetooth()
-  : technology_(technology_name)
+ivi::settings::bluetooth::bluetooth(event_callback const & e)
+  : technology_(technology_name, e)
 {
 }
 
index 31741c0..78f4519 100644 (file)
@@ -55,7 +55,7 @@ namespace ivi
     public:
 
       /// Constructor.
-      bluetooth();
+      bluetooth(event_callback const & e);
 
       /// Destructor.
       virtual ~bluetooth();
index 9ff91ea..3cd8c49 100644 (file)
@@ -35,8 +35,8 @@
 #include <boost/lexical_cast.hpp>
 
 
-ivi::settings::clock::clock()
-  : connman_("net.connman.Clock", "/")
+ivi::settings::clock::clock(event_callback const & e)
+  : connman_("net.connman.Clock", "/", e)
 {
 }
 
@@ -255,39 +255,13 @@ ivi::settings::clock::is_updates_auto(char const * name,
 bool
 ivi::settings::clock::set_property(char const * name,
                                    GVariant * value,
-                                   response_callback response,
+                                   response_callback /* response */,
                                    GError *& error)
 {
-  bool success = false;
-
-  // Get notified when the clock property has changed.
-  auto property_promise = connman_.get_property_changed_promise(name);
-  auto property_future  = property_promise->get_future();
-
   smart_ptr<GVariant> const ret(
     connman_.set_property(name, value, error));
 
-  if (ret != nullptr) {
-    static int const timeout = 5000;  // milliseconds
-
-    // Block until the clock property has changed..
-    std::future_status const status =
-      property_future.wait_for(std::chrono::milliseconds(timeout));
-
-    if (status == std::future_status::ready) {
-      smart_ptr<GVariant> const value(property_future.get());
-      success = true;
-    } else {
-      response.send_error(
-        std::string("Wait for clock property \"")
-                    + name + "\" set failed: "
-        + (status == std::future_status::timeout
-           ? "timeout"
-           : "deferred"));
-    }
-  }
-
-  return success;
+  return ret.get() != nullptr;
 }
 
 GVariant *
index e902af4..f6d6c18 100644 (file)
@@ -58,7 +58,7 @@ namespace ivi
     public:
 
       /// Constructor.
-      clock();
+      clock(event_callback const & e);
 
       /// Destructor.
       virtual ~clock();
index a3864c8..2b410a6 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include "connman.hpp"
+#include "signal_callback.hpp"
 
 #include <settingsd/reverse_lock.hpp>
 
 #include <stdexcept>
 
 
-namespace
-{
-  void
-  on_property_changed(GDBusConnection * /* connection */,
-                      char const * /* sender_name */,
-                      char const * /* object_path */,
-                      char const * /* interface_name */,
-                      char const * /* signal_name */,
-                      GVariant   * parameters,
-                      gpointer     user_data)
-  {
-    // Notify callers about the scan results.
-    typedef ivi::settings::connman::user_data user_data_type;
-
-    user_data_type * const data = static_cast<user_data_type *>(user_data);
-
-    std::lock_guard<std::mutex> lock(data->mutex);
-    auto const end = data->promises.end();
-    for (auto i = data->promises.begin(); i != end; ) {
-      gchar    * pname  = nullptr;
-      GVariant * pvalue = nullptr;
-
-      g_variant_get(parameters, "(sv)", &pname, &pvalue);
-
-      using namespace ivi::settings;
-
-      smart_ptr<gchar> name(pname);
-      smart_ptr<GVariant> value(pvalue);
-
-      auto & p = *i;
-
-      // Set the value in the promise if the desired property name
-      // matches.
-      if (strcmp(p.first, pname) == 0) {
-        {
-          // Release the mutex during the promise::set_value()
-          // call/notification so that we don't unnecessarily block
-          // threads attempting to get a property_changed promise.
-          typedef ivi::settings::reverse_lock<std::mutex> reverse_lock;
-          reverse_lock reverse(data->mutex);
-
-          std::lock_guard<reverse_lock> kcol(reverse);
-
-          p.second->set_value(std::move(value));
-        }
-
-        // Done with the pointer to the promise.  Remove it from the
-        // list now since we already have an iterator to it.
-        i = data->promises.erase(i);
-      } else {
-        // Nothing to erase.  Advance to the next list element.
-        ++i;
-      }
-    }
-  }
-}
-
-// ---------------------------------------------------------------
-
 ivi::settings::connman::connman(char const * interface,
-                                char const * path)
+                                char const * path,
+                                event_callback const & e)
   : proxy_(nullptr)
-  , mutex_()
-  , promises_()
-  , data_(mutex_, promises_)
+  , event_callback_(e)
   , subscription_id_(0)
 {
   static char const name[] = "net.connman";  // Service
@@ -135,8 +76,8 @@ ivi::settings::connman::connman(char const * interface,
         path,
         nullptr,
         G_DBUS_SIGNAL_FLAGS_NONE,
-        on_property_changed,
-        &data_,
+        on_connman_signal,
+        &event_callback_,
         nullptr);
 }
 
@@ -169,26 +110,6 @@ ivi::settings::connman::set_property(char const * property,
                                 &error);
 }
 
-ivi::settings::connman::shared_promise_type
-ivi::settings::connman::get_property_changed_promise(
-  char const * property)
-{
-  // This promise must exist long enough for the value to retrieved
-  // from the future.  Use a shared_ptr<> to make that possible.
-  shared_promise_type promise = std::make_shared<promise_type>();
-
-  {
-    std::lock_guard<std::mutex> lock(mutex_);
-
-    // Add a new std::promise to the promises list.  The promise will
-    // only be used once, and will be removed once its value has been
-    // set.
-    promises_.push_back(std::make_pair(property, promise));
-  }
-
-  return promise;
-}
-
 
 // Local Variables:
 // mode:c++
index fa51caa..60deb31 100644 (file)
 
 #include <settingsd/glib_traits.hpp>
 #include <settingsd/smart_ptr.hpp>
+#include <settingsd/event_callback.hpp>
 
 #include <gio/gio.h>
 
-#include <future>
-#include <utility>
-#include <list>
-#include <memory>
-
 
 namespace ivi
 {
@@ -62,40 +58,12 @@ namespace ivi
        * @param[in] path      Connman D-Bus object path.
        */
       connman(char const * interface,
-              char const * path);
+              char const * path,
+              event_callback const & e);
 
       /// Destructor.
       ~connman();
 
-      /// The type held by the @c future containing the async result.
-      typedef smart_ptr<GVariant> future_value_type;
-
-      /// The @c future type returned by the @c promise.
-      typedef std::future<future_value_type>  future_type;
-
-      /// The @c promise that provides the async result.
-      typedef std::promise<future_value_type> promise_type;
-
-      /**
-       * Smart pointer to the @c promise that provides the async
-       * result.
-       */
-      typedef std::shared_ptr<promise_type> shared_promise_type;
-
-      /**
-       * Property Name/value pair type held by the @c future
-       * containing the async result.  Both values will be populated
-       * when retrieving the value from the @c PropertyChanged
-       * signal.
-       *
-       * @note The property name is meant for internal use.
-       */
-      typedef std::pair<char const *,
-                        shared_promise_type> promise_value_type;
-
-      /// List type for promises to be updated with async result.
-      typedef std::list<promise_value_type> promise_list_type;
-
       /**
        * Set @a property to given @a value on the underlying connman
        * object.
@@ -113,14 +81,6 @@ namespace ivi
                               GVariant * value,
                               GError *& error);
 
-      /**
-       * Get a promise that will contain the changed connman property
-       * when it becomes available.  The user must obtain the value
-       * from the corresponding future object.
-       */
-      shared_promise_type get_property_changed_promise(
-        char const * property);
-
       /// Get pointer to underlying GDBusProxy.
       GDBusProxy * proxy() const { return proxy_; }
 
@@ -138,46 +98,13 @@ namespace ivi
         return g_dbus_proxy_get_object_path(proxy_);
       }
 
-      /**
-       * @struct user_data
-       *
-       * @brief Struct passed to @c ServicesChanged signal handler.
-       */
-      struct user_data
-      {
-        /// Constructor.
-        user_data(std::mutex & m, promise_list_type & p)
-          : mutex(m)
-          , promises(p)
-        {
-        }
-
-        /**
-         * References to mutex used to synchronize access to the list
-         * of promises.
-         */
-        std::mutex & mutex;
-
-        /// List of promises to be updated with the changed services.
-        promise_list_type & promises;
-      };
-
     private:
 
       /// The proxy used to access the Connman D-Bus API.
       GDBusProxy * proxy_;
 
-      /// Mutex used to synchronize access to the promises list.
-      std::mutex mutex_;
-
-      /**
-       * List of promises that will be updated with the
-       * PropertyChanged signal results.
-       */
-      promise_list_type promises_;
-
-      /// User data passed to @c PropertyChanged signal handler.
-      user_data data_;
+      /// Callback through which events will be sent to clients.
+      event_callback event_callback_;
 
       /// PropertyChanged signal subscription ID.
       guint subscription_id_;
index 0be40e8..594671f 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * @file connman_manager.cpp
  *
- * @brief Connman_Manager-based settings plugin.
+ * @brief Connman Manager operations.
  *
  * @author Ossama Othman @<ossama.othman@@intel.com@>
  *
  */
 
 #include "connman_manager.hpp"
+#include "signal_callback.hpp"
 
-#include <settingsd/json_glib_traits.hpp>
-#include <settingsd/reverse_lock.hpp>
+#include <settingsd/glib_traits.hpp>
+#include <settingsd/smart_ptr.hpp>
 
 #include <cstring>
 
 
-namespace
-{
-  void
-  on_services_changed(GDBusConnection * /* connection */,
-                      char const * /* sender_name */,
-                      char const * /* object_path */,
-                      char const * /* interface_name */,
-                      char const * /* signal_name */,
-                      GVariant   * parameters,
-                      gpointer     user_data)
-  {
-    gsize const num_params = g_variant_n_children(parameters);
-    if (num_params != 2) {
-      // We should never get here!
-      g_printerr("Number of ServicesChanged signal parameters "
-                 "is not 2: %" G_GSIZE_FORMAT "\n",
-                 num_params);
-
-      return;
-    }
-
-    using namespace ivi::settings;
-
-    // Changed services are found in the first ServicesChanged
-    // argument.
-    smart_ptr<GVariant> const changed_services(
-      g_variant_get_child_value(parameters, 0));
-
-    // Serialize the changed services into a JSON tree.  This will be
-    // an array of [object, dict], where "object" is the D-Bus object
-    // path, and "dict" is a dictionary of object-specific
-    // properties.
-    smart_ptr<JsonNode> services(
-      json_gvariant_serialize(changed_services.get()));
-
-    // Notify callers about the scan results.
-    typedef connman_manager::user_data user_data_type;
-
-    user_data_type * const p = static_cast<user_data_type *>(user_data);
-    connman_manager::promise_list_type & promises = p->promises;
-
-    // Synchronize access to the promises list.
-    std::lock_guard<std::mutex> lock(p->mutex);
-
-    // Note that the end() iterator must be retrieved during each loop
-    // iteration in case another thread caused another promise to be
-    // added to the list when the lock was temporarily unlocked during
-    // the promise::set_value() call below.
-    for (auto i = promises.begin(); i != promises.end(); ) {
-      auto & promise = *i;
-
-      {
-        // Release the mutex during the promise::set_value()
-        // call/notification so that we don't unnecessarily block
-        // threads attempting to get a services_changed promise.
-        typedef ivi::settings::reverse_lock<std::mutex> reverse_lock;
-        reverse_lock reverse(p->mutex);
-
-        std::lock_guard<reverse_lock> kcol(reverse);
-
-        // Thread that owns the corresponding future must transfer
-        // ownership of the JsonNode to something that will release
-        // the underlying JsonNode, e.g. a JsonBuilder via
-        // json_builder_add_value() or another smart_ptr<JsonNode>.
-        promise->set_value(std::move(services));
-      }
-
-      // Done with the pointer to the promise.  Remove it from the
-      // list now since we already have an iterator to it.
-      i = promises.erase(i);
-    }
-  }
-
-}
-
-// ---------------------------------------------------------------
-
-ivi::settings::connman_manager::connman_manager()
+ivi::settings::connman_manager::connman_manager(
+  event_callback const & e)
   : connman_("net.connman.Manager",     // Interface
-             "/")                       // Object path
-  , mutex_()
-  , promises_()
-  , data_(mutex_, promises_)
+             "/",                       // Object path
+             e)
+  , event_callback_(e)
   , subscription_id_(
       g_dbus_connection_signal_subscribe(
         g_dbus_proxy_get_connection(G_DBUS_PROXY(connman_.proxy())),
@@ -124,8 +48,8 @@ ivi::settings::connman_manager::connman_manager()
         connman_.object_path(),
         nullptr,
         G_DBUS_SIGNAL_FLAGS_NONE,
-        on_services_changed,
-        &data_,
+        on_connman_signal,
+        &event_callback_,
         nullptr))
 {
 }
@@ -201,25 +125,6 @@ ivi::settings::connman_manager::get_services(GError *& error) const
                                 &error);
 }
 
-ivi::settings::connman_manager::shared_promise_type
-ivi::settings::connman_manager::get_services_changed_promise()
-{
-  // This promise must exist long enough for the value to retrieved
-  // from the future.  Use a shared_ptr<> to make that possible.
-  shared_promise_type p = std::make_shared<promise_type>();
-
-  {
-    std::lock_guard<std::mutex> lock(mutex_);
-
-    // Add a new std::promise to the promises list.  The promise will
-    // only be used once, and will be removed once its value has been
-    // set.
-    promises_.push_back(p);
-  }
-
-  return p;
-}
-
 
 // Local Variables:
 // mode:c++
index 352b9fe..1361df5 100644 (file)
 
 #include "connman.hpp"
 
-#include <settingsd/glib_traits.hpp>
-#include <settingsd/smart_ptr.hpp>
+#include <settingsd/event_callback.hpp>
 
 #include <gio/gio.h>
-#include <json-glib/json-glib.h>
 
-#include <future>
-#include <list>
-#include <memory>
+#include <string>
 
 
 namespace ivi
@@ -57,7 +53,7 @@ namespace ivi
     public:
 
       /// Constructor.
-      connman_manager();
+      connman_manager(event_callback const & e);
 
       /// Destructor.
       ~connman_manager();
@@ -89,72 +85,13 @@ namespace ivi
        */
       GVariant * get_services(GError *& error) const;
 
-      /// The type held by the @c future containing the async result.
-      typedef smart_ptr<JsonNode>             future_value_type;
-
-      /// The @c future type returned by the @c promise.
-      typedef std::future<future_value_type>  future_type;
-
-      /// The @c promise that provides the async result.
-      typedef std::promise<future_value_type> promise_type;
-
-      /**
-       * Smart pointer to the @c promise that provides the async
-       * result.
-       */
-      typedef std::shared_ptr<promise_type> shared_promise_type;
-
-      /// List type for promises to be updated with async result.
-      typedef std::list<shared_promise_type> promise_list_type;
-
-      /**
-       * Get a promise that will contain changed connman services when
-       * they become available.  The user must obtain the value from
-       * the corresponding future object and release the @c JsonNode*
-       * value contained within that future with @c json_node_free().
-       */
-      shared_promise_type get_services_changed_promise();
-
-      /**
-       * @struct user_data
-       *
-       * @brief Struct passed to @c ServicesChanged signal handler.
-       */
-      struct user_data
-      {
-        /// Constructor.
-        user_data(std::mutex & m, promise_list_type & p)
-          : mutex(m)
-          , promises(p)
-        {
-        }
-
-        /**
-         * References to mutex used to synchronize access to the list
-         * of promises.
-         */
-        std::mutex & mutex;
-
-        /// List of promises to be updated with the changed services.
-        promise_list_type & promises;
-      };
-
     private:
 
       /// The proxy used to access the connman Manager D-Bus API.
       connman connman_;
 
-      /// Mutex used to synchronize access to the promises list.
-      std::mutex mutex_;
-
-      /**
-       * List of promises that will be updated with the
-       * ServicesChanged signal results.
-       */
-      promise_list_type promises_;
-
-      /// User data passed to @c ServicesChanged signal handler.
-      user_data data_;
+      /// Callback through which events will be sent to clients.
+      event_callback event_callback_;
 
       /// ServicesChanged signal subscription ID.
       guint const subscription_id_;
index 3e8699c..5a015b7 100644 (file)
@@ -36,8 +36,8 @@ namespace
 
 // ----------------------------------------------------------------------
 
-ivi::settings::ethernet::ethernet()
-  : technology_(technology_name)
+ivi::settings::ethernet::ethernet(event_callback const & e)
+  : technology_(technology_name, e)
 {
 }
 
index 65bf7fe..2bfa2fb 100644 (file)
@@ -55,7 +55,7 @@ namespace ivi
     public:
 
       /// Constructor.
-      ethernet();
+      ethernet(event_callback const & e);
 
       /// Destructor.
       virtual ~ethernet();
index 2719f3d..a072f25 100644 (file)
 #include <memory>
 
 
+namespace ivi
+{
+  namespace settings
+  {
+    class event_callback;
+  }
+}
+
 /// Plugin factory/registration function.
 extern "C" IVI_SETTINGS_CONNMAN_API bool
-register_settings(ivi::settings::registrar & r)
+register_settings(ivi::settings::registrar & r,
+                  ivi::settings::event_callback const & e)
 {
   std::unique_ptr<ivi::settings::plugin> bt(
-    new ivi::settings::bluetooth);
+    new ivi::settings::bluetooth(e));
 
   std::unique_ptr<ivi::settings::plugin> clk(
-    new ivi::settings::clock);
+    new ivi::settings::clock(e));
 
   std::unique_ptr<ivi::settings::plugin> eth(
-    new ivi::settings::ethernet);
+    new ivi::settings::ethernet(e));
 
   std::unique_ptr<ivi::settings::plugin> wifi(
-    new ivi::settings::wifi);
+    new ivi::settings::wifi(e));
 
   return
     r.register_setting(std::move(bt))
index ecf267d..968dd2d 100644 (file)
 #include <chrono>
 
 
-ivi::settings::service::service(std::string service_path)
+ivi::settings::service::service(std::string service_path,
+                                event_callback const & e)
   : connman_("net.connman.Service",     // Interface
-             service_path.c_str())      // Object path
+             service_path.c_str(),      // Object path
+             e)
 {
 }
 
index a10268e..fc1f289 100644 (file)
@@ -54,8 +54,11 @@ namespace ivi
        *
        * @param[in] service_path The D-Bus object path for connman
        *                         service.
+       * @param[in] e            Callback through which events will be
+       *                         sent to clients.
        */
-      service(std::string service_path);
+      service(std::string service_path,
+              event_callback const & e);
 
       /// Connect to the service.
       void connect(response_callback response);
diff --git a/plugins/connman/signal_callback.cpp b/plugins/connman/signal_callback.cpp
new file mode 100644 (file)
index 0000000..899707c
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * @file signal_callback.cpp
+ *
+ * @brief Connman signal callback.
+ *
+ * @author Ossama Othman @<ossama.othman@@intel.com@>
+ *
+ * @copyright @par
+ * Copyright 2013 Intel Corporation All Rights Reserved.
+ * @par
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ * @par
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * @par
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ */
+
+#include "signal_callback.hpp"
+
+#include <settingsd/event_callback.hpp>
+
+#include <json-glib/json-glib.h>
+
+
+void
+on_connman_signal(GDBusConnection * /* connection */,
+                  char const * /* sender_name */,
+                  char const * object_path,
+                  char const * interface_name,
+                  char const * signal_name,
+                  GVariant   * parameters,
+                  gpointer     user_data)
+{
+  using ivi::settings::event_callback;
+  event_callback * const e = static_cast<event_callback *>(user_data);
+
+  // Send event to clients.
+
+  /**
+   * @todo Fix type == nullptr.
+   */
+  e->send_event(
+    object_path,
+    interface_name,
+    signal_name,
+    [parameters](JsonBuilder * builder)
+    {
+      // Serialize the changed services into a JSON tree.
+      // The ServicesChanged signal parameters are:
+      //
+      //   array{object, dict}, array{object})
+      //
+      // where "object" is the D-Bus object path, and "dict" is a
+      // dictionary of object-specific properties.  The first
+      // parameter is list of changed services.  The second is a
+      // list of removed services.
+
+      /**
+       * @todo Can @c json_gvariant_serialize() ever return a
+       *       @c nullptr?
+       */
+      JsonNode * const services = json_gvariant_serialize(parameters);
+
+      json_builder_set_member_name(builder, "value");
+      json_builder_add_value(builder, services);
+
+      // No need to free the JsonNode.  The builder will take
+      // ownership of it.
+    });
+}
+
+
+// Local Variables:
+// mode:c++
+// c-basic-offset:2
+// indent-tabs-mode: nil
+// End:
diff --git a/plugins/connman/signal_callback.hpp b/plugins/connman/signal_callback.hpp
new file mode 100644 (file)
index 0000000..3c781a3
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * @file signal_callback.hpp
+ *
+ * @brief Connman signal callback.
+ *
+ * @author Ossama Othman @<ossama.othman@@intel.com@>
+ *
+ * @copyright @par
+ * Copyright 2013 Intel Corporation All Rights Reserved.
+ * @par
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ * @par
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * @par
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef IVI_SETTINGS_CONNMAN_SIGNAL_CALLBACK_HPP
+#define IVI_SETTINGS_CONNMAN_SIGNAL_CALLBACK_HPP
+
+#include <gio/gio.h>
+
+
+extern "C"
+void on_connman_signal(GDBusConnection * connection,
+                       char const * sender_name,
+                       char const * object_path,
+                       char const * interface_name,
+                       char const * signal_name,
+                       GVariant   * parameters,
+                       gpointer     user_data);
+
+#endif  /* IVI_SETTINGS_CONNMAN_SIGNAL_CALLBACK_HPP */
+
+
+// Local Variables:
+// mode:c++
+// c-basic-offset:2
+// indent-tabs-mode: nil
+// End:
index bda9a1e..1ac890a 100644 (file)
 #include <chrono>
 
 
-ivi::settings::technology::technology(std::string tech)
+ivi::settings::technology::technology(std::string tech,
+                                      event_callback const & e)
   : connman_("net.connman.Technology",     // Interface
              ("/net/connman/technology/"
-              + tech).c_str())             // Object path
-  , manager_()
+              + tech).c_str(),             // Object path
+             e)
+  , manager_(e)
   , technology_(tech)
+  , event_callback_(e)
 {
 }
 
@@ -132,10 +135,6 @@ ivi::settings::technology::set_powered(JsonReader * reader,
 
   constexpr char const name[] = "Powered";
 
-  // Get notified when the technology property has changed.
-  auto property_promise = connman_.get_property_changed_promise(name);
-  auto property_future  = property_promise->get_future();
-
   GError * error = nullptr;
 
   smart_ptr<GVariant> ret(
@@ -146,27 +145,9 @@ ivi::settings::technology::set_powered(JsonReader * reader,
   smart_ptr<GError> safe_error(error);
 
   if (ret != nullptr) {
-    constexpr int const timeout = 5000;  // milliseconds
-
-    // Block until the technology property has changed..
-    std::future_status const status =
-      property_future.wait_for(std::chrono::milliseconds(timeout));
-
-    if (status == std::future_status::ready) {
-
       // Nothing to add to successful response.
       response.send_response(
         [](JsonBuilder * /* builder */) {});
-
-    } else {
-
-      response.send_error(
-        std::string("Wait for enable status failed: ")
-        + (status == std::future_status::timeout
-           ? "timeout"
-           : "deferred"));
-
-    }
   } else if (error != nullptr) {
     response.send_error(
       "Unable to set " + technology_ + " powered state: "
@@ -192,10 +173,6 @@ ivi::settings::technology::scan(JsonReader * reader,
     return;
   }
 
-  // Get notified when scan results are available.
-  auto services_promise = manager_.get_services_changed_promise();
-  auto services_future  = services_promise->get_future();
-
   // The scan could take a while.
   constexpr gint const timeout = 10000;  // milliseconds
   GError * error = nullptr;
@@ -212,32 +189,7 @@ ivi::settings::technology::scan(JsonReader * reader,
   smart_ptr<GError> safe_error(error);
 
   if (ret != nullptr) {
-    constexpr gint const future_timeout = 5000;  // milliseconds
-
-    // Block until the scan results are in.
-    std::future_status const status =
-      services_future.wait_for(std::chrono::milliseconds(future_timeout));
-
-    if (status != std::future_status::deferred) {
-      // Don't call get() if the future isn't ready.  Otherwise this
-      // will be a blocking call.
-      if (status == std::future_status::ready) {
-        smart_ptr<JsonNode> const changed_services(services_future.get());
-      }
-
-      send_services(response, error);
-
-      // A timeout is okay since some services may not have changed
-      // within the timeout period but log it just in case.
-      if (status == std::future_status::timeout)
-        g_debug("%s settings: Timed out waiting for changed services.\n",
-                technology_.c_str());
-    } else {
-      // We only get here if the future status is
-      // std::future_status::deferred, meaning the result hasn't been
-      // determined yet.  That should never happen in our case.
-      response.send_error("Wait for scan results failed: deferred");
-    }
+    send_services(response, error);
   } else if (error != nullptr) {
     response.send_error(
       "Unable to scan " + technology_ + ": "
@@ -260,7 +212,7 @@ ivi::settings::technology::connect(JsonReader * reader,
 
   /// @todo Refactor malformed JSON request handling code.
   if (service_path != nullptr) {
-    service s(service_path);
+    service s(service_path, event_callback_);
     s.connect(response);
   } else {
     response.send_error(
@@ -279,7 +231,7 @@ ivi::settings::technology::disconnect(JsonReader * reader,
   json_reader_end_member(reader);
 
   if (service_path != nullptr) {
-    service s(service_path);
+    service s(service_path, event_callback_);
     s.disconnect(response);
   } else {
     response.send_error(
index 44fdef6..44025bc 100644 (file)
@@ -55,8 +55,11 @@ namespace ivi
        * Constructor.
        *
        * @param[in] tech The connman technology, e.g. "bluetooth".
+       * @param[in] e    Callback through which events will be sent to
+       *                 clients.
        */
-      technology(std::string tech);
+      technology(std::string tech,
+                 event_callback const & e);
 
       /// Handle requests common to all connman technologies.
       void handle_request(std::string request,
@@ -117,12 +120,21 @@ namespace ivi
       /// The proxy used to access the connman Technology D-Bus API.
       connman connman_;
 
-      /// The proxy used to access the connman Manager D-Bus API.
+      /**
+       * The proxy used to access the connman Manager D-Bus API.
+       *
+       * @todo There is no point in making this @c connman_manager
+       *       instance technology-specific since it is really a
+       *       connman global object.
+       */
       connman_manager manager_;
 
       /// Technology name, e.g. "bluetooth" or "wifi".
       std::string const technology_;
 
+      /// Callback through which events will be sent to clients.
+      event_callback event_callback_;
+
     };
 
   }
index 15bebe6..8cc4c03 100644 (file)
@@ -36,8 +36,8 @@ namespace
 
 // ----------------------------------------------------------------------
 
-ivi::settings::wifi::wifi()
-  : technology_(technology_name)
+ivi::settings::wifi::wifi(event_callback const & e)
+  : technology_(technology_name, e)
 {
 }
 
index d82c82b..24cb5bd 100644 (file)
@@ -57,7 +57,7 @@ namespace ivi
     public:
 
       /// Constructor.
-      wifi();
+      wifi(event_callback const & e);
 
       /// Destructor.
       virtual ~wifi();
index eab3698..c9b15da 100644 (file)
@@ -55,12 +55,12 @@ namespace
     switch(reason) {
     case LWS_CALLBACK_ESTABLISHED:
       // Enable event dispatching to client.
-      manager->subscribe_client(wsi);
+      mgr->subscribe_client(wsi);
       break;
 
     case LWS_CALLBACK_CLOSED:
       // Disable event dispatching to client.
-      manager->unsubscribe_client(wsi);
+      mgr->unsubscribe_client(wsi);
       break;
 
     case LWS_CALLBACK_RECEIVE: