TIVI-1924: Initial commit of IVI settings daemon.
[profile/ivi/settings-daemon.git] / plugins / connman / technology.cpp
1 /**
2  * @file technology.cpp
3  *
4  * @brief Connman technology request handling.
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 "technology.hpp"
28 #include "service.hpp"
29 #include <settingsd/response_callback.hpp>
30 #include <settingsd/glib_traits.hpp>
31 #include <settingsd/json_glib_traits.hpp>
32 #include <settingsd/smart_ptr.hpp>
33
34 #include <cstring>
35 #include <chrono>
36
37
38 ivi::settings::technology::technology(std::string tech)
39   : connman_("net.connman.Technology",     // Interface
40              ("/net/connman/technology/"
41               + tech).c_str())             // Object path
42   , manager_()
43   , technology_(tech)
44 {
45 }
46
47 void
48 ivi::settings::technology::handle_request(std::string request,
49                                           response_callback response)
50 {
51   smart_ptr<JsonParser> const parser(json_parser_new());
52   json_parser_load_from_data(parser.get(), request.c_str(), -1, nullptr);
53
54   smart_ptr<JsonReader> const safe_reader(
55     json_reader_new(json_parser_get_root(parser.get())));
56   JsonReader * const reader = safe_reader.get();
57
58   char const * name = nullptr;
59   if (json_reader_read_member(reader, "name"))
60     name = json_reader_get_string_value(reader);
61   else
62     response.send_error(
63       "Malformed " + technology_ + " request: missing 'name' element");
64
65   json_reader_end_member(reader);
66
67   if (name != nullptr) {
68     if (strcmp(name, "is_enabled") == 0)
69       get_powered(reader, response);
70     else if (strcmp(name, "enable") == 0)
71       set_powered(reader, response);
72     else if (strcmp(name, "scan") == 0)
73       scan(reader, response);
74     else if (strcmp(name, "connect") == 0)
75       connect(reader, response);
76     else if (strcmp(name, "disconnect") == 0)
77       disconnect(reader, response);
78     else {
79       response.send_error(
80         "Unrecognized " + technology_ + " request name: "
81         + name);
82     }
83   } else {
84     response.send_error(
85       "Operation name for " + technology_ + " request is not a string.");
86   }
87 }
88
89 void
90 ivi::settings::technology::get_powered(JsonReader * reader,
91                                        response_callback response)
92 {
93   bool null = false;
94   if (json_reader_read_member(reader, "value")) {
95     null = json_reader_get_null_value(reader);
96   }
97   json_reader_end_member(reader);
98
99   if (!null) {
100     response.send_error(
101       technology_ + " is_enabled parameter is not null.");
102     return;
103   }
104
105   smart_ptr<GVariant> const property(
106     get_property("Powered", G_VARIANT_TYPE_BOOLEAN, response));
107
108   if (property != nullptr) {
109     bool const powered = g_variant_get_boolean(property.get());
110
111     response.send_response(
112       [powered](JsonBuilder * builder)
113       {
114         json_builder_set_member_name(builder, "value");
115         json_builder_add_boolean_value(builder, powered);
116       });
117   }
118
119   // Error responses handled in get_property() method.
120 }
121
122 void
123 ivi::settings::technology::set_powered(JsonReader * reader,
124                                        response_callback response)
125 {
126   bool enable = false;
127   if (json_reader_read_member(reader, "value")) {
128     enable = json_reader_get_boolean_value(reader);
129   }
130
131   json_reader_end_member(reader);
132
133   constexpr char const name[] = "Powered";
134
135   // Get notified when the technology property has changed.
136   auto property_promise = connman_.get_property_changed_promise(name);
137   auto property_future  = property_promise->get_future();
138
139   GError * error = nullptr;
140
141   smart_ptr<GVariant> ret(
142     connman_.set_property(name,
143                           g_variant_new_boolean(enable),
144                           error));
145
146   smart_ptr<GError> safe_error(error);
147
148   if (ret != nullptr) {
149     constexpr int const timeout = 5000;  // milliseconds
150
151     // Block until the technology property has changed..
152     std::future_status const status =
153       property_future.wait_for(std::chrono::milliseconds(timeout));
154
155     if (status == std::future_status::ready) {
156
157       // Nothing to add to successful response.
158       response.send_response(
159         [](JsonBuilder * /* builder */) {});
160
161     } else {
162
163       response.send_error(
164         std::string("Wait for enable status failed: ")
165         + (status == std::future_status::timeout
166            ? "timeout"
167            : "deferred"));
168
169     }
170   } else if (error != nullptr) {
171     response.send_error(
172       "Unable to set " + technology_ + " powered state: "
173       + error->message);
174   } else {
175     response.send_error(
176       "Malformed " + technology_ + " enable request value.");
177   }
178 }
179
180 void
181 ivi::settings::technology::scan(JsonReader * reader,
182                                 response_callback response)
183 {
184   bool null = false;
185   if (json_reader_read_member(reader, "value")) {
186     null = json_reader_get_null_value(reader);
187   }
188   json_reader_end_member(reader);
189
190   if (!null) {
191     response.send_error(technology_ + " scan parameter is not null.");
192     return;
193   }
194
195   // Get notified when scan results are available.
196   auto services_promise = manager_.get_services_changed_promise();
197   auto services_future  = services_promise->get_future();
198
199   // The scan could take a while.
200   constexpr gint const timeout = 10000;  // milliseconds
201   GError * error = nullptr;
202
203   smart_ptr<GVariant> const ret(
204     g_dbus_proxy_call_sync(connman_.proxy(),
205                            "Scan",
206                            nullptr,  // No parameters
207                            G_DBUS_CALL_FLAGS_NONE,
208                            timeout,
209                            nullptr,  // Not cancellable
210                            &error));
211
212   smart_ptr<GError> safe_error(error);
213
214   if (ret != nullptr) {
215     constexpr gint const future_timeout = 5000;  // milliseconds
216
217     // Block until the scan results are in.
218     std::future_status const status =
219       services_future.wait_for(std::chrono::milliseconds(future_timeout));
220
221     if (status != std::future_status::deferred) {
222       // Don't call get() if the future isn't ready.  Otherwise this
223       // will be a blocking call.
224       if (status == std::future_status::ready) {
225         smart_ptr<JsonNode> const changed_services(services_future.get());
226       }
227
228       send_services(response, error);
229
230       // A timeout is okay since some services may not have changed
231       // within the timeout period but log it just in case.
232       if (status == std::future_status::timeout)
233         g_debug("%s settings: Timed out waiting for changed services.\n",
234                 technology_.c_str());
235     } else {
236       // We only get here if the future status is
237       // std::future_status::deferred, meaning the result hasn't been
238       // determined yet.  That should never happen in our case.
239       response.send_error("Wait for scan results failed: deferred");
240     }
241   } else if (error != nullptr) {
242     response.send_error(
243       "Unable to scan " + technology_ + ": "
244       + error->message);
245   } else {
246     response.send_error(
247       "Malformed " + technology_ + " scan request value.");
248   }
249 }
250
251 void
252 ivi::settings::technology::connect(JsonReader * reader,
253                                    response_callback response)
254 {
255   char const * service_path = nullptr;
256   if (json_reader_read_member(reader, "value")) {
257     service_path = json_reader_get_string_value(reader);
258   }
259   json_reader_end_member(reader);
260
261   /// @todo Refactor malformed JSON request handling code.
262   if (service_path != nullptr) {
263     service s(service_path);
264     s.connect(response);
265   } else {
266     response.send_error(
267       "Malformed " + technology_ + " connect request value.");
268   }
269 }
270
271 void
272 ivi::settings::technology::disconnect(JsonReader * reader,
273                                       response_callback response)
274 {
275   char const * service_path = nullptr;
276   if (json_reader_read_member(reader, "value")) {
277     service_path = json_reader_get_string_value(reader);
278   }
279   json_reader_end_member(reader);
280
281   if (service_path != nullptr) {
282     service s(service_path);
283     s.disconnect(response);
284   } else {
285     response.send_error(
286       "Malformed " + technology_ + " disconnect request value.");
287   }
288 }
289
290 void
291 ivi::settings::technology::send_services(response_callback response,
292                                          GError *& error)
293 {
294   smart_ptr<GVariant> const services(manager_.get_services(error));
295
296   if (services != nullptr) {
297     response.send_response(
298       [&services](JsonBuilder * builder)
299       {
300         /**
301          * @todo Can @c json_gvariant_serialize() ever return a nullptr?
302          */
303         JsonNode * const value =
304           json_gvariant_serialize(services.get());
305
306         json_builder_set_member_name(builder, "value");
307         json_builder_add_value(builder, value);
308
309         // No need to free the JsonNode.  The builder will take
310         // ownership of it.
311       });
312   }
313 }
314
315 GVariant *
316 ivi::settings::technology::get_property(char const * name,
317                                         GVariantType const * type,
318                                         response_callback response)
319 {
320   GError * error = nullptr;
321
322   smart_ptr<GVariant> const properties(
323     manager_.get_properties(technology_, error));
324
325   smart_ptr<GError> safe_error(error);
326
327   smart_ptr<GVariant> property;
328   if (properties != nullptr) {
329     property.reset(
330       g_variant_lookup_value(properties.get(), name, type));
331
332     if (property == nullptr) {
333       response.send_error(
334         "Internal " + technology_
335         + "error: \"" + name + "\" property lookup failed.");
336     }
337   } else if (error != nullptr) {
338     response.send_error(
339       "Unable to get " + technology_ + "properties: "
340       + error->message);
341   } else {
342     // This scenario will occur if the technology doesn't exist on
343     // the platform.  For example, attempting to retrieve the wifi
344     // "Powered" property on a platform without supported hardware
345     // will result in this error.
346     response.send_error(technology_ + " properties are not available.");
347   }
348
349   return property.release();
350 }
351
352
353 // Local Variables:
354 // mode:c++
355 // c-basic-offset:2
356 // indent-tabs-mode: nil
357 // End: