Upgrade ofono to 1.11 and merge to 2.0alpha
[profile/ivi/ofono.git] / src / location-reporting.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
6  *  Copyright (C) 2011  Intel Corporation. All rights reserved.
7  *  Copyright (C) 2011  ProFUSION embedded systems.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31
32 #include <glib.h>
33 #include <gdbus.h>
34
35 #include "ofono.h"
36 #include "common.h"
37
38 #ifndef DBUS_TYPE_UNIX_FD
39 #define DBUS_TYPE_UNIX_FD -1
40 #endif
41
42 static GSList *g_drivers = NULL;
43
44 struct ofono_location_reporting {
45         DBusMessage *pending;
46         const struct ofono_location_reporting_driver *driver;
47         void *driver_data;
48         struct ofono_atom *atom;
49         ofono_bool_t enabled;
50         char *client_owner;
51         guint disconnect_watch;
52 };
53
54 static const char *location_reporting_type_to_string(
55                                         enum ofono_location_reporting_type type)
56 {
57         switch (type) {
58         case OFONO_LOCATION_REPORTING_TYPE_NMEA:
59                 return "nmea";
60         };
61
62         return NULL;
63 }
64
65 static DBusMessage *location_reporting_get_properties(DBusConnection *conn,
66                                                 DBusMessage *msg, void *data)
67
68 {
69         struct ofono_location_reporting *lr = data;
70         DBusMessage *reply;
71         DBusMessageIter iter;
72         DBusMessageIter dict;
73         const char *type;
74         dbus_bool_t value;
75
76         reply = dbus_message_new_method_return(msg);
77         if (reply == NULL)
78                 return NULL;
79
80         dbus_message_iter_init_append(reply, &iter);
81         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
82                                         OFONO_PROPERTIES_ARRAY_SIGNATURE,
83                                         &dict);
84
85         value = lr->enabled;
86         ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &value);
87
88         type = location_reporting_type_to_string(lr->driver->type);
89         ofono_dbus_dict_append(&dict, "Type", DBUS_TYPE_STRING, &type);
90
91         dbus_message_iter_close_container(&iter, &dict);
92
93         return reply;
94 }
95
96 static void client_remove(struct ofono_location_reporting *lr)
97 {
98         DBusConnection *conn = ofono_dbus_get_connection();
99
100         if (lr->disconnect_watch) {
101                 g_dbus_remove_watch(conn, lr->disconnect_watch);
102                 lr->disconnect_watch = 0;
103         }
104
105         g_free(lr->client_owner);
106 }
107
108 static void signal_enabled(const struct ofono_location_reporting *lr)
109 {
110         DBusConnection *conn = ofono_dbus_get_connection();
111         const char *path = __ofono_atom_get_path(lr->atom);
112         dbus_bool_t value = lr->enabled;
113
114         ofono_dbus_signal_property_changed(conn, path,
115                                         OFONO_LOCATION_REPORTING_INTERFACE,
116                                         "Enabled", DBUS_TYPE_BOOLEAN, &value);
117 }
118
119 static void client_exited_disable_cb(const struct ofono_error *error,
120                                                                 void *data)
121 {
122         struct ofono_location_reporting *lr = data;
123
124         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
125                 ofono_error("Disabling location-reporting failed");
126                 return;
127         }
128
129         client_remove(lr);
130         lr->enabled = FALSE;
131
132         signal_enabled(lr);
133 }
134
135 static void client_exited(DBusConnection *conn, void *data)
136 {
137         struct ofono_location_reporting *lr = data;
138
139         lr->disconnect_watch = 0;
140
141         lr->driver->disable(lr, client_exited_disable_cb, lr);
142 }
143
144 static void location_reporting_disable_cb(const struct ofono_error *error,
145                                                                 void *data)
146 {
147         struct ofono_location_reporting *lr = data;
148         DBusMessage *reply;
149
150         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
151                 ofono_error("Disabling location-reporting failed");
152
153                 reply = __ofono_error_failed(lr->pending);
154                 __ofono_dbus_pending_reply(&lr->pending, reply);
155                 return;
156         }
157
158         client_remove(lr);
159         lr->enabled = FALSE;
160
161         reply = dbus_message_new_method_return(lr->pending);
162         __ofono_dbus_pending_reply(&lr->pending, reply);
163
164         signal_enabled(lr);
165 }
166
167 static void location_reporting_enable_cb(const struct ofono_error *error,
168                                                         int fd, void *data)
169 {
170         struct ofono_location_reporting *lr = data;
171         DBusConnection *conn = ofono_dbus_get_connection();
172         DBusMessage *reply;
173
174         if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
175                 ofono_error("Enabling location-reporting failed");
176
177                 reply = __ofono_error_failed(lr->pending);
178                 __ofono_dbus_pending_reply(&lr->pending, reply);
179                 return;
180         }
181
182         lr->enabled = TRUE;
183         lr->client_owner = g_strdup(dbus_message_get_sender(lr->pending));
184         lr->disconnect_watch = g_dbus_add_disconnect_watch(conn,
185                                 lr->client_owner, client_exited, lr, NULL);
186
187         reply = dbus_message_new_method_return(lr->pending);
188         dbus_message_append_args(reply, DBUS_TYPE_UNIX_FD, &fd,
189                                                         DBUS_TYPE_INVALID);
190
191         __ofono_dbus_pending_reply(&lr->pending, reply);
192
193         signal_enabled(lr);
194 }
195
196 static DBusMessage *location_reporting_request(DBusConnection *conn,
197                                                 DBusMessage *msg, void *data)
198 {
199         struct ofono_location_reporting *lr = data;
200
201         if (lr->pending != NULL)
202                 return __ofono_error_busy(msg);
203
204         if (lr->enabled)
205                 return __ofono_error_in_use(msg);
206
207         lr->pending = dbus_message_ref(msg);
208
209         lr->driver->enable(lr, location_reporting_enable_cb, lr);
210
211         return NULL;
212 }
213
214 static DBusMessage *location_reporting_release(DBusConnection *conn,
215                                                 DBusMessage *msg, void *data)
216 {
217         struct ofono_location_reporting *lr = data;
218         const char *caller = dbus_message_get_sender(msg);
219
220         /*
221          * Avoid a race by not trying to release the device if there is a
222          * pending message or client already signaled it's exiting. In the
223          * later case, the device will eventually be released in
224          * client_exited_disable_cb().
225          */
226         if (lr->pending != NULL || (lr->enabled && !lr->disconnect_watch))
227                 return __ofono_error_busy(msg);
228
229         if (lr->enabled == FALSE)
230                 return __ofono_error_not_available(msg);
231
232         if (g_strcmp0(caller, lr->client_owner))
233                 return __ofono_error_access_denied(msg);
234
235         lr->pending = dbus_message_ref(msg);
236
237         lr->driver->disable(lr, location_reporting_disable_cb, lr);
238
239         return NULL;
240 }
241
242 static const GDBusMethodTable location_reporting_methods[] = {
243         { GDBUS_METHOD("GetProperties",
244                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
245                         location_reporting_get_properties) },
246         { GDBUS_ASYNC_METHOD("Request",
247                         NULL, GDBUS_ARGS({ "fd", "h" }),
248                         location_reporting_request) },
249         { GDBUS_ASYNC_METHOD("Release", NULL, NULL,
250                                         location_reporting_release) },
251         { }
252 };
253
254 static const GDBusSignalTable location_reporting_signals[] = {
255         { GDBUS_SIGNAL("PropertyChanged",
256                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
257         { }
258 };
259
260 int ofono_location_reporting_driver_register(
261                                 const struct ofono_location_reporting_driver *d)
262 {
263         DBG("driver: %p, name: %s", d, d->name);
264
265         if (d == NULL || d->probe == NULL)
266                 return -EINVAL;
267
268         g_drivers = g_slist_prepend(g_drivers, (void *) d);
269
270         return 0;
271 }
272
273 void ofono_location_reporting_driver_unregister(
274                                 const struct ofono_location_reporting_driver *d)
275 {
276         DBG("driver: %p, name: %s", d, d->name);
277
278         if (d == NULL)
279                 return;
280
281         g_drivers = g_slist_remove(g_drivers, (void *) d);
282 }
283
284 struct ofono_modem *ofono_location_reporting_get_modem(
285                                         struct ofono_location_reporting *lr)
286 {
287         return __ofono_atom_get_modem(lr->atom);
288 }
289
290 static void location_reporting_unregister(struct ofono_atom *atom)
291 {
292         struct ofono_location_reporting *lr = __ofono_atom_get_data(atom);
293         const char *path = __ofono_atom_get_path(lr->atom);
294         DBusConnection *conn = ofono_dbus_get_connection();
295         struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom);
296
297         ofono_modem_remove_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE);
298         g_dbus_unregister_interface(conn, path,
299                                         OFONO_LOCATION_REPORTING_INTERFACE);
300 }
301
302 static void location_reporting_remove(struct ofono_atom *atom)
303 {
304         struct ofono_location_reporting *lr = __ofono_atom_get_data(atom);
305
306         DBG("atom: %p", atom);
307
308         if (lr == NULL)
309                 return;
310
311         if (lr->driver && lr->driver->remove)
312                 lr->driver->remove(lr);
313
314         g_free(lr);
315 }
316
317 struct ofono_location_reporting *ofono_location_reporting_create(
318                                                 struct ofono_modem *modem,
319                                                 unsigned int vendor,
320                                                 const char *driver, void *data)
321 {
322         struct ofono_location_reporting *lr;
323         GSList *l;
324
325         if (driver == NULL)
326                 return NULL;
327
328         /* Only D-Bus >= 1.3 supports fd-passing */
329         if (DBUS_TYPE_UNIX_FD == -1)
330                 return NULL;
331
332         lr = g_try_new0(struct ofono_location_reporting, 1);
333         if (lr == NULL)
334                 return NULL;
335
336         lr->atom = __ofono_modem_add_atom(modem,
337                                         OFONO_ATOM_TYPE_LOCATION_REPORTING,
338                                         location_reporting_remove, lr);
339
340         for (l = g_drivers; l; l = l->next) {
341                 const struct ofono_location_reporting_driver *drv = l->data;
342
343                 if (g_strcmp0(drv->name, driver) != 0)
344                         continue;
345
346                 if (drv->probe(lr, vendor, data) < 0)
347                         continue;
348
349                 lr->driver = drv;
350                 break;
351         }
352
353         return lr;
354 }
355
356 void ofono_location_reporting_register(struct ofono_location_reporting *lr)
357 {
358         DBusConnection *conn = ofono_dbus_get_connection();
359         struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom);
360         const char *path = __ofono_atom_get_path(lr->atom);
361
362         if (!g_dbus_register_interface(conn, path,
363                                         OFONO_LOCATION_REPORTING_INTERFACE,
364                                         location_reporting_methods,
365                                         location_reporting_signals,
366                                         NULL, lr, NULL)) {
367                 ofono_error("Could not create %s interface",
368                                         OFONO_LOCATION_REPORTING_INTERFACE);
369                 return;
370         }
371
372         ofono_modem_add_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE);
373         __ofono_atom_register(lr->atom, location_reporting_unregister);
374 }
375
376 void ofono_location_reporting_remove(struct ofono_location_reporting *lr)
377 {
378         __ofono_atom_free(lr->atom);
379 }
380
381 void ofono_location_reporting_set_data(struct ofono_location_reporting *lr,
382                                                                 void *data)
383 {
384         lr->driver_data = data;
385 }
386
387 void *ofono_location_reporting_get_data(struct ofono_location_reporting *lr)
388 {
389         return lr->driver_data;
390 }