TIVI-1924: Initial commit of IVI settings daemon.
[profile/ivi/settings-daemon.git] / plugins / connman / connman_manager.cpp
1 /**
2  * @file connman_manager.cpp
3  *
4  * @brief Connman_Manager-based settings plugin.
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/json_glib_traits.hpp>
30 #include <settingsd/reverse_lock.hpp>
31
32 #include <cstring>
33
34
35 namespace
36 {
37   void
38   on_services_changed(GDBusConnection * /* connection */,
39                       char const * /* sender_name */,
40                       char const * /* object_path */,
41                       char const * /* interface_name */,
42                       char const * /* signal_name */,
43                       GVariant   * parameters,
44                       gpointer     user_data)
45   {
46     gsize const num_params = g_variant_n_children(parameters);
47     if (num_params != 2) {
48       // We should never get here!
49       g_printerr("Number of ServicesChanged signal parameters "
50                  "is not 2: %" G_GSIZE_FORMAT "\n",
51                  num_params);
52
53       return;
54     }
55
56     using namespace ivi::settings;
57
58     // Changed services are found in the first ServicesChanged
59     // argument.
60     smart_ptr<GVariant> const changed_services(
61       g_variant_get_child_value(parameters, 0));
62
63     // Serialize the changed services into a JSON tree.  This will be
64     // an array of [object, dict], where "object" is the D-Bus object
65     // path, and "dict" is a dictionary of object-specific
66     // properties.
67     smart_ptr<JsonNode> services(
68       json_gvariant_serialize(changed_services.get()));
69
70     // Notify callers about the scan results.
71     typedef connman_manager::user_data user_data_type;
72
73     user_data_type * const p = static_cast<user_data_type *>(user_data);
74     connman_manager::promise_list_type & promises = p->promises;
75
76     // Synchronize access to the promises list.
77     std::lock_guard<std::mutex> lock(p->mutex);
78
79     // Note that the end() iterator must be retrieved during each loop
80     // iteration in case another thread caused another promise to be
81     // added to the list when the lock was temporarily unlocked during
82     // the promise::set_value() call below.
83     for (auto i = promises.begin(); i != promises.end(); ) {
84       auto & promise = *i;
85
86       {
87         // Release the mutex during the promise::set_value()
88         // call/notification so that we don't unnecessarily block
89         // threads attempting to get a services_changed promise.
90         typedef ivi::settings::reverse_lock<std::mutex> reverse_lock;
91         reverse_lock reverse(p->mutex);
92
93         std::lock_guard<reverse_lock> kcol(reverse);
94
95         // Thread that owns the corresponding future must transfer
96         // ownership of the JsonNode to something that will release
97         // the underlying JsonNode, e.g. a JsonBuilder via
98         // json_builder_add_value() or another smart_ptr<JsonNode>.
99         promise->set_value(std::move(services));
100       }
101
102       // Done with the pointer to the promise.  Remove it from the
103       // list now since we already have an iterator to it.
104       i = promises.erase(i);
105     }
106   }
107
108 }
109
110 // ---------------------------------------------------------------
111
112 ivi::settings::connman_manager::connman_manager()
113   : connman_("net.connman.Manager",     // Interface
114              "/")                       // Object path
115   , mutex_()
116   , promises_()
117   , data_(mutex_, promises_)
118   , subscription_id_(
119       g_dbus_connection_signal_subscribe(
120         g_dbus_proxy_get_connection(G_DBUS_PROXY(connman_.proxy())),
121         nullptr,
122         connman_.interface_name(),
123         "ServicesChanged",
124         connman_.object_path(),
125         nullptr,
126         G_DBUS_SIGNAL_FLAGS_NONE,
127         on_services_changed,
128         &data_,
129         nullptr))
130 {
131 }
132
133 ivi::settings::connman_manager::~connman_manager()
134 {
135   g_dbus_connection_signal_unsubscribe(
136     g_dbus_proxy_get_connection(G_DBUS_PROXY(connman_.proxy())),
137     subscription_id_);
138 }
139
140 GVariant *
141 ivi::settings::connman_manager::get_properties(
142   std::string const & technology,
143   GError *& error) const
144 {
145   constexpr gint const timeout = 5000;  // milliseconds
146
147   smart_ptr<GVariant> const dictionary(
148     g_dbus_proxy_call_sync(connman_.proxy(),
149                            "GetTechnologies",
150                            nullptr,  // No parameters
151                            G_DBUS_CALL_FLAGS_NONE,
152                            timeout,
153                            nullptr,  // Not cancellable
154                            &error));
155
156   if (dictionary != nullptr) {
157     GVariantIter * i = nullptr;
158     g_variant_get(dictionary.get(), "(a(oa{sv}))", &i);
159     smart_ptr<GVariantIter> const safe_i(i);
160
161     for (smart_ptr<GVariant> child(g_variant_iter_next_value(i));
162          child != nullptr;
163          child.reset(g_variant_iter_next_value(i))) {
164
165       // The object path is the first tuple element.
166       smart_ptr<GVariant> const tmp(
167         g_variant_get_child_value(child.get(), 0));
168
169       char const * object =
170         g_variant_get_string(tmp.get(), nullptr);
171
172       // The technology is found at end the object path,
173       // e.g. "/net/connman/technology/wifi" for the wifi.
174       char const * tech = strrchr(object, '/');
175
176       if (tech != nullptr) {
177         ++tech;  // Skip the found '/'.
178
179         if (technology == tech) {
180           // The property dictionary is the second tuple element.
181           return g_variant_get_child_value(child.get(), 1);
182         }
183       }
184     }
185   }
186
187   return nullptr;
188 }
189
190 GVariant *
191 ivi::settings::connman_manager::get_services(GError *& error) const
192 {
193   constexpr gint const timeout = 5000;  // milliseconds
194
195   return g_dbus_proxy_call_sync(connman_.proxy(),
196                                 "GetServices",
197                                 nullptr,  // No parameters
198                                 G_DBUS_CALL_FLAGS_NONE,
199                                 timeout,
200                                 nullptr,  // Not cancellable
201                                 &error);
202 }
203
204 ivi::settings::connman_manager::shared_promise_type
205 ivi::settings::connman_manager::get_services_changed_promise()
206 {
207   // This promise must exist long enough for the value to retrieved
208   // from the future.  Use a shared_ptr<> to make that possible.
209   shared_promise_type p = std::make_shared<promise_type>();
210
211   {
212     std::lock_guard<std::mutex> lock(mutex_);
213
214     // Add a new std::promise to the promises list.  The promise will
215     // only be used once, and will be removed once its value has been
216     // set.
217     promises_.push_back(p);
218   }
219
220   return p;
221 }
222
223
224 // Local Variables:
225 // mode:c++
226 // c-basic-offset:2
227 // indent-tabs-mode: nil
228 // End: