From: Ossama Othman Date: Wed, 11 Dec 2013 23:43:22 +0000 (-0800) Subject: Fix TIVI-{1191,1192,2108} by implementing a connman 'Agent'. X-Git-Tag: accepted/tizen/ivi/20131214.022856^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3b597925ae11ce329673f1a3322b81d2a2da6708;p=profile%2Fivi%2Fsettings-daemon.git Fix TIVI-{1191,1192,2108} by implementing a connman 'Agent'. Change-Id: Ibd0277bb3252b33b30f96b0221938aab161128a0 Signed-off-by: Ossama Othman --- diff --git a/.gitignore b/.gitignore index a1c8282..3d01d77 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ src/settingsd tests/settings_test config.* !plugins/connman/config.h +plugins/connman/settings-agent.conf !packaging/settingsd.socket *.tar.* doc/doxygen diff --git a/configure.ac b/configure.ac index 4fb9240..89b4b2f 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ]) -AC_INIT([settingsd], [0.3], [ossama.othman@intel.com]) +AC_INIT([settingsd], [0.4], [ossama.othman@intel.com]) AM_INIT_AUTOMAKE([1.11.1 foreign -Wall -Werror -Wno-portability silent-rules]) LT_INIT([disable-static dlopen]) diff --git a/include/settingsd/event_callback.hpp b/include/settingsd/event_callback.hpp index 910082a..54e9013 100644 --- a/include/settingsd/event_callback.hpp +++ b/include/settingsd/event_callback.hpp @@ -73,7 +73,7 @@ namespace ivi * formatted event data. */ bool send_event( - std::function event_builder); + std::function event_builder) const; private: @@ -84,12 +84,12 @@ namespace ivi * The appropriate "header" information will be prepended to the * event. */ - unique_ptr begin_event(); + unique_ptr begin_event() const; /** * End the JSON formatted event to the Settings app request. */ - void end_event(unique_ptr const & builder); + void end_event(unique_ptr const & builder) const; private: diff --git a/include/settingsd/glib_traits.hpp b/include/settingsd/glib_traits.hpp index 7fc4d9c..c2daedf 100644 --- a/include/settingsd/glib_traits.hpp +++ b/include/settingsd/glib_traits.hpp @@ -53,7 +53,6 @@ namespace ivi }; }; - // GVariant traits specialization. template<> struct traits diff --git a/include/settingsd/response_callback.hpp b/include/settingsd/response_callback.hpp index 02936ef..d3f6014 100644 --- a/include/settingsd/response_callback.hpp +++ b/include/settingsd/response_callback.hpp @@ -77,14 +77,14 @@ namespace ivi * JSON formatted response data. */ bool send_response( - std::function response_builder); + std::function response_builder) const; /** * Send error response to Settings app. * * @param[in] error_message Free form error message. */ - bool send_error(std::string error_message); + bool send_error(std::string error_message) const; /** * The settings type/ID associated with the request and @@ -103,7 +103,7 @@ namespace ivi */ void add_string_value(JsonBuilder * builder, std::string const & name, - std::string const & value); + std::string const & value) const; /** * Begin the JSON formatted response to the Settings app @@ -115,12 +115,12 @@ namespace ivi * * @param[in] result @c "succeeded" or @c "failed" */ - unique_ptr begin_response(char const * result); + unique_ptr begin_response(char const * result) const; /** * End the JSON formatted response to the Settings app request. */ - void end_response(unique_ptr const & builder); + void end_response(unique_ptr const & builder) const; private: diff --git a/include/settingsd/send_callback.hpp b/include/settingsd/send_callback.hpp index e0af875..835b84d 100644 --- a/include/settingsd/send_callback.hpp +++ b/include/settingsd/send_callback.hpp @@ -72,7 +72,7 @@ namespace ivi * pre-marshalled (in-memory) payload. */ bool send_payload(char const * send_type, - unique_ptr const & builder); + unique_ptr const & builder) const; /** * Check if the WebSocket instance @a wsi corresponds to this diff --git a/lib/event_callback.cpp b/lib/event_callback.cpp index 12cc69c..b4bf113 100644 --- a/lib/event_callback.cpp +++ b/lib/event_callback.cpp @@ -37,7 +37,7 @@ ivi::settings::event_callback::event_callback(manager & mgr) bool ivi::settings::event_callback::send_event( - std::function event_builder) + std::function event_builder) const { unique_ptr const builder = begin_event(); @@ -50,7 +50,7 @@ ivi::settings::event_callback::send_event( } ivi::settings::unique_ptr -ivi::settings::event_callback::begin_event() +ivi::settings::event_callback::begin_event() const { // Construct JSON event string. unique_ptr safe_builder(json_builder_new()); @@ -66,7 +66,7 @@ ivi::settings::event_callback::begin_event() void ivi::settings::event_callback::end_event( - ivi::settings::unique_ptr const & builder) + ivi::settings::unique_ptr const & builder) const { json_builder_end_object(builder.get()); } diff --git a/lib/manager.cpp b/lib/manager.cpp index 5ca8d2b..3f8ad7c 100644 --- a/lib/manager.cpp +++ b/lib/manager.cpp @@ -210,7 +210,7 @@ ivi::settings::manager::unsubscribe_client(libwebsocket * wsi) bool ivi::settings::manager::send_event( - unique_ptr const & builder) + unique_ptr const & builder) const { bool success = true; diff --git a/lib/manager.hpp b/lib/manager.hpp index aea12fd..95f9ea9 100644 --- a/lib/manager.hpp +++ b/lib/manager.hpp @@ -103,7 +103,7 @@ namespace ivi * * @return @c true on success. */ - bool send_event(unique_ptr const & builder); + bool send_event(unique_ptr const & builder) const; private: @@ -150,7 +150,7 @@ namespace ivi map_type settings_; /// Mutex used to synchronize access to the send_callback list. - std::mutex mutex_; + mutable std::mutex mutex_; /** * List of client WebSockets to be used for sending events to diff --git a/lib/response_callback.cpp b/lib/response_callback.cpp index 24bf616..eb77a86 100644 --- a/lib/response_callback.cpp +++ b/lib/response_callback.cpp @@ -50,7 +50,7 @@ ivi::settings::response_callback::response_callback( bool ivi::settings::response_callback::send_response( - std::function response_builder) + std::function response_builder) const { unique_ptr const builder = begin_response("succeeded"); @@ -73,7 +73,8 @@ ivi::settings::response_callback::send_response( } bool -ivi::settings::response_callback::send_error(std::string error_message) +ivi::settings::response_callback::send_error( + std::string error_message) const { unique_ptr builder = begin_response("failed"); @@ -98,7 +99,7 @@ void ivi::settings::response_callback::add_string_value( JsonBuilder * builder, std::string const & name, - std::string const & value) + std::string const & value) const { json_builder_set_member_name(builder, name.c_str()); if (value.empty()) @@ -108,7 +109,8 @@ ivi::settings::response_callback::add_string_value( } ivi::settings::unique_ptr -ivi::settings::response_callback::begin_response(char const * result) +ivi::settings::response_callback::begin_response( + char const * result) const { // Construct JSON response. unique_ptr safe_builder(json_builder_new()); @@ -127,7 +129,7 @@ ivi::settings::response_callback::begin_response(char const * result) void ivi::settings::response_callback::end_response( - ivi::settings::unique_ptr const & builder) + ivi::settings::unique_ptr const & builder) const { json_builder_end_object(builder.get()); } diff --git a/lib/send_callback.cpp b/lib/send_callback.cpp index 9330f8e..c243141 100644 --- a/lib/send_callback.cpp +++ b/lib/send_callback.cpp @@ -56,7 +56,7 @@ ivi::settings::send_callback::operator=(send_callback const & other) bool ivi::settings::send_callback::send_payload( char const * send_type, - unique_ptr const & builder) + unique_ptr const & builder) const { unique_ptr const generator(json_generator_new()); unique_ptr const root(json_builder_get_root(builder.get())); diff --git a/packaging/settingsd.changes b/packaging/settingsd.changes index 57e1c64..22029ef 100644 --- a/packaging/settingsd.changes +++ b/packaging/settingsd.changes @@ -1,3 +1,7 @@ +* Wed Dec 11 2013 Ossama Othman accepted/tizen/20131207.002830@9d8e1e0 +- Bump version to 0.4. +- Fix TIVI-{1191,1192,2108} by implementing a connman 'Agent'. + * Wed Dec 04 2013 Ossama Othman accepted/tizen_ivi_stable/20131119.040308@7e5f66d - Bump version to 0.3. - Support technology PropertyChanged signal reporting again. diff --git a/packaging/settingsd.spec b/packaging/settingsd.spec index 13504d6..0f5a112 100644 --- a/packaging/settingsd.spec +++ b/packaging/settingsd.spec @@ -1,6 +1,6 @@ Name: settingsd Summary: Tizen IVI Settings Daemon -Version: 0.3 +Version: 0.4 Release: 1 Group: Application Framework/Settings License: LGPL-2.1 @@ -83,6 +83,7 @@ ln -sf ../settingsd.socket %{buildroot}/%{_unitdir_user}/sockets.target.wants/ %{_libdir}/libsettings.so.* %{pkglibdir}/*.so %config %{_sysconfdir}/%{name}/* +%config %{_sysconfdir}/dbus-1/system.d/settings-agent.conf %{_unitdir_user}/settingsd.service %{_unitdir_user}/settingsd.socket %{_unitdir_user}/sockets.target.wants/settingsd.socket diff --git a/plugins/connman/Makefile.am b/plugins/connman/Makefile.am index d452e63..c883485 100644 --- a/plugins/connman/Makefile.am +++ b/plugins/connman/Makefile.am @@ -32,17 +32,23 @@ IVI_SETTINGS_PLUGIN_CPPFLAGS = \ IVI_SETTINGS_PLUGIN_LIBRARY = $(top_builddir)/lib/libsettings.la -connman_la_SOURCES = \ - dbus_connection.cpp \ - connman.cpp \ - connman_manager.cpp \ - connman_service.cpp \ - connman_technology.cpp \ - service.cpp \ - technology.cpp \ - clock.cpp \ - registration.cpp \ - subscription_manager.cpp +## The D-Bus name that will be owned by the settings daemon's connman +## Agent. +IVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME = \ + "$(IVI_SETTINGS_DBUS_NAME).connman.Agent" + +connman_la_SOURCES = \ + dbus_connection.cpp \ + connman.cpp \ + connman_manager.cpp \ + connman_service.cpp \ + connman_technology.cpp \ + service.cpp \ + technology.cpp \ + clock.cpp \ + agent.cpp \ + registration.cpp \ + subscription_manager.cpp \ $(BUILT_SOURCES) connman_la_CXXFLAGS = \ $(IVI_SETTINGS_PLUGIN_CXXFLAGS) \ @@ -51,7 +57,9 @@ connman_la_CXXFLAGS = \ connman_la_CFLAGS = \ $(IVI_SETTINGS_PLUGIN_CFLAGS) \ $(GIO_CFLAGS) -connman_la_CPPFLAGS = $(IVI_SETTINGS_PLUGIN_CPPFLAGS) +connman_la_CPPFLAGS = \ + $(IVI_SETTINGS_PLUGIN_CPPFLAGS) \ + -DIVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME='$(IVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME)' connman_la_LIBADD = $(IVI_SETTINGS_PLUGIN_LIBRARY) connman_la_LDFLAGS = -no-undefined \ -module \ @@ -70,6 +78,7 @@ noinst_HEADERS = \ service.hpp \ technology.hpp \ clock.hpp \ + agent.hpp \ subscription_manager.hpp dbus_codegen_verbose = $(dbus_codegen_verbose_$(V)) @@ -85,9 +94,32 @@ BUILT_SOURCES = \ agent-glue.c \ agent-glue.h -CLEANFILES = $(BUILT_SOURCES) +dbussystemconf_DATA = settings-agent.conf +dbussystemconfdir = $(sysconfdir)/dbus-1/system.d/ + +## Silent mode output for settingsd config file generation. +config_verbose = $(config_verbose_$(V)) +config_verbose_ = $(config_verbose_$(AM_DEFAULT_VERBOSITY)) +config_verbose_0 = @echo CONFGEN $@; + +agent_name = $(IVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME) + +## The configure script won't fully expand $libdir so leverage `make' +## based variable expansion instead. +settings-agent.conf: Makefile settings-agent.conf.in + $(config_verbose)rm -f $@ $@.tmp; \ + srcdir=''; \ + test -f ./$@.in || srcdir=$(srcdir)/; \ + sed \ + -e 's,@agent_name[@],$(agent_name),g' \ + $${srcdir}$@.in >$@.tmp; \ + chmod 644 $@.tmp; \ + mv $@.tmp $@ + + +CLEANFILES = $(BUILT_SOURCES) settings-agent.conf dist-hook: cd $(distdir); rm -f $(BUILT_SOURCES) -EXTRA_DIST = agent.xml +EXTRA_DIST = agent.xml settings-agent.conf.in diff --git a/plugins/connman/agent.cpp b/plugins/connman/agent.cpp new file mode 100644 index 0000000..97f5a14 --- /dev/null +++ b/plugins/connman/agent.cpp @@ -0,0 +1,430 @@ +/** + * @file agent.cpp + * + * @brief Settings daemon D-Bus connection adapter. + * + * @author Michael Leibowitz @ + * @author Ossama Othman @ + * + * @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 "agent.hpp" +#include "../../lib/config.hpp" + +#include +#include + +#include +#include +#include +#include + + +namespace +{ + // -------------------------------------------------------------- + // D-Bus Name Acquisition + // -------------------------------------------------------------- + /** + * @brief Callback function invoked when a D-Bus name is acquired. + * + * @param[in] connection The D-Bus connection on which the name was + * acquired. + * @param[in] name The name being owned. + * @param[in] user_data User-provided data. + */ + void + on_name_acquired(GDBusConnection * /* connection */, + gchar const * name, + gpointer /* user_data */) + { + g_debug("Name \"%s\" acquired on D-Bus.\n", name); + } + + /** + * @brief Callback function invoked when a D-Bus name is no longer + * owned or the D-Bus connection has been closed. + * + * @param[in] connection The D-Bus connection on which the name was + * acquired. + * @param[in] name The name being owned. + * @param[in] user_data User-provided data. + * + */ + void + on_name_lost(GDBusConnection * /* connection */, + gchar const * name, + gpointer /* user_data */) + { + // This can happen if the appropriate D-Bus policy is not + // installed, for example. + throw std::runtime_error(std::string("Lost name \"") + + name + "\" on D-Bus!"); + } + + // -------------------------------------------------------------- + // Connman Agent Handlers + // -------------------------------------------------------------- + /** + * @class agent_operation_handler + * + * @brief Handle agent operations through given callback. + * + * This class factors out common connection data retrieval code, and + * performs Agent operations through the given callback. + */ + class agent_operation_handler + { + typedef ivi::settings::agent agent; + typedef agent::user_data data_type; + + // Prevent copying. + agent_operation_handler(agent_operation_handler const &) = delete; + void operator=(agent_operation_handler const &) = delete; + + public: + + /** + * Constructor + * + * @param[in] service D-Bus object path for connman service + * @param[in] user_data User/connection data + * @param[in] callback Callback that implements Agent operation + */ + agent_operation_handler( + gchar const * service, + gpointer user_data, + std::function + callback) + : data_(static_cast(user_data)) + , guard_(data_->lock) + , iterator_(data_->info.find(service)) + { + // data_ should never be null! + + if (iterator_ != data_->info.end()) + callback(iterator_->second); + } + + /// Destructor + ~agent_operation_handler() + { + // Done with the connection data. Remove it from the map. + if (iterator_ != data_->info.end()) + data_->info.erase(iterator_); + } + + private: + /// User/connection data. + data_type * const data_; + + /** + * Synchronize access to the user/connection data map. + * + * @note This lock will be held for the life of the + * @c agent_operation_handler object, i.e. for the duration + * of the operation. This is needed to synchronize the map + * @c erase() operation in the destructor. The erase() + * operation is performed in the destructor to ensure + * element removal occurs if an exception is thrown by the + * callback. + */ + std::lock_guard guard_; + + /// Map iterator that points to the connection data. + agent::user_data::map_type::iterator const iterator_; + }; + + bool + on_handle_release(Agent * object, + GDBusMethodInvocation * invocation, + gpointer /* user_data */) + { + agent_complete_release(object, invocation); + return true; + } + + bool + on_handle_report_error(Agent * object, + GDBusMethodInvocation * invocation, + gchar const * service, + gchar const * error, + gpointer user_data) + { + using ivi::settings::agent; + + agent_operation_handler(service, + user_data, + [error](agent::connect_data const & d){ + d.response.send_error(std::string(error)); + }); + + agent_complete_report_error(object, invocation); + + return true; + } + + bool + on_handle_request_browser(Agent * object, + GDBusMethodInvocation * invocation, + gchar const * /* service */, + gchar const * /* url */, + gpointer /* user_data */) + { + agent_complete_request_browser(object, invocation); + return true; + } + + bool + on_handle_request_input(Agent * object, + GDBusMethodInvocation * invocation, + gchar const * service, + GVariant * fields, + gpointer user_data) + { + using ivi::settings::agent; + + agent_operation_handler( + service, + user_data, + [object, invocation, fields](agent::connect_data const & d){ + using ivi::settings::unique_ptr; + + // Prepare to build a dictionary, i.e. an array of + // dictionary entries, "a{sv}". + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + bool error_occurred = false; + bool found_field = false; + + // Extract requested mandatory and alternate fields. + GVariantIter * vi = nullptr; + g_variant_get(fields, "a{sv}", &vi); + unique_ptr const iter(vi); + + gchar * fname = nullptr; + GVariant * fvalue = nullptr; + + /** + * @todo Refactor this GVariant dictionary iteration code. + */ + while (g_variant_iter_next(vi, "{sv}", &fname, &fvalue)) { + // + unique_ptr const field(fname); + unique_ptr const arguments(fvalue); + + GVariantIter * ai = nullptr; + g_variant_get(arguments.get(), "a{sv}", &ai); + unique_ptr const iter(ai); + + gchar * argname = nullptr; + GVariant * argvalue = nullptr; + + while (g_variant_iter_next(ai, "{sv}", &argname, &argvalue)) { + unique_ptr const name(argname); + unique_ptr const value(argvalue); + + // Determine which requested fields are mandatory. + if (strcmp(argname, "Requirement") == 0 + && strcmp(g_variant_get_string(argvalue, nullptr), + "mandatory") == 0) { + // Check if we were supplied the required field, and add + // it to the dictionary result. + auto const i = d.info.find(fname); + if (i != d.info.end()) { + // Note that the dictionary is of the form a{sv} so + // we wrap the dictionary string value in a variant. + g_variant_builder_add_value( + &builder, + g_variant_new_dict_entry( + g_variant_new_string(fname), + g_variant_new_variant( + g_variant_new_string(i->second.c_str())))); + + /** + * @todo Is there any way to tell if an entry was + * added to the indefinite array being + * constructed through the glib GVariant builder + * interface? We can't get rid of this flag if + * that is the case. + */ + found_field = true; + } else { + std::string err( + std::string("Required connection field \"") + + fname + + "\" not supplied."); + + /** + * @todo This send_error() call is probably + * redundant. Look into removing it. + */ + d.response.send_error(err); + + /** + * @bug Register a settingsd-specific error during + * initialization via + * @c g_dbus_error_register_error(), and use it + * in this call instead. + */ + g_dbus_method_invocation_return_dbus_error( + invocation, + "net.connman.Agent.Error.Canceled", + err.c_str()); + + error_occurred = true; + + return; + } + } + } + } + + if (!error_occurred) { + GVariant * dictionary = nullptr; + + // We can't call g_variant_builder_end() if no children + // (dictionary entries) were added to the indefinite array + // being constructed through the builder. + if (found_field) + dictionary = g_variant_builder_end(&builder); + + // The method return value will contain a dictionary, + // i.e. "a{sv}", of the requested input fields. + agent_complete_request_input(object, invocation, dictionary); + } + }); + + return true; + } + + bool + on_handle_cancel(Agent * object, + GDBusMethodInvocation * invocation, + gpointer /* user_data */) + { + agent_complete_cancel(object, invocation); + return true; + } +} + +// ---------------------------------------------------------------- + +ivi::settings::agent::agent(GDBusConnection * connection) + : owner_id_( + // The bus name will be of the form: + // "org.tizen.settingsd.connman.Agent" + g_bus_own_name_on_connection(connection, + IVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_name_acquired, + on_name_lost, + nullptr, // user_data, + nullptr)) + , interface_(agent_skeleton_new()) + , object_path_("/" IVI_SETTINGS_DBUS_NAME "/connman/Agent") + , data_() +{ + // Construct object path of the form: + // /org/tizen/settingsd/connman/Agent" + std::replace(object_path_.begin(), object_path_.end(), '.', '/'); + + // Connect the signal handlers corresponding to the Agent + // interface. + g_signal_connect(interface_, + "handle-release", + G_CALLBACK(on_handle_release), + &data_); + + g_signal_connect(interface_, + "handle-report-error", + G_CALLBACK(on_handle_report_error), + &data_); + + g_signal_connect(interface_, + "handle-request-browser", + G_CALLBACK(on_handle_request_browser), + &data_); + + g_signal_connect(interface_, + "handle-request-input", + G_CALLBACK(on_handle_request_input), + &data_); + + g_signal_connect(interface_, + "handle-cancel", + G_CALLBACK(on_handle_cancel), + &data_); + + // Export the interface on the bus. + GError * error = nullptr; + if (!g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(interface_), + connection, + object_path_.c_str(), + &error)) { + unique_ptr safe_error(error); + throw std::runtime_error(error->message); + } +} + +ivi::settings::agent::~agent() +{ + if (interface_ != nullptr) { + g_dbus_interface_skeleton_unexport( + G_DBUS_INTERFACE_SKELETON(interface_)); + g_object_unref(interface_); + } + + g_bus_unown_name(owner_id_); +} + +bool +ivi::settings::agent::register_connect_data( + char const * service_path, + std::map && info, + response_callback const & response) +{ + connect_data data(std::move(info), response); + + std::lock_guard guard(data_.lock); + + auto const result = + data_.info.insert( + std::make_pair(service_path, std::move(data))); + + return result.second; +} + +bool +ivi::settings::agent::deregister_connect_data(char const * service_path) +{ + std::lock_guard guard(data_.lock); + + return data_.info.erase(service_path) == 1; +} + + +// Local Variables: +// mode:c++ +// c-basic-offset:2 +// indent-tabs-mode: nil +// End: diff --git a/plugins/connman/agent.hpp b/plugins/connman/agent.hpp new file mode 100644 index 0000000..65159f2 --- /dev/null +++ b/plugins/connman/agent.hpp @@ -0,0 +1,188 @@ +/** + * @file agent.hpp + * + * @brief Connman agent implementation header. + * + * @author Ossama Othman @ + * + * @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; either + * version 2.1 of the License, or (at your option) any later version. + * @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 + * + * @note This is an internal header. + */ + +#ifndef IVI_SETTINGS_AGENT_HPP +#define IVI_SETTINGS_AGENT_HPP + +#include "agent-glue.h" + +#include + +#include +#include +#include + + +namespace ivi +{ + namespace settings + { + class agent + { + public: + + typedef std::map connect_info_map_type; + + /** + * Constructor. + * + * @param[in] connection D-Bus connection on which agent will be + * exported. + */ + agent(GDBusConnection *connection); + + /// Destructor. + ~agent(); + + /** + * Register data that may be needed when handling Connman Agent + * method calls. + */ + bool register_connect_data(char const * service_path, + connect_info_map_type && info, + response_callback const & response); + + /** + * Deregister connectio data. + */ + bool deregister_connect_data(char const * service_path); + + /// Get D-Bus object path for this @c Agent. + char const * + object_path() const + { + return object_path_.c_str(); + } + + /** + * @struct connect_data + * + * @brief Connman Service object-specific connect data. + * + * An instance of this structure will be created each time a + * Connman connection operation will be performed in case + * Connman needs additional information, such as a passphrase, + * to complete a connection. + */ + struct connect_data + { + /** + * Constructor. + * + * @param[in] info_map Map of Connman Agent API field name to + * value. + * @param[in] resp Callback object through which responses + * will be sent to the caller + * (e.g. Settings app). + */ + connect_data(connect_info_map_type && info_map, + response_callback const & resp) + : info(info_map) + , response(resp) + { + } + + /// Move constructor. + connect_data(connect_data && other) + : info(std::move(other.info)) + , response(other.response) + { + } + + /// Disallow copy construction and assignment. + connect_data(connect_data const &) = delete; + void operator=(connect_data const &) = delete; + + /** + * Map of Connman Agent API field name to value. + */ + std::map info; + + /** + * The response callback through errors will be sent to the + * caller. + */ + response_callback response; + }; + + /** + * @struct user_data + * + * @brief Wrapper for data passed to Connman Agent method + * handlers. + */ + struct user_data + { + /// Synchronize access to the response/request data map. + std::mutex lock; + + typedef std::map map_type; + + /** + * Map of service D-Bus object path to request/response + * data. + */ + map_type info; + }; + + private: + + /** + * @name Prevent copying + */ + //@{ + agent(agent const &) = delete; + agent & operator=(agent const &) = delete; + //@} + + private: + + /// ID corresponding to owned name on D-Bus system bus. + guint const owner_id_; + + /// GDBus Agent skeleton object. + Agent * const interface_; + + /// D-Bus object path for this @c Agent. + std::string object_path_; + + /// Service connect map, data, etc. + user_data data_; + + }; + } +} + +#endif /* IVI_SETTINGS_AGENT_HPP */ + + +// Local Variables: +// mode:c++ +// c-basic-offset:2 +// indent-tabs-mode: nil +// End: diff --git a/plugins/connman/agent.xml b/plugins/connman/agent.xml index 1ba7ae2..2435573 100644 --- a/plugins/connman/agent.xml +++ b/plugins/connman/agent.xml @@ -49,6 +49,7 @@ + diff --git a/plugins/connman/clock.cpp b/plugins/connman/clock.cpp index f0ff628..fcb8fa3 100644 --- a/plugins/connman/clock.cpp +++ b/plugins/connman/clock.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,12 +38,28 @@ ivi::settings::clock::clock(GDBusConnection * connection, event_callback const & e) - : connman_("net.connman.Clock", "/", connection, e) + : connman_("net.connman.Clock", "/", connection) + , event_callback_(e) + , subscription_id_( + g_dbus_connection_signal_subscribe( + connection, + nullptr, + connman_.interface_name(), + "PropertyChanged", + connman_.object_path(), + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_dbus_signal, + &event_callback_, + nullptr)) { } ivi::settings::clock::~clock() { + g_dbus_connection_signal_unsubscribe( + connman_.connection(), + subscription_id_); } std::string const & diff --git a/plugins/connman/clock.hpp b/plugins/connman/clock.hpp index 636c6b9..64a1796 100644 --- a/plugins/connman/clock.hpp +++ b/plugins/connman/clock.hpp @@ -36,6 +36,7 @@ #include "connman.hpp" #include +#include #include @@ -119,6 +120,12 @@ namespace ivi /// Underlying connman proxy. connman connman_; + /// Callback through which events will be sent to clients. + event_callback event_callback_; + + /// PropertyChanged signal subscription ID. + guint const subscription_id_; + }; } diff --git a/plugins/connman/connman.cpp b/plugins/connman/connman.cpp index 707111b..f2b586a 100644 --- a/plugins/connman/connman.cpp +++ b/plugins/connman/connman.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -37,11 +36,8 @@ ivi::settings::connman::connman(char const * interface, char const * path, - GDBusConnection * connection, - event_callback const & e) + GDBusConnection * connection) : proxy_(nullptr) - , event_callback_(e) - , subscription_id_(0) { static char const name[] = "net.connman"; // Service @@ -67,31 +63,12 @@ ivi::settings::connman::connman(char const * interface, throw std::runtime_error(error->message); } - - // Listen for changes to properties. - subscription_id_ = - g_dbus_connection_signal_subscribe( - connection, - nullptr, - interface, - "PropertyChanged", - path, - nullptr, - G_DBUS_SIGNAL_FLAGS_NONE, - on_dbus_signal, - &event_callback_, - nullptr); } ivi::settings::connman::~connman() { - if (proxy_ != nullptr) { - g_dbus_connection_signal_unsubscribe( - g_dbus_proxy_get_connection(G_DBUS_PROXY(proxy_)), - subscription_id_); - + if (proxy_ != nullptr) g_object_unref(proxy_); - } } GVariant * diff --git a/plugins/connman/connman.hpp b/plugins/connman/connman.hpp index de4a7b4..e722c99 100644 --- a/plugins/connman/connman.hpp +++ b/plugins/connman/connman.hpp @@ -28,8 +28,6 @@ #ifndef IVI_SETTINGS_CONNMAN_HPP #define IVI_SETTINGS_CONNMAN_HPP -#include - #include @@ -55,13 +53,10 @@ namespace ivi * @param[in] interface Connman D-Bus interface. * @param[in] path Connman D-Bus object path. * @param[in] connection Underlying D-Bus connection. - * @param[in] e Callback through which events will be - * sent to clients. */ connman(char const * interface, char const * path, - GDBusConnection * connection, - event_callback const & e); + GDBusConnection * connection); /// Destructor. ~connman(); @@ -112,12 +107,6 @@ namespace ivi /// The proxy used to access the Connman D-Bus API. GDBusProxy * proxy_; - /// Callback through which events will be sent to clients. - event_callback event_callback_; - - /// PropertyChanged signal subscription ID. - guint subscription_id_; - }; } diff --git a/plugins/connman/connman_manager.cpp b/plugins/connman/connman_manager.cpp index 79a70eb..dae076a 100644 --- a/plugins/connman/connman_manager.cpp +++ b/plugins/connman/connman_manager.cpp @@ -32,6 +32,7 @@ #include #include +#include // ---------------------------------------------------------------------- @@ -107,8 +108,7 @@ ivi::settings::connman_manager::connman_manager( event_callback const & e) : connman_("net.connman.Manager", // Interface "/", // Object path - connection, - e) + connection) , data_(connection, e) , technology_added_id_(subscribe_to_signal(connection, "TechnologyAdded", @@ -122,6 +122,7 @@ ivi::settings::connman_manager::connman_manager( "ServicesChanged", on_dbus_signal, &data_.callback)) + , agent_(connection) { // The ServicesChanged signal parameters are: // @@ -137,17 +138,13 @@ ivi::settings::connman_manager::connman_manager( * method. */ // Subscribe to PropertyChanged signal for all technologies. - constexpr gint const timeout = 5000; // milliseconds GError * error = nullptr; unique_ptr const dictionary( - g_dbus_proxy_call_sync(connman_.proxy(), - "GetTechnologies", - nullptr, // No parameters - G_DBUS_CALL_FLAGS_NONE, - timeout, - nullptr, // Not cancellable - &error)); + call_method("GetTechnologies", + nullptr, // No parameters, + error)); + unique_ptr safe_error(error); if (dictionary != nullptr) { @@ -171,10 +168,50 @@ ivi::settings::connman_manager::connman_manager( tech_path); } } + + // Register the Agent implementation responsible for handling input + // requests for secure and hidden networks. + if (!g_variant_is_object_path(agent_.object_path())) { + throw std::runtime_error(std::string("Invalid Agent object path: ") + + agent_.object_path()); + } + + error = nullptr; + unique_ptr const ret( + call_method("RegisterAgent", + g_variant_new("(o)", + agent_.object_path()), + error)); + + if (ret == nullptr) { + safe_error.reset(error); + + throw std::runtime_error( + std::string("Unable to register agent: ") + error->message); + } } ivi::settings::connman_manager::~connman_manager() { + // Unregister agent. + if (g_variant_is_object_path(agent_.object_path())) { + GError * error = nullptr; + + unique_ptr const ret( + call_method("UnregisterAgent", + g_variant_new("(o)", + agent_.object_path()), + error)); + + if (ret == nullptr) { + unique_ptr const safe_error(error); + + g_warning("Unable to unregister agent: %s\n", error->message); + } + } + + // Unsubscribe from ServicesChanged, TechnologyRemoved and + // TechnologyAdded signals. g_dbus_connection_signal_unsubscribe(connman_.connection(), services_changed_id_); g_dbus_connection_signal_unsubscribe(connman_.connection(), @@ -226,21 +263,33 @@ ivi::settings::connman_manager::handle_request( } } +bool +ivi::settings::connman_manager::register_connect_data( + char const * service_path, + std::map && info, + response_callback const & response) +{ + return agent_.register_connect_data(service_path, + std::move(info), + response); +} + +bool +ivi::settings::connman_manager::deregister_connect_data( + char const * service_path) +{ + return agent_.deregister_connect_data(service_path); +} + GVariant * ivi::settings::connman_manager::get_properties( std::string const & technology, GError *& error) const { - constexpr gint const timeout = 5000; // milliseconds - unique_ptr const dictionary( - g_dbus_proxy_call_sync(connman_.proxy(), - "GetTechnologies", - nullptr, // No parameters - G_DBUS_CALL_FLAGS_NONE, - timeout, - nullptr, // Not cancellable - &error)); + call_method("GetTechnologies", + nullptr, // No parameters, + error)); if (dictionary != nullptr) { GVariantIter * i = nullptr; @@ -255,7 +304,7 @@ ivi::settings::connman_manager::get_properties( unique_ptr const tmp( g_variant_get_child_value(child.get(), 0)); - char const * object = + char const * const object = g_variant_get_string(tmp.get(), nullptr); // The technology is found at end the object path, @@ -280,44 +329,68 @@ void ivi::settings::connman_manager::get_services( response_callback & response) const { - call_method("GetServices", response); + constexpr char name[] = "GetServices"; + GError * error = nullptr; + + unique_ptr ret(call_method(name, + nullptr, // No parameters + error)); + + unique_ptr safe_error(error); + + handle_return_value(name, ret.get(), error, response); } void ivi::settings::connman_manager::get_technologies( response_callback & response) const { - call_method("GetTechnologies", response); + constexpr char name[] = "GetTechnologies"; + GError * error = nullptr; + + unique_ptr ret(call_method(name, + nullptr, // No parameters + error)); + + unique_ptr safe_error(error); + + handle_return_value(name, ret.get(), error, response); } -void +GVariant * ivi::settings::connman_manager::call_method( char const * name, - response_callback & response) const + GVariant * parameters, + GError *& error) const { constexpr gint const timeout = 5000; // milliseconds - GError * error = nullptr; - unique_ptr ret( + return g_dbus_proxy_call_sync(connman_.proxy(), name, - nullptr, // No parameters + parameters, G_DBUS_CALL_FLAGS_NONE, timeout, nullptr, // Not cancellable - &error)); - - unique_ptr safe_error(error); + &error); +} +void +ivi::settings::connman_manager::handle_return_value( + char const * name, + GVariant * ret, + GError * error, + response_callback & response) const +{ if (ret != nullptr) { response.send_response( - [&ret](JsonBuilder * builder) + [ret](JsonBuilder * builder) { /** * @todo Can @c json_gvariant_serialize() ever return a nullptr? */ JsonNode * const value = - json_gvariant_serialize(ret.get()); + json_gvariant_serialize(ret); json_builder_set_member_name(builder, "value"); json_builder_add_value(builder, value); @@ -350,7 +423,6 @@ ivi::settings::connman_manager::subscribe_to_signal( nullptr); } - // Local Variables: // mode:c++ // c-basic-offset:2 diff --git a/plugins/connman/connman_manager.hpp b/plugins/connman/connman_manager.hpp index e96542d..8d06494 100644 --- a/plugins/connman/connman_manager.hpp +++ b/plugins/connman/connman_manager.hpp @@ -29,6 +29,7 @@ #include "connman.hpp" #include "subscription_manager.hpp" +#include "agent.hpp" #include #include @@ -76,6 +77,19 @@ namespace ivi //@} /** + * Register data that may be needed when handling Connman Agent + * method calls. + */ + bool register_connect_data(char const * service_path, + std::map && info, + response_callback const & response); + + /** + * Deregister connection data. + */ + bool deregister_connect_data(char const * service_path); + + /** * Get the properties for a specific technology. * * @param[in] technology Connman technology for which properties @@ -125,17 +139,31 @@ namespace ivi void get_technologies(response_callback & response) const; /** - * Call the method @a name on the connman Manager object. This - * method is meant to be used for @c GetServices and - * @c GetTechnologies requests. + * Call the method @a name on the connman Manager object. + * + * @param[in] name The connman Manager object method + * name. + * @param[in] parameters Method parameters. + * @param[inout] error Pointer to error object containing + * failure related information. Caller + * must deallocate if it is not null. * - * @param[in] name The connman Manager object method - * name. - * @param[inout] response Callback used to inform the caller of - * the results. + * @return Variant containing result of underlying Connman + * Manager D-Bus call. The caller must release the + * returned @c GVariant. */ - void call_method(char const * name, - response_callback & response) const; + GVariant * call_method(char const * name, + GVariant * parameters, + GError *& error) const; + + + /** + * Send response or error to client that initiated the request. + */ + void handle_return_value(char const * name, + GVariant * ret, + GError * error, + response_callback & response) const; typedef void (*ivi_signal_callback)(GDBusConnection * connection, char const * sender_name, @@ -168,6 +196,12 @@ namespace ivi /// ServicesChanged signal subscription ID. guint const services_changed_id_; + /** + * The connman Agent implemention that handles secure and + * hidden network related requests. + */ + agent agent_; + }; } diff --git a/plugins/connman/connman_service.cpp b/plugins/connman/connman_service.cpp index 143087d..5c3714d 100644 --- a/plugins/connman/connman_service.cpp +++ b/plugins/connman/connman_service.cpp @@ -45,9 +45,9 @@ namespace ivi::settings::connman_service::connman_service( GDBusConnection * connection, - event_callback const & e) + connman_manager & manager) : connection_(connection) - , event_callback_(e) + , manager_(manager) { } @@ -90,8 +90,8 @@ ivi::settings::connman_service::handle_request( json_reader_end_element(reader); if (path != nullptr) { - service s(path, connection_, event_callback_); - s.handle_request(name, reader, response); + service s(path, connection_); + s.handle_request(name, reader, manager_, response); } } json_reader_end_member(reader); diff --git a/plugins/connman/connman_service.hpp b/plugins/connman/connman_service.hpp index c76902c..bbd9dcd 100644 --- a/plugins/connman/connman_service.hpp +++ b/plugins/connman/connman_service.hpp @@ -30,7 +30,6 @@ #include "connman.hpp" #include -#include #include @@ -57,7 +56,7 @@ namespace ivi /// Constructor. connman_service(GDBusConnection * connection, - event_callback const & e); + connman_manager & manager); /// Destructor. ~connman_service(); @@ -81,8 +80,11 @@ namespace ivi /// The underlying D-Bus connection. GDBusConnection * const connection_; - /// Callback through which events will be sent to clients. - event_callback event_callback_; + /** + * The object used to register connection information for use + * by the Connman Agent. + */ + connman_manager & manager_; }; diff --git a/plugins/connman/connman_technology.cpp b/plugins/connman/connman_technology.cpp index b44d59e..89033a9 100644 --- a/plugins/connman/connman_technology.cpp +++ b/plugins/connman/connman_technology.cpp @@ -45,14 +45,11 @@ namespace ivi::settings::connman_technology::connman_technology( GDBusConnection * connection, - connman_manager & manager, - event_callback const & e) + connman_manager & manager) : connman_("net.connman.Technology", // Interface "/", // Object path - connection, - e) + connection) , manager_(manager) - , event_callback_(e) { } @@ -95,10 +92,7 @@ ivi::settings::connman_technology::handle_request( json_reader_end_element(reader); if (path != nullptr) { - technology t(path, - connman_.connection(), - manager_, - event_callback_); + technology t(path, connman_.connection(), manager_); t.handle_request(name, reader, response); } } diff --git a/plugins/connman/connman_technology.hpp b/plugins/connman/connman_technology.hpp index ad8b78c..09e30f9 100644 --- a/plugins/connman/connman_technology.hpp +++ b/plugins/connman/connman_technology.hpp @@ -30,7 +30,6 @@ #include "connman.hpp" #include -#include #include @@ -57,8 +56,7 @@ namespace ivi /// Constructor. connman_technology(GDBusConnection * connection, - connman_manager & manager, - event_callback const & e); + connman_manager & manager); /// Destructor. ~connman_technology(); @@ -85,9 +83,6 @@ namespace ivi /// The proxy used to access the connman Manager D-Bus API. connman_manager & manager_; - /// Callback through which events will be sent to clients. - event_callback event_callback_; - }; } diff --git a/plugins/connman/registration.cpp b/plugins/connman/registration.cpp index 51f4aaa..edc4fe5 100644 --- a/plugins/connman/registration.cpp +++ b/plugins/connman/registration.cpp @@ -59,10 +59,10 @@ register_settings(ivi::settings::registrar & r, std::unique_ptr manager(mgr); std::unique_ptr service( - new ivi::settings::connman_service(connection, e)); + new ivi::settings::connman_service(connection, *mgr)); std::unique_ptr technology( - new ivi::settings::connman_technology(connection, *mgr, e)); + new ivi::settings::connman_technology(connection, *mgr)); std::unique_ptr clk( new ivi::settings::clock(connection, e)); diff --git a/plugins/connman/service.cpp b/plugins/connman/service.cpp index 8d25157..c956cb6 100644 --- a/plugins/connman/service.cpp +++ b/plugins/connman/service.cpp @@ -34,25 +34,24 @@ ivi::settings::service::service(std::string service_path, - GDBusConnection * connection, - event_callback const & e) + GDBusConnection * connection) : connman_("net.connman.Service", // Interface service_path.c_str(), // Object path - connection, - e) + connection) { } void ivi::settings::service::handle_request(char const * name, JsonReader * reader, + connman_manager & manager, response_callback & response) { if (name != nullptr) { if (strcmp(name, "connect") == 0) - connect(reader, response); + connect(reader, manager, response); else if (strcmp(name, "disconnect") == 0) - disconnect(reader, response); + disconnect(reader, manager, response); else { response.send_error( std::string("Unrecognized connman service request name: ") @@ -62,14 +61,56 @@ ivi::settings::service::handle_request(char const * name, } void -ivi::settings::service::connect(JsonReader * /* reader */, +ivi::settings::service::connect(JsonReader * reader, + connman_manager & manager, response_callback response) { + bool successful_parse = false; + + typedef agent::connect_info_map_type map_type; + map_type info; + + // The service connection information is found in the second array + // element. + if (json_reader_read_element(reader, 1) + && json_reader_is_object(reader)) { + gint const count = json_reader_count_members(reader); + for (gint i = 0; i < count; ++i) { + if (json_reader_read_element(reader, i)) { + char const * const name = json_reader_get_member_name(reader); + char const * const value = json_reader_get_string_value(reader); + + if (name != nullptr && value != nullptr) + info.insert(std::make_pair(name, value)); + } + json_reader_end_element(reader); + } + + // This cast is safe. + if (static_cast(info.size()) == count) { + successful_parse = true; + } + } + json_reader_end_element(reader); + + if (!successful_parse) { + response.send_error("Unable to parse service connection information."); + return; + } + + if (!manager.register_connect_data(connman_.object_path(), + std::move(info), + response)) { + response.send_error("Unable to register connection information."); + return; + } + call_method("Connect", response); } void ivi::settings::service::disconnect(JsonReader * reader, + connman_manager & manager, response_callback response) { bool null = false; @@ -86,13 +127,15 @@ ivi::settings::service::disconnect(JsonReader * reader, } call_method("Disconnect", response); + + manager.deregister_connect_data(connman_.object_path()); } void ivi::settings::service::call_method(char const * name, response_callback response) { - constexpr gint const timeout = 10000; // milliseconds + constexpr gint const timeout = 15000; // milliseconds GError * error = nullptr; unique_ptr const ret( diff --git a/plugins/connman/service.hpp b/plugins/connman/service.hpp index 56c9b62..f5c33b5 100644 --- a/plugins/connman/service.hpp +++ b/plugins/connman/service.hpp @@ -59,8 +59,7 @@ namespace ivi * sent to clients. */ service(std::string service_path, - GDBusConnection * connection, - event_callback const & e); + GDBusConnection * connection); /** * Handle connman Service object request. @@ -74,16 +73,19 @@ namespace ivi */ void handle_request(char const * name, JsonReader * reader, + connman_manager & manager, response_callback & response); private: /// Connect to the service. void connect(JsonReader * reader, + connman_manager & manager, response_callback response); /// Disconnect from the service. void disconnect(JsonReader * reader, + connman_manager & manager, response_callback response); /** diff --git a/plugins/connman/settings-agent.conf.in b/plugins/connman/settings-agent.conf.in new file mode 100644 index 0000000..cf8651d --- /dev/null +++ b/plugins/connman/settings-agent.conf.in @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/plugins/connman/technology.cpp b/plugins/connman/technology.cpp index 15c0f1f..e8e3d14 100644 --- a/plugins/connman/technology.cpp +++ b/plugins/connman/technology.cpp @@ -26,7 +26,6 @@ #include "technology.hpp" #include "connman_manager.hpp" -#include "service.hpp" #include #include @@ -38,14 +37,11 @@ ivi::settings::technology::technology(char const * path, GDBusConnection * connection, - connman_manager & manager, - event_callback const & e) + connman_manager & manager) : connman_("net.connman.Technology", // Interface path, // Object path - connection, - e) + connection) , manager_(manager) - , event_callback_(e) { } diff --git a/plugins/connman/technology.hpp b/plugins/connman/technology.hpp index fde6737..5008eb5 100644 --- a/plugins/connman/technology.hpp +++ b/plugins/connman/technology.hpp @@ -59,14 +59,10 @@ namespace ivi * @param[in] path The connman technology object path. * @param[in] connection Underlying D-Bus connection. * @param[in] manager Connman manager proxy. - * @param[in] e Callback through which events will be - * sent to clients. */ technology(char const * path, GDBusConnection * connection, - connman_manager & manager, - event_callback const & e); - + connman_manager & manager); /** * Handle connman technology-specific request. @@ -122,9 +118,6 @@ namespace ivi /// The proxy used to access the connman Manager D-Bus API. connman_manager & manager_; - /// Callback through which events will be sent to clients. - event_callback event_callback_; - }; }