c448466e6af8de2a441a06ad788a138980772f5f
[platform/upstream/connman.git] / plugins / dundee.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27
28 #include <gdbus.h>
29
30 #define CONNMAN_API_SUBJECT_TO_CHANGE
31 #include <connman/plugin.h>
32 #include <connman/device.h>
33 #include <connman/network.h>
34 #include <connman/dbus.h>
35
36 #define DUNDEE_SERVICE                  "org.ofono.dundee"
37 #define DUNDEE_MANAGER_INTERFACE        DUNDEE_SERVICE ".Manager"
38
39 #define DEVICE_ADDED                    "DeviceAdded"
40 #define DEVICE_REMOVED                  "DeviceRemoved"
41
42 #define GET_DEVICES                     "GetDevices"
43
44 #define TIMEOUT 40000
45
46 static DBusConnection *connection;
47
48 static GHashTable *dundee_devices = NULL;
49
50 struct dundee_data {
51         char *path;
52 };
53
54 static void device_destroy(gpointer data)
55 {
56         struct dundee_data *info = data;
57
58         g_free(info->path);
59
60         g_free(info);
61 }
62
63 static int network_probe(struct connman_network *network)
64 {
65         DBG("network %p", network);
66
67         return 0;
68 }
69
70 static void network_remove(struct connman_network *network)
71 {
72         DBG("network %p", network);
73 }
74
75 static int network_connect(struct connman_network *network)
76 {
77         DBG("network %p", network);
78
79         return 0;
80 }
81
82 static int network_disconnect(struct connman_network *network)
83 {
84         DBG("network %p", network);
85
86         return 0;
87 }
88
89 static struct connman_network_driver network_driver = {
90         .name           = "network",
91         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN,
92         .probe          = network_probe,
93         .remove         = network_remove,
94         .connect        = network_connect,
95         .disconnect     = network_disconnect,
96 };
97
98 static int dundee_probe(struct connman_device *device)
99 {
100         DBG("device %p", device);
101
102         return 0;
103 }
104
105 static void dundee_remove(struct connman_device *device)
106 {
107         DBG("device %p", device);
108 }
109
110 static int dundee_enable(struct connman_device *device)
111 {
112         DBG("device %p", device);
113
114         return 0;
115 }
116
117 static int dundee_disable(struct connman_device *device)
118 {
119         DBG("device %p", device);
120
121         return 0;
122 }
123
124 static struct connman_device_driver dundee_driver = {
125         .name           = "dundee",
126         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
127         .probe          = dundee_probe,
128         .remove         = dundee_remove,
129         .enable         = dundee_enable,
130         .disable        = dundee_disable,
131 };
132
133 static void add_device(const char *path, DBusMessageIter *properties)
134 {
135         struct dundee_data *info;
136
137         info = g_hash_table_lookup(dundee_devices, path);
138         if (info != NULL)
139                 return;
140
141         info = g_try_new0(struct dundee_data, 1);
142         if (info == NULL)
143                 return;
144
145         info->path = g_strdup(path);
146
147         g_hash_table_insert(dundee_devices, g_strdup(path), info);
148 }
149
150 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
151                                 void *user_data)
152 {
153         DBusMessageIter iter, properties;
154         const char *path;
155         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
156                 DBUS_TYPE_ARRAY_AS_STRING
157                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
158                 DBUS_TYPE_STRING_AS_STRING
159                 DBUS_TYPE_VARIANT_AS_STRING
160                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
161
162         if (dbus_message_has_signature(message, signature) == FALSE) {
163                 connman_error("dundee signature does not match");
164                 return TRUE;
165         }
166
167         DBG("");
168
169         if (dbus_message_iter_init(message, &iter) == FALSE)
170                 return TRUE;
171
172         dbus_message_iter_get_basic(&iter, &path);
173
174         dbus_message_iter_next(&iter);
175         dbus_message_iter_recurse(&iter, &properties);
176
177         add_device(path, &properties);
178
179         return TRUE;
180 }
181
182 static void remove_device(DBusConnection *connection, const char *path)
183 {
184         DBG("path %s", path);
185
186         g_hash_table_remove(dundee_devices, path);
187 }
188
189 static gboolean device_removed(DBusConnection *connection, DBusMessage *message,
190                                 void *user_data)
191 {
192         const char *path;
193         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
194
195         if (dbus_message_has_signature(message, signature) == FALSE) {
196                 connman_error("dundee signature does not match");
197                 return TRUE;
198         }
199
200         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
201                                 DBUS_TYPE_INVALID);
202         remove_device(connection, path);
203         return TRUE;
204 }
205
206 static void manager_get_devices_reply(DBusPendingCall *call, void *user_data)
207 {
208         DBusMessage *reply;
209         DBusError error;
210         DBusMessageIter array, dict;
211         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
212                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
213                 DBUS_TYPE_OBJECT_PATH_AS_STRING
214                 DBUS_TYPE_ARRAY_AS_STRING
215                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
216                 DBUS_TYPE_STRING_AS_STRING
217                 DBUS_TYPE_VARIANT_AS_STRING
218                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
219                 DBUS_STRUCT_END_CHAR_AS_STRING;
220
221         DBG("");
222
223         reply = dbus_pending_call_steal_reply(call);
224
225         if (dbus_message_has_signature(reply, signature) == FALSE) {
226                 connman_error("dundee signature does not match");
227                 goto done;
228         }
229
230         dbus_error_init(&error);
231
232         if (dbus_set_error_from_message(&error, reply) == TRUE) {
233                 connman_error("%s", error.message);
234                 dbus_error_free(&error);
235                 goto done;
236         }
237
238         if (dbus_message_iter_init(reply, &array) == FALSE)
239                 goto done;
240
241         dbus_message_iter_recurse(&array, &dict);
242
243         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
244                 DBusMessageIter value, properties;
245                 const char *path;
246
247                 dbus_message_iter_recurse(&dict, &value);
248                 dbus_message_iter_get_basic(&value, &path);
249
250                 dbus_message_iter_next(&value);
251                 dbus_message_iter_recurse(&value, &properties);
252
253                 add_device(path, &properties);
254
255                 dbus_message_iter_next(&dict);
256         }
257
258 done:
259         dbus_message_unref(reply);
260
261         dbus_pending_call_unref(call);
262 }
263
264 static int manager_get_devices(void)
265 {
266         DBusMessage *message;
267         DBusPendingCall *call;
268
269         DBG("");
270
271         message = dbus_message_new_method_call(DUNDEE_SERVICE, "/",
272                                         DUNDEE_MANAGER_INTERFACE, GET_DEVICES);
273         if (message == NULL)
274                 return -ENOMEM;
275
276         if (dbus_connection_send_with_reply(connection, message,
277                                                 &call, TIMEOUT) == FALSE) {
278                 connman_error("Failed to call GetDevices()");
279                 dbus_message_unref(message);
280                 return -EINVAL;
281         }
282
283         if (call == NULL) {
284                 connman_error("D-Bus connection not available");
285                 dbus_message_unref(message);
286                 return -EINVAL;
287         }
288
289         dbus_pending_call_set_notify(call, manager_get_devices_reply,
290                                         NULL, NULL);
291
292         dbus_message_unref(message);
293
294         return -EINPROGRESS;
295 }
296
297 static void dundee_connect(DBusConnection *connection, void *user_data)
298 {
299         DBG("connection %p", connection);
300
301         dundee_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
302                                         g_free, device_destroy);
303
304         manager_get_devices();
305 }
306
307 static void dundee_disconnect(DBusConnection *connection, void *user_data)
308 {
309         DBG("connection %p", connection);
310
311         g_hash_table_destroy(dundee_devices);
312         dundee_devices = NULL;
313 }
314
315 static guint watch;
316 static guint added_watch;
317 static guint removed_watch;
318
319 static int dundee_init(void)
320 {
321         int err;
322
323         connection = connman_dbus_get_connection();
324         if (connection == NULL)
325                 return -EIO;
326
327         watch = g_dbus_add_service_watch(connection, DUNDEE_SERVICE,
328                         dundee_connect, dundee_disconnect, NULL, NULL);
329
330         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
331                                                 DUNDEE_MANAGER_INTERFACE,
332                                                 DEVICE_ADDED, device_added,
333                                                 NULL, NULL);
334
335         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
336                                                 DUNDEE_MANAGER_INTERFACE,
337                                                 DEVICE_REMOVED, device_removed,
338                                                 NULL, NULL);
339
340         if (watch == 0 || added_watch == 0 || removed_watch == 0) {
341                 err = -EIO;
342                 goto remove;
343         }
344
345         err = connman_network_driver_register(&network_driver);
346         if (err < 0)
347                 goto remove;
348
349         err = connman_device_driver_register(&dundee_driver);
350         if (err < 0) {
351                 connman_network_driver_unregister(&network_driver);
352                 goto remove;
353         }
354
355         return 0;
356
357 remove:
358         g_dbus_remove_watch(connection, watch);
359         g_dbus_remove_watch(connection, added_watch);
360         g_dbus_remove_watch(connection, removed_watch);
361
362         dbus_connection_unref(connection);
363
364         return err;
365 }
366
367 static void dundee_exit(void)
368 {
369         g_dbus_remove_watch(connection, watch);
370         g_dbus_remove_watch(connection, added_watch);
371         g_dbus_remove_watch(connection, removed_watch);
372
373         connman_device_driver_unregister(&dundee_driver);
374         connman_network_driver_unregister(&network_driver);
375
376         dbus_connection_unref(connection);
377 }
378
379 CONNMAN_PLUGIN_DEFINE(dundee, "Dundee plugin", VERSION,
380                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, dundee_init, dundee_exit)