tizen beta release
[platform/core/location/gps-manager.git] / gps-manager / gps_manager.c
1 /*
2  * gps-manager
3  *
4  * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Youngae Kang <youngae.kang@samsung.com>, Yunhan Kim <yhan.kim@samsung.com>,
7  *          Genie Kim <daejins.kim@samsung.com>, Minjune Kim <sena06.kim@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25 #include <time.h>
26 #include <stdlib.h>
27
28 #include <glib.h>
29 #include <glib-object.h>
30
31 #include <dbus/dbus.h>
32 #include <dbus/dbus-glib-bindings.h>
33 #include <dbus/dbus-glib-lowlevel.h>
34
35 #include <geoclue/gc-provider.h>
36 #include <geoclue/geoclue-error.h>
37 #include <geoclue/gc-iface-position.h>
38 #include <geoclue/gc-iface-velocity.h>
39 #include <geoclue/gc-iface-nmea.h>
40 #include <geoclue/gc-iface-satellite.h>
41 #include "gps_manager_data_types.h"
42 #include "gps_manager.h"
43
44 #include "server.h"
45
46 #define GEOCLUE_GPSMANAGER_DBUS_SERVICE "org.freedesktop.Geoclue.Providers.GpsManager"
47 #define GEOCLUE_GPSMANAGER_DBUS_PATH    "/org/freedesktop/Geoclue/Providers/GpsManager"
48
49 typedef struct {
50         GcProvider parent;
51         GMainLoop *loop;
52
53         GeoclueStatus status;
54         pos_data_t position;
55         sv_data_t satellite;
56         nmea_data_t nmea;
57
58         GHashTable *connections;
59 } GeoclueGpsManager;
60
61 typedef struct {
62         GcProviderClass parent_class;
63 } GeoclueGpsManagerClass;
64
65 #define GEOCLUE_TYPE_GPSMANAGER (geoclue_gpsmanager_get_type ())
66 #define GEOCLUE_GPSMANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEOCLUE_TYPE_GPSMANAGER, GeoclueGpsManager))
67
68 static void init_position(GcIfacePositionClass * iface);
69 static void init_velocity(GcIfaceVelocityClass * iface);
70 static void init_nmea(GcIfaceNmeaClass * iface);
71 static void init_satellite(GcIfaceSatelliteClass * iface);
72 static void init_geoclue(GcIfaceGeoclueClass * iface);
73
74 G_DEFINE_TYPE_WITH_CODE(GeoclueGpsManager, geoclue_gpsmanager, GC_TYPE_PROVIDER,
75                         G_IMPLEMENT_INTERFACE(GC_TYPE_IFACE_POSITION, init_position)
76                         G_IMPLEMENT_INTERFACE(GC_TYPE_IFACE_VELOCITY, init_velocity)
77                         G_IMPLEMENT_INTERFACE(GC_TYPE_IFACE_NMEA, init_nmea)
78                         G_IMPLEMENT_INTERFACE(GC_TYPE_IFACE_SATELLITE, init_satellite)
79                         G_IMPLEMENT_INTERFACE(GC_TYPE_IFACE_GEOCLUE, init_geoclue));
80
81 static void constructed(GObject * object)
82 {
83         GeoclueGpsManager *gps_manager = GEOCLUE_GPSMANAGER(object);
84
85         gps_manager->connections = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
86
87         gps_manager->status = GEOCLUE_STATUS_UNAVAILABLE;
88
89         memset(&gps_manager->position, 0x00, sizeof(pos_data_t));
90         memset(&gps_manager->satellite, 0x00, sizeof(sv_data_t));
91         memset(&gps_manager->nmea, 0x00, sizeof(nmea_data_t));
92
93         ((GObjectClass *) geoclue_gpsmanager_parent_class)->constructed(object);
94 }
95
96 static void finalize(GObject * object)
97 {
98         ((GObjectClass *) geoclue_gpsmanager_parent_class)->finalize(object);
99 }
100
101 static void dispose(GObject * object)
102 {
103         GeoclueGpsManager *gpsmanager = GEOCLUE_GPSMANAGER(object);
104
105         g_hash_table_destroy(gpsmanager->connections);
106
107         ((GObjectClass *) geoclue_gpsmanager_parent_class)->dispose(object);
108 }
109
110 static gboolean get_status(GcIfaceGeoclue * gc, GeoclueStatus * status, GError ** error)
111 {
112         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
113
114         *status = gpsmanager->status;
115
116         return TRUE;
117 }
118
119 static gboolean get_provider_info(GcIfaceGeoclue * geoclue, gchar ** name, gchar ** description, GError ** error)
120 {
121         if (name) {
122                 *name = g_strdup("geoclue-gpsmanager");
123         }
124         if (description) {
125                 *description = g_strdup("Geoclue-gpsmanager");
126         }
127         return TRUE;
128 }
129
130 static void print_option(gpointer key, gpointer value, gpointer data)
131 {
132         g_print("   %s - %s\n", (char *)key, (char *)value);
133 }
134
135 static gboolean set_options(GcIfaceGeoclue * gc, GHashTable * options, GError ** error)
136 {
137         g_print("Options received---\n");
138         g_hash_table_foreach(options, print_option, NULL);
139         return TRUE;
140 }
141
142 static void shutdown(GcProvider * provider)
143 {
144         /* No shutdown!! */
145 }
146
147 static void start_tracking(GeoclueGpsManager * gpsmanager)
148 {
149         gpsmanager->status = GEOCLUE_STATUS_ACQUIRING;
150         request_start_session();
151 }
152
153 static void stop_tracking(GeoclueGpsManager * gpsmanager)
154 {
155         request_stop_session();
156         gpsmanager->status = GEOCLUE_STATUS_UNAVAILABLE;
157 }
158
159 static void update_position_cb(pos_data_t * pos, gps_error_t error, void *user_data)
160 {
161         GeocluePositionFields fields;
162         GeoclueAccuracy *accuracy;
163
164         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(user_data));
165         memcpy(&gpsmanager->position, pos, sizeof(pos_data_t));
166
167         gpsmanager->status = GEOCLUE_STATUS_AVAILABLE;
168
169         fields = (GEOCLUE_POSITION_FIELDS_LATITUDE | GEOCLUE_POSITION_FIELDS_LONGITUDE | GEOCLUE_POSITION_FIELDS_ALTITUDE);
170
171         accuracy = geoclue_accuracy_new(GEOCLUE_ACCURACY_LEVEL_DETAILED, pos->hor_accuracy, pos->ver_accuracy);
172
173         gc_iface_position_emit_position_changed(GC_IFACE_POSITION(gpsmanager),
174                                                 fields, pos->timestamp, pos->latitude, pos->longitude, pos->altitude, accuracy);
175
176         fields = (GEOCLUE_VELOCITY_FIELDS_SPEED | GEOCLUE_VELOCITY_FIELDS_DIRECTION);
177
178         gc_iface_velocity_emit_velocity_changed(GC_IFACE_VELOCITY(gpsmanager),
179                                                 fields, pos->timestamp, pos->speed, pos->bearing, 0.0);
180
181         geoclue_accuracy_free(accuracy);
182
183         gc_iface_geoclue_emit_status_changed(GC_IFACE_GEOCLUE(gpsmanager), GEOCLUE_STATUS_AVAILABLE);
184 }
185
186 static void update_satellite_cb(sv_data_t * sv, void *user_data)
187 {
188         int index;
189         int timestamp = 0;
190         int satellite_used = 0;
191         GArray *used_prn;
192         GPtrArray *satellite_info;
193
194         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(user_data));
195         memcpy(&gpsmanager->satellite, sv, sizeof(sv_data_t));
196
197         used_prn = g_array_new(FALSE, FALSE, sizeof(guint));
198         for (index = 0; index < sv->num_of_sat; ++index) {
199                 if (sv->sat[index].used) {
200                         g_array_append_val(used_prn, sv->sat[index].prn);
201                         ++satellite_used;
202                 }
203         }
204
205         satellite_info = g_ptr_array_new();
206
207         for (index = 0; index < sv->num_of_sat; ++index) {
208                 GValue sv_info = { 0, };
209                 g_value_init(&sv_info, GEOCLUE_SATELLITE_INFO);
210                 g_value_take_boxed(&sv_info, dbus_g_type_specialized_construct(GEOCLUE_SATELLITE_INFO));
211                 dbus_g_type_struct_set(&sv_info,
212                                        0, sv->sat[index].prn,
213                                        1, sv->sat[index].elevation,
214                                        2, sv->sat[index].azimuth, 3, sv->sat[index].snr, G_MAXUINT);
215                 g_ptr_array_add(satellite_info, g_value_get_boxed(&sv_info));
216         }
217
218         gc_iface_satellite_emit_satellite_changed(GC_IFACE_SATELLITE(gpsmanager),
219                                                   timestamp, satellite_used, sv->num_of_sat, used_prn, satellite_info);
220
221         g_array_free(used_prn, TRUE);
222         g_ptr_array_free(satellite_info, TRUE);
223 }
224
225 static void update_nmea_cb(nmea_data_t * nmea, void *user_data)
226 {
227         int timestamp = 0;
228
229         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(user_data));
230
231         gpsmanager->nmea.data = g_malloc(nmea->len + 1);
232         g_memmove(gpsmanager->nmea.data, nmea->data, nmea->len);
233         gpsmanager->nmea.data[nmea->len] = '\0';
234
235         gc_iface_nmea_emit_nmea_changed(GC_IFACE_NMEA(gpsmanager), timestamp, gpsmanager->nmea.data);
236
237         g_free(gpsmanager->nmea.data);
238 }
239
240 static void add_reference(GcIfaceGeoclue * gc, DBusGMethodInvocation * context)
241 {
242         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
243         char *sender;
244         int *pcount;
245
246         sender = dbus_g_method_get_sender(context);
247         if (g_hash_table_size(gpsmanager->connections) == 0) {
248                 start_tracking(gpsmanager);
249         }
250         pcount = g_hash_table_lookup(gpsmanager->connections, sender);
251         if (!pcount) {
252                 pcount = g_malloc0(sizeof(int));
253                 g_hash_table_insert(gpsmanager->connections, sender, pcount);
254         }
255         (*pcount)++;
256
257         g_debug("add_reference (%s) (%d)", sender, (*pcount));
258
259         dbus_g_method_return(context);
260 }
261
262 static gboolean remove_client(GeoclueGpsManager * gpsmanager, const char *client)
263 {
264         int *pcount;
265
266         pcount = g_hash_table_lookup(gpsmanager->connections, client);
267         if (!pcount) {
268                 return FALSE;
269         }
270         (*pcount)--;
271         if (*pcount == 0) {
272                 g_hash_table_remove(gpsmanager->connections, client);
273         }
274         if (g_hash_table_size(gpsmanager->connections) == 0) {
275                 g_debug("There is no connections!");
276                 stop_tracking(gpsmanager);
277         }
278         return TRUE;
279 }
280
281 static gboolean remove_all_clients(GeoclueGpsManager * gpsmanager, const char *client)
282 {
283         int *pcount;
284
285         pcount = g_hash_table_lookup(gpsmanager->connections, client);
286         if (!pcount) {
287                 return FALSE;
288         }
289         g_hash_table_remove(gpsmanager->connections, client);
290         if (g_hash_table_size(gpsmanager->connections) == 0) {
291                 g_debug("There is no connections!");
292                 stop_tracking(gpsmanager);
293         }
294         return TRUE;
295 }
296
297 static void remove_reference(GcIfaceGeoclue * gc, DBusGMethodInvocation * context)
298 {
299         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
300         char *sender;
301
302         sender = dbus_g_method_get_sender(context);
303         if (!remove_client(gpsmanager, sender)) {
304                 g_warning("Unreffed by client taht has not been referenced");
305         }
306
307         g_free(sender);
308
309         dbus_g_method_return(context);
310 }
311
312 static void name_owner_changed(DBusGProxy * proxy, const char *name, const char *prev_owner, const char *new_owner, void *gc)
313 {
314         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
315         g_debug("name_owner_changed, name:%s, prev_owner:%s, new_owner:%s", name, prev_owner, new_owner);
316         if (strcmp(new_owner, "") == 0 && strcmp(name, prev_owner) == 0) {
317                 if (remove_all_clients(gpsmanager, prev_owner)) {
318                         g_warning("Impolite client %s disconnected without unreferencing\n", prev_owner);
319                 }
320         }
321 }
322
323 static void geoclue_gpsmanager_class_init(GeoclueGpsManagerClass * klass)
324 {
325         GObjectClass *o_class = (GObjectClass *) klass;
326         GcProviderClass *p_class = (GcProviderClass *) klass;
327
328         o_class->constructed = constructed;
329         o_class->finalize = finalize;
330         o_class->dispose = dispose;
331         p_class->get_status = get_status;
332         p_class->set_options = set_options;
333         p_class->shutdown = shutdown;
334 }
335
336 static void geoclue_gpsmanager_init(GeoclueGpsManager * gpsmanager)
337 {
338         GError *error = NULL;
339         DBusGProxy *driver;
340         guint request_ret;
341         GcProvider *provider;
342         g_debug("geoclue_gpsmanager_init");
343
344         g_return_if_fail(GC_IS_PROVIDER(gpsmanager));
345         provider = GC_PROVIDER(gpsmanager);
346         g_return_if_fail(provider->connection != NULL);
347
348         driver = dbus_g_proxy_new_for_name(provider->connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
349
350         if (!org_freedesktop_DBus_request_name(driver, GEOCLUE_GPSMANAGER_DBUS_SERVICE, 0, &request_ret, &error)) {
351                 g_warning("%s was unable to register service %s: %s",
352                           G_OBJECT_TYPE_NAME(provider), GEOCLUE_GPSMANAGER_DBUS_SERVICE, error->message);
353                 g_error_free(error);
354                 return;
355         }
356
357         dbus_g_proxy_add_signal(driver, "NameOwnerChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
358
359         dbus_g_proxy_connect_signal(driver, "NameOwnerChanged", G_CALLBACK(name_owner_changed), provider, NULL);
360
361         dbus_g_connection_register_g_object(provider->connection, GEOCLUE_GPSMANAGER_DBUS_PATH, G_OBJECT(provider));
362 }
363
364 static gboolean get_position(GcIfacePosition * gc,
365                              GeocluePositionFields * fields,
366                              int *timestamp,
367                              double *latitude,
368                              double *longitude, double *altitude, GeoclueAccuracy ** accuracy, GError ** error)
369 {
370         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
371
372         if (gpsmanager->status == GEOCLUE_STATUS_ACQUIRING) {
373                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_NOT_AVAILABLE, "ERROR: Acquiring");
374                 return FALSE;
375         }
376
377         if (gpsmanager->status != GEOCLUE_STATUS_AVAILABLE) {
378                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Not Available");
379                 return FALSE;
380         }
381
382         if (!fields) {
383                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Invalid parameters");
384                 return FALSE;
385         }
386
387         *fields = GEOCLUE_POSITION_FIELDS_NONE;
388
389         if (timestamp) {
390                 *timestamp = gpsmanager->position.timestamp;
391         }
392
393         if (latitude) {
394                 *fields |= GEOCLUE_POSITION_FIELDS_LATITUDE;
395                 *latitude = gpsmanager->position.latitude;
396         }
397
398         if (longitude) {
399                 *fields |= GEOCLUE_POSITION_FIELDS_LONGITUDE;
400                 *longitude = gpsmanager->position.longitude;
401         }
402
403         if (altitude) {
404                 *fields |= GEOCLUE_POSITION_FIELDS_ALTITUDE;
405                 *altitude = gpsmanager->position.altitude;
406         }
407
408         if (accuracy) {
409                 *accuracy = geoclue_accuracy_new(GEOCLUE_ACCURACY_LEVEL_DETAILED,
410                                                  gpsmanager->position.hor_accuracy, gpsmanager->position.ver_accuracy);
411         }
412
413         return TRUE;
414 }
415
416 static gboolean get_velocity(GcIfaceVelocity * gc,
417                              GeoclueVelocityFields * fields,
418                              int *timestamp, double *speed, double *direction, double *climb, GError ** error)
419 {
420         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
421
422         if (gpsmanager->status == GEOCLUE_STATUS_ACQUIRING) {
423                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_NOT_AVAILABLE, "ERROR: Acquiring");
424                 return FALSE;
425         }
426
427         if (gpsmanager->status != GEOCLUE_STATUS_AVAILABLE) {
428                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Not Available");
429                 return FALSE;
430         }
431
432         if (!fields) {
433                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Invalid parameters");
434                 return FALSE;
435         }
436
437         *fields = GEOCLUE_VELOCITY_FIELDS_NONE;
438
439         if (timestamp) {
440                 *timestamp = gpsmanager->position.timestamp;
441         }
442
443         if (speed) {
444                 *fields |= GEOCLUE_VELOCITY_FIELDS_SPEED;
445                 *speed = gpsmanager->position.speed;
446         }
447
448         if (direction) {
449                 *fields |= GEOCLUE_VELOCITY_FIELDS_DIRECTION;
450                 *direction = gpsmanager->position.bearing;
451         }
452
453         /* A climb field does not supported because of poor accuracy. */
454
455         return TRUE;
456 }
457
458 static gboolean get_nmea(GcIfaceNmea * gc, int *timestamp, char **nmea_data, GError ** error)
459 {
460         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
461
462         if (gpsmanager->status == GEOCLUE_STATUS_UNAVAILABLE || gpsmanager->status == GEOCLUE_STATUS_ERROR) {
463                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Not Available");
464                 return FALSE;
465         }
466
467         if (timestamp) {
468                 *timestamp = gpsmanager->position.timestamp;
469         }
470
471         if (nmea_data) {
472                 *nmea_data = g_memdup(gpsmanager->nmea.data, gpsmanager->nmea.len);
473         }
474
475         return TRUE;
476 }
477
478 static gboolean get_satellite(GcIfaceSatellite * gc,
479                               int *timestamp,
480                               int *satellite_used,
481                               int *satellite_visible, GArray ** used_prn, GPtrArray ** satellite_info, GError ** error)
482 {
483         GeoclueGpsManager *gpsmanager = (GEOCLUE_GPSMANAGER(gc));
484
485         if (gpsmanager->status == GEOCLUE_STATUS_UNAVAILABLE || gpsmanager->status == GEOCLUE_STATUS_ERROR) {
486                 g_set_error(error, GEOCLUE_ERROR, GEOCLUE_ERROR_FAILED, "ERROR: Not Available");
487                 return FALSE;
488         }
489
490         if (timestamp) {
491                 *timestamp = gpsmanager->position.timestamp;
492         }
493
494         if (satellite_used) {
495                 int index;
496                 int count = 0;
497                 for (index = 0; index < gpsmanager->satellite.num_of_sat; ++index) {
498                         count += gpsmanager->satellite.sat[index].used ? 1 : 0;
499                 }
500                 *satellite_used = count;
501         }
502
503         if (satellite_visible) {
504                 *satellite_visible = gpsmanager->satellite.num_of_sat;
505         }
506
507         if (used_prn) {
508                 int index;
509                 *used_prn = g_array_new(FALSE, FALSE, sizeof(guint));
510
511                 for (index = 0; index < gpsmanager->satellite.num_of_sat; ++index) {
512                         if (gpsmanager->satellite.sat[index].used) {
513                                 g_array_append_val(*used_prn, gpsmanager->satellite.sat[index].prn);
514                         }
515                 }
516         }
517
518         if (satellite_info) {
519                 int index;
520                 *satellite_info = g_ptr_array_new();
521                 for (index = 0; index < gpsmanager->satellite.num_of_sat; ++index) {
522                         GValue sv_info = { 0, };
523                         g_value_init(&sv_info, GEOCLUE_SATELLITE_INFO);
524                         g_value_take_boxed(&sv_info, dbus_g_type_specialized_construct(GEOCLUE_SATELLITE_INFO));
525                         dbus_g_type_struct_set(&sv_info,
526                                                0, gpsmanager->satellite.sat[index].prn,
527                                                1, gpsmanager->satellite.sat[index].elevation,
528                                                2, gpsmanager->satellite.sat[index].azimuth,
529                                                3, gpsmanager->satellite.sat[index].snr, G_MAXUINT);
530                         g_ptr_array_add(*satellite_info, g_value_get_boxed(&sv_info));
531                 }
532
533         }
534         return TRUE;
535 }
536
537 static void init_position(GcIfacePositionClass * iface)
538 {
539         iface->get_position = get_position;
540 }
541
542 static void init_velocity(GcIfaceVelocityClass * iface)
543 {
544         iface->get_velocity = get_velocity;
545 }
546
547 static void init_nmea(GcIfaceNmeaClass * iface)
548 {
549         iface->get_nmea = get_nmea;
550 }
551
552 static void init_satellite(GcIfaceSatelliteClass * iface)
553 {
554         iface->get_satellite = get_satellite;
555 }
556
557 static void init_geoclue(GcIfaceGeoclueClass * iface)
558 {
559         iface->get_provider_info = get_provider_info;
560         iface->add_reference = add_reference;
561         iface->remove_reference = remove_reference;
562 }
563
564 int main(int argc, char **argv)
565 {
566         GeoclueGpsManager *gpsmanager;
567         struct gps_callbacks cb;
568         cb.pos_cb = update_position_cb;
569         cb.sv_cb = update_satellite_cb;
570         cb.nmea_cb = update_nmea_cb;
571
572         if (!g_thread_supported()) {
573                 g_thread_init(NULL);
574         }
575         dbus_g_thread_init();
576         g_type_init();
577
578         initialize_server(argc, argv);
579
580         gpsmanager = g_object_new(GEOCLUE_TYPE_GPSMANAGER, NULL);
581         register_update_callbacks(&cb, gpsmanager);
582
583         gpsmanager->loop = g_main_loop_new(NULL, TRUE);
584         g_main_loop_run(gpsmanager->loop);
585
586         g_main_loop_unref(gpsmanager->loop);
587         g_object_unref(gpsmanager);
588
589         deinitialize_server();
590
591         return 0;
592 }