4 * @brief Settings daemon D-Bus connection adapter.
6 * @author Michael Leibowitz @<michael.leibowitz@@intel.com@>
7 * @author Ossama Othman @<ossama.othman@@intel.com@>
10 * Copyright 2013 Intel Corporation All Rights Reserved.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation;
15 * version 2.1 of the License.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301 USA
29 #include "../../lib/config.hpp"
31 #include <settingsd/glib_traits.hpp>
32 #include <settingsd/unique_ptr.hpp>
42 // --------------------------------------------------------------
43 // D-Bus Name Acquisition
44 // --------------------------------------------------------------
46 * @brief Callback function invoked when a D-Bus name is acquired.
48 * @param[in] connection The D-Bus connection on which the name was
50 * @param[in] name The name being owned.
51 * @param[in] user_data User-provided data.
54 on_name_acquired(GDBusConnection * /* connection */,
56 gpointer /* user_data */)
58 g_debug("Name \"%s\" acquired on D-Bus.\n", name);
62 * @brief Callback function invoked when a D-Bus name is no longer
63 * owned or the D-Bus connection has been closed.
65 * @param[in] connection The D-Bus connection on which the name was
67 * @param[in] name The name being owned.
68 * @param[in] user_data User-provided data.
72 on_name_lost(GDBusConnection * /* connection */,
74 gpointer /* user_data */)
76 // This can happen if the appropriate D-Bus policy is not
77 // installed, for example.
78 throw std::runtime_error(std::string("Lost name \"")
79 + name + "\" on D-Bus!");
82 // --------------------------------------------------------------
83 // Connman Agent Handlers
84 // --------------------------------------------------------------
86 * @class agent_operation_handler
88 * @brief Handle agent operations through given callback.
90 * This class factors out common connection data retrieval code, and
91 * performs Agent operations through the given callback.
93 class agent_operation_handler
95 typedef ivi::settings::agent agent;
96 typedef agent::user_data data_type;
99 agent_operation_handler(agent_operation_handler const &) = delete;
100 void operator=(agent_operation_handler const &) = delete;
107 * @param[in] service D-Bus object path for connman service
108 * @param[in] user_data User/connection data
109 * @param[in] callback Callback that implements Agent operation
111 agent_operation_handler(
112 gchar const * service,
114 std::function<void(ivi::settings::agent::connect_data const &)>
116 : data_(static_cast<data_type *>(user_data))
117 , guard_(data_->lock)
118 , iterator_(data_->info.find(service))
120 // data_ should never be null!
122 if (iterator_ != data_->info.end())
123 callback(iterator_->second);
127 ~agent_operation_handler()
129 // Done with the connection data. Remove it from the map.
130 if (iterator_ != data_->info.end())
131 data_->info.erase(iterator_);
135 /// User/connection data.
136 data_type * const data_;
139 * Synchronize access to the user/connection data map.
141 * @note This lock will be held for the life of the
142 * @c agent_operation_handler object, i.e. for the duration
143 * of the operation. This is needed to synchronize the map
144 * @c erase() operation in the destructor. The erase()
145 * operation is performed in the destructor to ensure
146 * element removal occurs if an exception is thrown by the
149 std::lock_guard<std::mutex> guard_;
151 /// Map iterator that points to the connection data.
152 agent::user_data::map_type::iterator const iterator_;
156 on_handle_release(Agent * object,
157 GDBusMethodInvocation * invocation,
158 gpointer /* user_data */)
160 agent_complete_release(object, invocation);
165 on_handle_report_error(Agent * object,
166 GDBusMethodInvocation * invocation,
167 gchar const * service,
171 using ivi::settings::agent;
173 agent_operation_handler(service,
175 [error](agent::connect_data const & d){
176 d.response.send_error(std::string(error));
179 agent_complete_report_error(object, invocation);
185 on_handle_request_browser(Agent * object,
186 GDBusMethodInvocation * invocation,
187 gchar const * /* service */,
188 gchar const * /* url */,
189 gpointer /* user_data */)
191 agent_complete_request_browser(object, invocation);
196 on_handle_request_input(Agent * object,
197 GDBusMethodInvocation * invocation,
198 gchar const * service,
202 using ivi::settings::agent;
204 agent_operation_handler(
207 [object, invocation, fields](agent::connect_data const & d){
208 using ivi::settings::unique_ptr;
210 // Prepare to build a dictionary, i.e. an array of
211 // dictionary entries, "a{sv}".
212 GVariantBuilder builder;
213 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
215 bool error_occurred = false;
216 bool found_field = false;
218 // Extract requested mandatory and alternate fields.
219 GVariantIter * vi = nullptr;
220 g_variant_get(fields, "a{sv}", &vi);
221 unique_ptr<GVariantIter> const iter(vi);
223 gchar * fname = nullptr;
224 GVariant * fvalue = nullptr;
227 * @todo Refactor this GVariant dictionary iteration code.
229 while (g_variant_iter_next(vi, "{sv}", &fname, &fvalue)) {
231 unique_ptr<gchar> const field(fname);
232 unique_ptr<GVariant> const arguments(fvalue);
234 GVariantIter * ai = nullptr;
235 g_variant_get(arguments.get(), "a{sv}", &ai);
236 unique_ptr<GVariantIter> const iter(ai);
238 gchar * argname = nullptr;
239 GVariant * argvalue = nullptr;
241 while (g_variant_iter_next(ai, "{sv}", &argname, &argvalue)) {
242 unique_ptr<gchar> const name(argname);
243 unique_ptr<GVariant> const value(argvalue);
245 // Determine which requested fields are mandatory.
246 if (strcmp(argname, "Requirement") == 0
247 && strcmp(g_variant_get_string(argvalue, nullptr),
249 // Check if we were supplied the required field, and add
250 // it to the dictionary result.
251 auto const i = d.info.find(fname);
252 if (i != d.info.end()) {
253 // Note that the dictionary is of the form a{sv} so
254 // we wrap the dictionary string value in a variant.
255 g_variant_builder_add_value(
257 g_variant_new_dict_entry(
258 g_variant_new_string(fname),
259 g_variant_new_variant(
260 g_variant_new_string(i->second.c_str()))));
263 * @todo Is there any way to tell if an entry was
264 * added to the indefinite array being
265 * constructed through the glib GVariant builder
266 * interface? We can't get rid of this flag if
272 std::string("Required connection field \"")
274 + "\" not supplied.");
277 * @todo This send_error() call is probably
278 * redundant. Look into removing it.
280 d.response.send_error(err);
283 * @bug Register a settingsd-specific error during
285 * @c g_dbus_error_register_error(), and use it
286 * in this call instead.
288 g_dbus_method_invocation_return_dbus_error(
290 "net.connman.Agent.Error.Canceled",
293 error_occurred = true;
301 if (!error_occurred) {
302 GVariant * dictionary = nullptr;
304 // We can't call g_variant_builder_end() if no children
305 // (dictionary entries) were added to the indefinite array
306 // being constructed through the builder.
308 dictionary = g_variant_builder_end(&builder);
310 // The method return value will contain a dictionary,
311 // i.e. "a{sv}", of the requested input fields.
312 agent_complete_request_input(object, invocation, dictionary);
320 on_handle_cancel(Agent * object,
321 GDBusMethodInvocation * invocation,
322 gpointer /* user_data */)
324 agent_complete_cancel(object, invocation);
329 // ----------------------------------------------------------------
331 ivi::settings::agent::agent(GDBusConnection * connection)
333 // The bus name will be of the form:
334 // "org.tizen.settingsd.connman.Agent"
335 g_bus_own_name_on_connection(connection,
336 IVI_SETTINGS_CONNMAN_AGENT_DBUS_NAME,
337 G_BUS_NAME_OWNER_FLAGS_NONE,
340 nullptr, // user_data,
342 , interface_(agent_skeleton_new())
343 , object_path_("/" IVI_SETTINGS_DBUS_NAME "/connman/Agent")
346 // Construct object path of the form:
347 // /org/tizen/settingsd/connman/Agent"
348 std::replace(object_path_.begin(), object_path_.end(), '.', '/');
350 // Connect the signal handlers corresponding to the Agent
352 g_signal_connect(interface_,
354 G_CALLBACK(on_handle_release),
357 g_signal_connect(interface_,
358 "handle-report-error",
359 G_CALLBACK(on_handle_report_error),
362 g_signal_connect(interface_,
363 "handle-request-browser",
364 G_CALLBACK(on_handle_request_browser),
367 g_signal_connect(interface_,
368 "handle-request-input",
369 G_CALLBACK(on_handle_request_input),
372 g_signal_connect(interface_,
374 G_CALLBACK(on_handle_cancel),
377 // Export the interface on the bus.
378 GError * error = nullptr;
379 if (!g_dbus_interface_skeleton_export(
380 G_DBUS_INTERFACE_SKELETON(interface_),
382 object_path_.c_str(),
384 unique_ptr<GError> safe_error(error);
385 throw std::runtime_error(error->message);
389 ivi::settings::agent::~agent()
391 if (interface_ != nullptr) {
392 g_dbus_interface_skeleton_unexport(
393 G_DBUS_INTERFACE_SKELETON(interface_));
394 g_object_unref(interface_);
397 g_bus_unown_name(owner_id_);
401 ivi::settings::agent::register_connect_data(
402 char const * service_path,
403 std::map<std::string, std::string> && info,
404 response_callback const & response)
406 connect_data data(std::move(info), response);
408 std::lock_guard<std::mutex> guard(data_.lock);
412 std::make_pair(service_path, std::move(data)));
414 return result.second;
418 ivi::settings::agent::deregister_connect_data(char const * service_path)
420 std::lock_guard<std::mutex> guard(data_.lock);
422 return data_.info.erase(service_path) == 1;
429 // indent-tabs-mode: nil