Added API for enabling tethering
[profile/ivi/settings-daemon.git] / plugins / connman / connman_manager.cpp
1 /**
2  * @file connman_manager.cpp
3  *
4  * @brief Connman Manager operations.
5  *
6  * @author Ossama Othman @<ossama.othman@@intel.com@>
7  *
8  * @copyright @par
9  * Copyright 2013 Intel Corporation All Rights Reserved.
10  * @par
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation;
14  * version 2.1 of the License.
15  * @par
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  * @par
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA  02110-1301  USA
25  */
26
27 #include "connman_manager.hpp"
28
29 #include <settingsd/response_callback.hpp>
30 #include <settingsd/dbus_signal_callback.hpp>
31 #include <settingsd/glib_traits.hpp>
32 #include <settingsd/unique_ptr.hpp>
33
34 #include <cstring>
35 #include <stdexcept>
36
37 // ----------------------------------------------------------------------
38
39 namespace
40 {
41   std::string const manager_name("connman::manager");
42
43   void
44   on_technology_added(GDBusConnection * connection,
45                       char const * sender_name,
46                       char const * object_path,
47                       char const * interface_name,
48                       char const * signal_name,
49                       GVariant   * parameters,
50                       gpointer     user_data)
51   {
52     // The technology's object path is found in the first argument.
53     ivi::settings::unique_ptr<GVariant> const tech_path(
54       g_variant_get_child_value(parameters, 0));
55
56     // Subscribe to the technology's PropertyChanged signal.
57     typedef ivi::settings::connman_manager::signal_data signal_data;
58     signal_data * const data = static_cast<signal_data *>(user_data);
59
60     data->subscriptions.subscribe("net.connman.Technology",
61                                   "PropertyChanged",
62                                   g_variant_get_string(tech_path.get(),
63                                                        nullptr));
64
65     ivi::settings::on_dbus_signal(connection,
66                                   sender_name,
67                                   object_path,
68                                   interface_name,
69                                   signal_name,
70                                   parameters,
71                                   &data->callback);
72   }
73
74   void
75   on_technology_removed(GDBusConnection * connection,
76                         char const * sender_name,
77                         char const * object_path,
78                         char const * interface_name,
79                         char const * signal_name,
80                         GVariant   * parameters,
81                         gpointer     user_data)
82   {
83     // The technology's object path is found in the first argument.
84     ivi::settings::unique_ptr<GVariant> const tech_path(
85       g_variant_get_child_value(parameters, 0));
86
87     // Unubscribe from the technology's PropertyChanged signal.
88     typedef ivi::settings::connman_manager::signal_data signal_data;
89     signal_data * const data = static_cast<signal_data *>(user_data);
90
91     data->subscriptions.unsubscribe(
92       g_variant_get_string(tech_path.get(), nullptr));
93
94     ivi::settings::on_dbus_signal(connection,
95                                   sender_name,
96                                   object_path,
97                                   interface_name,
98                                   signal_name,
99                                   parameters,
100                                   &data->callback);
101   }
102 }
103
104 // ----------------------------------------------------------------------
105
106 ivi::settings::connman_manager::connman_manager(
107   GDBusConnection * connection,
108   event_callback const & e)
109   : connman_("net.connman.Manager",     // Interface
110              "/",                       // Object path
111              connection)
112   , data_(connection, e)
113   , technology_added_id_(subscribe_to_signal(connection,
114                                              "TechnologyAdded",
115                                              on_technology_added,
116                                              &data_))
117   , technology_removed_id_(subscribe_to_signal(connection,
118                                                "TechnologyRemoved",
119                                                on_technology_removed,
120                                                &data_))
121   , services_changed_id_(subscribe_to_signal(connection,
122                                              "ServicesChanged",
123                                              on_dbus_signal,
124                                              &data_.callback))
125   , agent_(connection)
126 {
127   // The ServicesChanged signal parameters are:
128   //
129   //   array{object, dict}, array{object})
130   //
131   // where "object" is the D-Bus object path, and "dict" is a
132   // dictionary of object-specific properties.  The first parameter is
133   // list of changed services.  The second is a list of removed
134   // services.
135
136   /**
137    * @todo Refactor duplicate code found here an in get_properties()
138    *       method.
139    */
140   // Subscribe to PropertyChanged signal for all technologies.
141   GError * error = nullptr;
142
143   unique_ptr<GVariant> const dictionary(
144     call_method("GetTechnologies",
145                 nullptr,  // No parameters,
146                 error));
147
148   unique_ptr<GError> safe_error(error);
149
150   if (dictionary != nullptr) {
151     GVariantIter * i = nullptr;
152     g_variant_get(dictionary.get(), "(a(oa{sv}))", &i);
153     unique_ptr<GVariantIter> const safe_i(i);
154
155     for (unique_ptr<GVariant> child(g_variant_iter_next_value(i));
156          child != nullptr;
157          child.reset(g_variant_iter_next_value(i))) {
158
159       // The object path is the first tuple element.
160       unique_ptr<GVariant> const tmp(
161         g_variant_get_child_value(child.get(), 0));
162
163       char const * const tech_path =
164         g_variant_get_string(tmp.get(), nullptr);
165
166       data_.subscriptions.subscribe("net.connman.Technology",
167                                     "PropertyChanged",
168                                     tech_path);
169     }
170   }
171
172   // Register the Agent implementation responsible for handling input
173   // requests for secure and hidden networks.
174   if (!g_variant_is_object_path(agent_.object_path())) {
175     throw std::invalid_argument(std::string("Invalid Agent object path: ")
176                                 + agent_.object_path());
177   }
178
179   error = nullptr;
180   unique_ptr<GVariant> const ret(
181     call_method("RegisterAgent",
182                 g_variant_new("(o)",
183                               agent_.object_path()),
184                 error));
185
186   if (ret == nullptr) {
187     safe_error.reset(error);
188
189     g_warning("Unable to register agent: %s.  "
190               "Hidden and protected connections will "
191               "not be supported.\n",
192               error->message);
193   }
194 }
195
196 ivi::settings::connman_manager::~connman_manager()
197 {
198   // Unregister agent.
199   if (g_variant_is_object_path(agent_.object_path())) {
200     GError * error = nullptr;
201
202     unique_ptr<GVariant> const ret(
203       call_method("UnregisterAgent",
204                   g_variant_new("(o)",
205                                 agent_.object_path()),
206                   error));
207
208     if (ret == nullptr) {
209       unique_ptr<GError> const safe_error(error);
210
211       g_warning("Unable to unregister agent: %s\n", error->message);
212     }
213   }
214
215   // Unsubscribe from ServicesChanged, TechnologyRemoved and
216   // TechnologyAdded signals.
217   g_dbus_connection_signal_unsubscribe(connman_.connection(),
218                                        services_changed_id_);
219   g_dbus_connection_signal_unsubscribe(connman_.connection(),
220                                        technology_removed_id_);
221   g_dbus_connection_signal_unsubscribe(connman_.connection(),
222                                        technology_added_id_);
223 }
224
225 std::string const &
226 ivi::settings::connman_manager::id() const
227 {
228   return manager_name;
229 }
230
231 void
232 ivi::settings::connman_manager::handle_request(
233   std::string request,
234   response_callback response)
235 {
236   unique_ptr<JsonParser> const parser(json_parser_new());
237   json_parser_load_from_data(parser.get(), request.c_str(), -1, nullptr);
238
239   unique_ptr<JsonReader> const safe_reader(
240     json_reader_new(json_parser_get_root(parser.get())));
241   JsonReader * const reader = safe_reader.get();
242
243   char const * name = nullptr;
244   if (json_reader_read_member(reader, "name"))
245     name = json_reader_get_string_value(reader);
246   else
247     response.send_error(
248       "Malformed " + id() + " request: missing 'name' element");
249
250   json_reader_end_member(reader);
251
252   if (name != nullptr) {
253     if (strcmp(name, "get_technologies") == 0)
254       get_technologies(response);
255     else if (strcmp(name, "get_services") == 0)
256       get_services(response);
257     else {
258       response.send_error(
259         "Unrecognized " + id() + " request name: "
260         + name);
261     }
262   } else {
263     response.send_error(
264       "Operation name for " + id() + " request is not a string.");
265   }
266 }
267
268 bool
269 ivi::settings::connman_manager::register_connect_data(
270   char const * service_path,
271   std::map<std::string, std::string> && info,
272   response_callback const & response)
273 {
274   return agent_.register_connect_data(service_path,
275                                       std::move(info),
276                                       response);
277 }
278
279 bool
280 ivi::settings::connman_manager::deregister_connect_data(
281   char const * service_path)
282 {
283   return agent_.deregister_connect_data(service_path);
284 }
285
286 GVariant *
287 ivi::settings::connman_manager::get_properties(
288   std::string const & technology,
289   GError *& error) const
290 {
291   unique_ptr<GVariant> const dictionary(
292     call_method("GetTechnologies",
293                 nullptr,  // No parameters,
294                 error));
295
296   if (dictionary != nullptr) {
297     GVariantIter * i = nullptr;
298     g_variant_get(dictionary.get(), "(a(oa{sv}))", &i);
299     unique_ptr<GVariantIter> const safe_i(i);
300
301     for (unique_ptr<GVariant> child(g_variant_iter_next_value(i));
302          child != nullptr;
303          child.reset(g_variant_iter_next_value(i))) {
304
305       // The object path is the first tuple element.
306       unique_ptr<GVariant> const tmp(
307         g_variant_get_child_value(child.get(), 0));
308
309       char const * const object =
310         g_variant_get_string(tmp.get(), nullptr);
311
312       // The technology is found at end the object path,
313       // e.g. "/net/connman/technology/wifi" for the wifi.
314       char const * tech = strrchr(object, '/');
315
316       if (tech != nullptr) {
317         ++tech;  // Skip the found '/'.
318
319         if (technology == tech) {
320           // The property dictionary is the second tuple element.
321           return g_variant_get_child_value(child.get(), 1);
322         }
323       }
324     }
325   }
326
327   return nullptr;
328 }
329
330 void
331 ivi::settings::connman_manager::get_services(
332   response_callback & response) const
333 {
334   constexpr char name[] = "GetServices";
335   GError * error = nullptr;
336
337   unique_ptr<GVariant> ret(call_method(name,
338                                        nullptr,  // No parameters
339                                        error));
340
341   unique_ptr<GError> safe_error(error);
342
343   handle_return_value(name, ret.get(), error, response);
344 }
345
346 void
347 ivi::settings::connman_manager::get_technologies(
348   response_callback & response) const
349 {
350   constexpr char name[] = "GetTechnologies";
351   GError * error = nullptr;
352
353   unique_ptr<GVariant> ret(call_method(name,
354                                        nullptr,  // No parameters
355                                        error));
356
357   unique_ptr<GError> safe_error(error);
358
359   handle_return_value(name, ret.get(), error, response);
360 }
361
362 GVariant *
363 ivi::settings::connman_manager::call_method(
364   char const * name,
365   GVariant * parameters,
366   GError *& error) const
367 {
368   constexpr gint const timeout = 5000;  // milliseconds
369
370   return
371     g_dbus_proxy_call_sync(connman_.proxy(),
372                            name,
373                            parameters,
374                            G_DBUS_CALL_FLAGS_NONE,
375                            timeout,
376                            nullptr,  // Not cancellable
377                            &error);
378 }
379
380 void
381 ivi::settings::connman_manager::handle_return_value(
382   char const * name,
383   GVariant * ret,
384   GError * error,
385   response_callback & response) const
386 {
387   if (ret != nullptr) {
388     response.send_response(
389       [ret](JsonBuilder * builder)
390       {
391         /**
392          * @todo Can @c json_gvariant_serialize() ever return a nullptr?
393          */
394         JsonNode * const value =
395           json_gvariant_serialize(ret);
396
397         json_builder_set_member_name(builder, "value");
398         json_builder_add_value(builder, value);
399
400         // No need to free the JsonNode.  The builder will take
401         // ownership of it.
402       });
403   } else if (error != nullptr) {
404     response.send_error(std::string(name) + " failed: " + error->message);
405   }
406 }
407
408 guint
409 ivi::settings::connman_manager::subscribe_to_signal(
410   GDBusConnection * connection,
411   char const* name,
412   ivi_signal_callback callback,
413   void * user_data)
414 {
415   return
416     g_dbus_connection_signal_subscribe(connection,
417                                        nullptr,
418                                        connman_.interface_name(),
419                                        name,
420                                        connman_.object_path(),
421                                        nullptr,
422                                        G_DBUS_SIGNAL_FLAGS_NONE,
423                                        callback,
424                                        user_data,
425                                        nullptr);
426 }
427
428 // Local Variables:
429 // mode:c++
430 // c-basic-offset:2
431 // indent-tabs-mode: nil
432 // End: