summaryrefslogtreecommitdiff
path: root/src/lib/elocation
diff options
context:
space:
mode:
authorStefan Schmidt <s.schmidt@samsung.com>2014-10-28 11:22:55 +0100
committerStefan Schmidt <s.schmidt@samsung.com>2014-11-11 11:02:08 +0100
commita2d2cdaf9f258ec4c6c7c33d912bbe41625e8532 (patch)
tree62f61f67cd6a74790e1b11f35a55f3a22e82b5ea /src/lib/elocation
parentc2f130a2f77f7f319e819ff7883c8695aa45319e (diff)
elocation: Add elocation libraray to EFL.
Elocation is meant as a convenience library to ease application developers the usage of geo information in their apps. Adding a geo tag to a picture or translating an address to a GPS position and show it on a map widget are just some of the use cases. In the beginning elocation will rely on the GeoClue1 DBus service. Supporting the new GeoClue2 DBus service is planned and worked on. GeoClue offers providers for various techniques to get hold off the current position. Ranging from GeoIP over wifi and GSM cell location to GPS. This has been developed a while ago and was living in my private dev space. It is about time to move this into EFL and bring it forward. The detection of the GeoClue service is being handled on runtime and no new dependency is added due to this library. @feature
Diffstat (limited to 'src/lib/elocation')
-rw-r--r--src/lib/elocation/Elocation.h418
-rw-r--r--src/lib/elocation/elocation.c1416
-rw-r--r--src/lib/elocation/elocation_private.h181
3 files changed, 2015 insertions, 0 deletions
diff --git a/src/lib/elocation/Elocation.h b/src/lib/elocation/Elocation.h
new file mode 100644
index 0000000000..851b679172
--- /dev/null
+++ b/src/lib/elocation/Elocation.h
@@ -0,0 +1,418 @@
1/**
2 * @brief Elocation Library
3 *
4 * @mainpage Elocation
5 * @version 0.0.0
6 * @author Stefan Schmidt <stefan@datenfreihafen.org>
7 * @date 2012
8 *
9 * @section intro Elocation Use Cases
10 *
11 * Elocation is meant as a convenience library to ease application developers
12 * the usage of geo information in their apps. Adding a geo tag to a picture or
13 * translating an address to a GPS position and show it on a map widget are just
14 * some of the use cases.
15 *
16 * In the beginning elocation will rely on the GeoClue DBus service. Its has
17 * providers for various techniques to get hold off the current position.
18 * Ranging from GeoIP over wifi and GSM cell location to GPS. As well as
19 * provider to translates between location in a textual form to coordinates
20 * (GeoCode).
21 *
22 * Elocation covers all of these interfaces but in the end it depends on your
23 * system and the installed GeoClue providers what can be used.
24 *
25 * Currently it offer the following functionality:
26 * @li Request current address in textual form
27 * @li Request current position in GPS format
28 * @li Translate a position into and address or an address in a position
29 *
30 * You can find the API documentation at @ref Location
31*/
32#ifndef _ELOCATION_H
33#define _ELOCATION_H
34
35#ifdef EAPI
36# undef EAPI
37#endif
38
39#ifdef _WIN32
40# ifdef EFL_ECORE_BUILD
41# ifdef DLL_EXPORT
42# define EAPI __declspec(dllexport)
43# else
44# define EAPI
45# endif /* ! DLL_EXPORT */
46# else
47# define EAPI __declspec(dllimport)
48# endif /* ! EFL_ECORE_BUILD */
49#else
50# ifdef __GNUC__
51# if __GNUC__ >= 4
52# define EAPI __attribute__ ((visibility("default")))
53# else
54# define EAPI
55# endif
56# else
57# define EAPI
58# endif
59#endif /* ! _WIN32 */
60
61#include <stdio.h>
62
63#include <Ecore.h>
64#include <Eldbus.h>
65
66/**
67 * @file Elocation.h
68 *
69 * @defgroup Location Location
70 */
71
72/**
73 * @ingroup Location
74 * @brief Available location events that are emitted from the library
75 * @since 1.8
76 *
77 * Ecore events emitted by the library. Applications can register ecore event
78 * handlers to react on such events. After the initial query this can be used
79 * to keep track of changes and update your UI or data accordingly.
80 * @{
81 */
82EAPI extern int ELOCATION_EVENT_STATUS; /**< Status changed */
83EAPI extern int ELOCATION_EVENT_POSITION; /**< Position changed */
84EAPI extern int ELOCATION_EVENT_ADDRESS; /**< Address changed */
85EAPI extern int ELOCATION_EVENT_VELOCITY; /**< Velocity changed */
86EAPI extern int ELOCATION_EVENT_GEOCODE; /**< Reply for geocode translation arrived */
87EAPI extern int ELOCATION_EVENT_REVERSEGEOCODE; /**< Reply for geocode translation arrived */
88EAPI extern int ELOCATION_EVENT_NMEA; /**< NMEA update */
89EAPI extern int ELOCATION_EVENT_SATELLITE; /**< Satellite info changed */
90EAPI extern int ELOCATION_EVENT_POI; /**< POI reply */
91EAPI extern int ELOCATION_EVENT_META_READY; /**< Meta provider is ready to be used */
92/**@}*/
93
94/**
95 * @ingroup Location
96 * @typedef Elocation_Accuracy_Level
97 * @since 1.8
98 *
99 * Different location accuracy levels from country level up to detailed,
100 * e.g. GPS, information.
101 * @{
102 */
103typedef enum {
104 ELOCATION_ACCURACY_LEVEL_NONE = 0,
105 ELOCATION_ACCURACY_LEVEL_COUNTRY,
106 ELOCATION_ACCURACY_LEVEL_REGION,
107 ELOCATION_ACCURACY_LEVEL_LOCALITY,
108 ELOCATION_ACCURACY_LEVEL_POSTALCODE,
109 ELOCATION_ACCURACY_LEVEL_STREET,
110 ELOCATION_ACCURACY_LEVEL_DETAILED,
111} Elocation_Accuracy_Level;
112/**@}*/
113
114/**
115 * @ingroup Location
116 * @typedef Elocation_Resource_Flags
117 * @since 1.8
118 *
119 * Flags for available system resources to be used for locating. So far they
120 * cover physical resources like network connection, cellular network
121 * connection and GPS.
122 * @{
123 */
124typedef enum {
125 ELOCATION_RESOURCE_NONE = 0,
126 ELOCATION_RESOURCE_NETWORK = 1 << 0, /**< Internet connection is available */
127 ELOCATION_RESOURCE_CELL = 1 << 1, /**< Cell network information, e.g. GSM, is available */
128 ELOCATION_RESOURCE_GPS = 1 << 2, /**< GPS information is available */
129
130 ELOCATION_RESOURCE_ALL = (1 << 10) - 1 /**< All resources are available */
131} Elocation_Resource_Flags;
132/**@}*/
133
134/**
135 * @ingroup Location
136 * @typedef Elocation_Accuracy
137 * @since 1.8
138 *
139 * Information about the accuracy of the reported location. For details about
140 * the level of accuracy see #Elocation_Accuracy_Level. It also covers
141 * horizontal and vertical accuracy. The values depend on the used provider
142 * and may very in quality.
143 */
144typedef struct _Elocation_Accuracy
145{
146 Elocation_Accuracy_Level level;
147 double horizontal;
148 double vertical;
149} Elocation_Accuracy;
150
151/**
152 * @ingroup Location
153 * @typedef Elocation_Address
154 * @since 1.8
155 *
156 * Location information in textual form. Depending on the used provider this
157 * can cover only the country or a detailed address with postcode and street.
158 * The level of detail varies depending on the used provider.
159 * A timestamp is available to calculate the age of the address data.
160 */
161typedef struct _Elocation_Address
162{
163 unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
164 char *country;
165 char *countrycode;
166 char *locality;
167 char *postalcode;
168 char *region;
169 char *timezone;
170 Elocation_Accuracy *accur;
171} Elocation_Address;
172
173/**
174 * @ingroup Location
175 * @typedef Elocation_Position
176 * @since 1.8
177 *
178 * Location information based on the GPS grid. Latitude, longitude and altitude.
179 * A timestamp is available to calculate the age of the address data.
180 */
181typedef struct _Elocation_Postion
182{
183 unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
184 double latitude;
185 double longitude;
186 double altitude;
187 Elocation_Accuracy *accur;
188} Elocation_Position;
189
190/**
191 * @ingroup Location
192 * @typedef Elocation_Velocity
193 * @since 1.8
194 *
195 * Velocity information. So far this interface is only offered with GPS based
196 * providers. It offers information about speed, direction and climb.
197 * A timestamp is available to calculate the age of the address data.
198 *
199 * FIXME: check units and formats of this values coming in from GeoClue
200 */
201typedef struct _Elocation_Velocity
202{
203 unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
204 double speed;
205 double direction;
206 double climb;
207} Elocation_Velocity;
208
209/**
210 * @ingroup Location
211 * @typedef Elocation_Requirements
212 * @since 1.8
213 *
214 * Requirement settings for the location provider. Requirements can be a level
215 * of accuracy or allowed resources like network access or GPS. See
216 * #Elocation_Resource_Flags for all available resource flags.
217 *
218 * Based on this setting the best provider is chosen between the available
219 * providers of GeoClue.
220 */
221typedef struct _Elocation_Requirements
222{
223 Elocation_Accuracy_Level accurancy_level;
224 int min_time; /**< Minimal time between updates. Not implemented upstream */
225 Eina_Bool require_update;
226 Elocation_Resource_Flags allowed_resources;
227} Elocation_Requirements;
228
229/**
230 * @brief Create a new address object to operate on.
231 * @return Address object.
232 *
233 * The returned address object is safe to be operated on. It can be used for
234 * all other elocation functions. Once finished with it it need to be destroyed
235 * with a call to #elocation_address_free.
236 *
237 * @ingroup Location
238 * @since 1.8
239 */
240EAPI Elocation_Address *elocation_address_new(void);
241
242/**
243 * @brief Free an address object
244 * @param address Address object to be freed.
245 *
246 * Destroys an address object created with #elocation_address_new. Should be
247 * used during the cleanup of the application or whenever the address object is
248 * no longer needed.
249 *
250 * @ingroup Location
251 * @since 1.8
252 */
253EAPI void elocation_address_free(Elocation_Address *address);
254
255/**
256 * @brief Create a new position object to operate on.
257 * @return Position object.
258 *
259 * The returned address object is safe to be operated on. It can be used for
260 * all other elocation functions. Once finished with it it need to be destroyed
261 * with a call to #elocation_address_free.
262 *
263 * @ingroup Location
264 * @since 1.8
265 */
266EAPI Elocation_Position *elocation_position_new(void);
267
268/**
269 * @brief Free an position object
270 * @param position Position object to be freed.
271 *
272 * Destroys a position object created with #elocation_address_new. Should be
273 * used during the cleanup of the application or whenever the location object is
274 * no longer needed.
275 *
276 * @ingroup Location
277 * @since 1.8
278 */
279EAPI void elocation_position_free(Elocation_Position *position);
280
281/**
282 * @brief Get the current address information.
283 * @param address Address struct to be filled with information.
284 * @return EINA_TRUE for success and EINA_FALSE for failure.
285 *
286 * Request the latest address. The requested to the underling components might
287 * be asynchronous so better check the timestamp if the data has changed. To get
288 * events when the address changes one can also subscribe to the
289 * #ELOCATION_EVENT_ADDRESS ecore event which will have the address object as
290 * event.
291 *
292 * @ingroup Location
293 * @since 1.8
294 */
295EAPI Eina_Bool elocation_address_get(Elocation_Address *address);
296
297/**
298 * @brief Get the current position information.
299 * @param position Position struct to be filled with information.
300 * @return EINA_TRUE for success and EINA_FALSE for failure.
301 *
302 * Request the latest position. The requested to the underling components might
303 * be asynchronous so better check the timestamp if the data has changed. To get
304 * events when the position changes one can also subscribe to the
305 * #ELOCATION_EVENT_POSITION ecore event which will have the position object as
306 * event.
307 *
308 * @ingroup Location
309 * @since 1.8
310 */
311EAPI Eina_Bool elocation_position_get(Elocation_Position *position);
312
313/**
314 * @brief Get the current status.
315 * @param status Status
316 * @return EINA_TRUE for success and EINA_FALSE for failure.
317 *
318 * @ingroup Location
319 * @since 1.8
320 */
321EAPI Eina_Bool elocation_status_get(int *status);
322
323/**
324 * @brief Set the requirements.
325 * @param requirements Requirements
326 * @return EINA_TRUE for success and EINA_FALSE for failure.
327 *
328 * Set the requirements for selecting a provider.
329 *
330 * @ingroup Location
331 * @since 1.8
332 */
333EAPI Eina_Bool elocation_requirements_set(Elocation_Requirements *requirements);
334
335/**
336 * @brief Convert position to address
337 * @param position_shadow Position input
338 * @param address_shadow Address output
339 * @return EINA_TRUE for success and EINA_FALSE for failure.
340 *
341 * Use a GeoCode provider to translate from a given GPS coordinate
342 * representation of a location to a representation in textual form.
343 *
344 * @ingroup Location
345 * @since 1.8
346 */
347EAPI Eina_Bool elocation_position_to_address(Elocation_Position *position_shadow, Elocation_Address *address_shadow);
348
349/**
350 * @brief Convert address to position
351 * @param address_shadow Address input
352 * @param position_shadow Position output
353 * @return EINA_TRUE for success and EINA_FALSE for failure.
354 *
355 * Use a GeoCode provider to translate from a given textual form
356 * representation of a location to a representation as GPS coordinates.
357 *
358 * @ingroup Location
359 * @since 1.8
360 */
361EAPI Eina_Bool elocation_address_to_position(Elocation_Address *address_shadow, Elocation_Position *position_shadow);
362
363/**
364 * @brief Convert free form address tring to position
365 * @param freeform_address Address string in free form
366 * @param position_shadow Position output
367 * @return EINA_TRUE for success and EINA_FALSE for failure.
368 *
369 * Similar GeoCode translation from textual form to GPS coordinates as
370 * #elocation_address_to_position but in this case the address is a simple
371 * string which hopefully contains enough information for the provider to
372 * understand and translate.
373 *
374 * Useful for an easy search interface in an application but also more error
375 * prone regarding correct results.
376 *
377 * @ingroup Location
378 * @since 1.8
379 */
380EAPI Eina_Bool elocation_freeform_address_to_position(const char *freeform_address, Elocation_Position *position_shadow);
381
382/**
383 * @brief Request a landmark position
384 * @param position_shadow Position ouput
385 * @param address_shadow Address input
386 * @return EINA_TRUE for success and EINA_FALSE for failure.
387 *
388 * Request a landmark position also known as Point Of Interest (POI) from
389 * GeoClue.
390 *
391 * @ingroup Location
392 * @since 1.8
393 */
394EAPI Eina_Bool elocation_landmarks_get(Elocation_Position *position_shadow, Elocation_Address *address_shadow);
395
396/**
397 * @brief Initialize the elocation subsystem.
398 * @return EINA_TRUE for success and EINA_FALSE for failure.
399 *
400 * This function must be called before using any of the Elocation functionality
401 * in your application to make sure it it setup correctly for usage.
402 *
403 * @ingroup Location
404 * @since 1.8
405 */
406EAPI Eina_Bool elocation_init(void);
407
408/**
409 * @brief Cleanup and shutdown the elocation subsystem.
410 *
411 * This function must be called when the application is no longer using any of
412 * the Elocation functionality to allow the subsystem to shutdown cleanly.
413 *
414 * @ingroup Location
415 * @since 1.8
416 */
417EAPI void elocation_shutdown(void);
418#endif
diff --git a/src/lib/elocation/elocation.c b/src/lib/elocation/elocation.c
new file mode 100644
index 0000000000..a14d19e483
--- /dev/null
+++ b/src/lib/elocation/elocation.c
@@ -0,0 +1,1416 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include <stdio.h>
6
7#include <Eina.h>
8#include <Ecore.h>
9#include <Eldbus.h>
10#include <Elocation.h>
11#include <elocation_private.h>
12
13/* FIXME: These globals really need to get reduced before leaving the PROTO
14 * area.
15 */
16static char *unique_name = NULL;
17static Eldbus_Connection *conn = NULL;
18static Elocation_Provider *address_provider = NULL;
19static Elocation_Provider *position_provider = NULL;
20static Eldbus_Object *obj_meta = NULL;
21static Eldbus_Proxy *manager_master = NULL;
22static Eldbus_Proxy *meta_geoclue = NULL;
23static Eldbus_Proxy *meta_address = NULL;
24static Eldbus_Proxy *meta_position = NULL;
25static Eldbus_Proxy *meta_masterclient = NULL;
26static Eldbus_Proxy *meta_velocity = NULL;
27static Eldbus_Proxy *meta_nmea = NULL;
28static Eldbus_Proxy *meta_satellite = NULL;
29static Eldbus_Proxy *geonames_geocode = NULL;
30static Eldbus_Proxy *geonames_rgeocode = NULL;
31static Eldbus_Proxy *master_poi = NULL;
32static Elocation_Address *address = NULL;
33static Elocation_Position *position = NULL;
34static Elocation_Address *addr_geocode = NULL;
35static Elocation_Position *pos_geocode = NULL;
36static Elocation_Velocity *velocity = NULL;
37static int *status = -1; /* 0 is a valid status code */
38static char nmea_sentence[256];
39
40int _elocation_log_dom = -1;
41
42/* Elocation ecore event types we provide to the application. */
43EAPI int ELOCATION_EVENT_IN;
44EAPI int ELOCATION_EVENT_OUT;
45EAPI int ELOCATION_EVENT_STATUS;
46EAPI int ELOCATION_EVENT_POSITION;
47EAPI int ELOCATION_EVENT_ADDRESS;
48EAPI int ELOCATION_EVENT_VELOCITY;
49EAPI int ELOCATION_EVENT_GEOCODE;
50EAPI int ELOCATION_EVENT_REVERSEGEOCODE;
51EAPI int ELOCATION_EVENT_NMEA;
52EAPI int ELOCATION_EVENT_SATELLITE;
53EAPI int ELOCATION_EVENT_POI;
54EAPI int ELOCATION_EVENT_META_READY;
55
56static void
57_dummy_free(void *user_data, void *func_data)
58{
59 /* Don't free the event data after dispatching the event. We keep track of
60 * it on our own
61 */
62}
63
64/* Generic provider message unmarshaller. Used from all different provider
65 * calbacks that receive such a message
66 */
67static Eina_Bool
68unmarshall_provider(const Eldbus_Message *reply, Elocation_Provider *provider)
69{
70 char *name = NULL, *desc = NULL, *service = NULL, *path = NULL;
71
72 if (!eldbus_message_arguments_get(reply, "ssss", &name, &desc, &service, &path))
73 return EINA_FALSE;
74
75 provider->name = strdup(name);
76 provider->description = strdup(desc);
77 provider->service = strdup(service);
78 provider->path = strdup(path);
79 return EINA_TRUE;
80}
81
82static void
83meta_address_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
84{
85 const char *err, *errmsg;
86 Elocation_Provider *addr_provider;
87
88 addr_provider = data;
89
90 if (eldbus_message_error_get(reply, &err, &errmsg))
91 {
92 ERR("Error: %s %s", err, errmsg);
93 return;
94 }
95
96 if (!unmarshall_provider(reply, addr_provider))
97 {
98 ERR("Error: Unable to unmarshall address provider");
99 return;
100 }
101
102 DBG("Meta address provider name: %s, %s, %s, %s", addr_provider->name,
103 addr_provider->description,
104 addr_provider->service,
105 addr_provider->path);
106}
107
108static void
109meta_position_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
110{
111 const char *err, *errmsg;
112 Elocation_Provider *pos_provider;
113
114 pos_provider = data;
115
116 if (eldbus_message_error_get(reply, &err, &errmsg))
117 {
118 ERR("Error: %s %s", err, errmsg);
119 return;
120 }
121
122 if (!unmarshall_provider(reply, pos_provider))
123 {
124 ERR("Error: Unable to unmarshall position provider");
125 return;
126 }
127
128 DBG("Meta position provider name: %s, %s, %s, %s", pos_provider->name,
129 pos_provider->description,
130 pos_provider->service,
131 pos_provider->path);
132}
133
134static void
135meta_address_provider_info_signal_cb(void *data, const Eldbus_Message *reply)
136{
137 Elocation_Provider *addr_provider;
138 addr_provider = data;
139
140 if (!unmarshall_provider(reply, addr_provider))
141 {
142 ERR("Error: Unable to unmarshall address provider");
143 return;
144 }
145
146 DBG("Meta address provider name changed: %s, %s, %s, %s", addr_provider->name,
147 addr_provider->description,
148 addr_provider->service,
149 addr_provider->path);
150}
151
152static void
153meta_position_provider_info_signal_cb(void *data, const Eldbus_Message *reply)
154{
155 Elocation_Provider *pos_provider;
156 pos_provider = data;
157
158 if (!unmarshall_provider(reply, pos_provider))
159 {
160 ERR("Error: Unable to unmarshall position provider");
161 return;
162 }
163
164 DBG("Meta position provider name changed: %s, %s, %s, %s", pos_provider->name,
165 pos_provider->description,
166 pos_provider->service,
167 pos_provider->path);
168}
169
170/* A address is quite flexible what kind of key value pairs it contains in the
171 * dict. Similar to a reverse GeoCode message as both return an address object.
172 */
173static Eina_Bool
174unmarshall_address(const Eldbus_Message *reply, Elocation_Address *addr)
175{
176 int32_t level, timestamp;
177 Eldbus_Message_Iter *sub, *dict, *entry;
178 double horizontal;
179 double vertical;
180 const char *key, *signature;
181 char *value;
182
183 signature = eldbus_message_signature_get(reply);
184
185 if (!strcmp(signature, "ia{ss}(idd)"))
186 {
187 if (!eldbus_message_arguments_get(reply, "ia{ss}(idd)", &timestamp, &dict, &sub))
188 return EINA_FALSE;
189 addr->timestamp = timestamp;
190 }
191 else if (!strcmp(signature, "a{ss}(idd)"))
192 {
193 if (!eldbus_message_arguments_get(reply, "a{ss}(idd)", &dict, &sub))
194 return EINA_FALSE;
195 addr->timestamp = 0;
196 }
197 else
198 return EINA_FALSE;
199
200
201 /* Cleanup potential old entries before re-using */
202 addr->country = NULL;
203 addr->countrycode = NULL;
204 addr->locality = NULL;
205 addr->postalcode = NULL;
206 addr->region = NULL;
207 addr->timezone = NULL;
208
209 while (eldbus_message_iter_get_and_next(dict, 'e', &entry))
210 {
211 eldbus_message_iter_arguments_get(entry, "ss", &key, &value);
212
213 if (!strcmp(key, "country"))
214 {
215 free(addr->country);
216 addr->country = strdup(value);
217 }
218 else if (!strcmp(key, "countrycode"))
219 {
220 free(addr->countrycode);
221 addr->countrycode = strdup(value);
222 }
223 else if (!strcmp(key, "locality"))
224 {
225 free(addr->locality);
226 addr->locality = strdup(value);
227 }
228 else if (!strcmp(key, "postalcode"))
229 {
230 free(addr->postalcode);
231 addr->postalcode = strdup(value);
232 }
233 else if (!strcmp(key, "region"))
234 {
235 free(addr->region);
236 addr->region = strdup(value);
237 }
238 else if (!strcmp(key, "timezone"))
239 {
240 free(addr->timezone);
241 addr->timezone = strdup(value);
242 }
243 }
244
245 eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical);
246 addr->accur->level = level;
247 addr->accur->horizontal = horizontal;
248 addr->accur->vertical = vertical;
249 return EINA_TRUE;
250}
251
252/* Receive and unmarshall a reverse GeoCode message. The dict can contain a
253 * variable set of key value pairs so we need to handle this with care
254 */
255static void
256rgeocode_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
257{
258 const char *err, *errmsg;
259
260 if (eldbus_message_error_get(reply, &err, &errmsg))
261 {
262 ERR("Error: %s %s", err, errmsg);
263 return;
264 }
265
266 if (!unmarshall_address(reply, addr_geocode))
267 {
268 ERR("Error: Unable to unmarshall address");
269 return;
270 }
271
272 /* Send out an event to all interested parties that we have an update */
273 ecore_event_add(ELOCATION_EVENT_REVERSEGEOCODE, addr_geocode, _dummy_free, NULL);
274}
275
276/* Point of Interest (POI) aka landmark message unmarshalling. Thsi interface is
277 * not in standard GeoClue but currently a Tizen extension.
278 */
279static void
280poi_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
281{
282 int32_t count, id, rank;
283 double lat, lon, bound_left, bound_top, bound_right, bound_bottom;
284 const char *name, *icon, *house, *road, *village, *suburb, *postcode;
285 const char *city, *county, *country, *country_code;
286 Eldbus_Message_Iter *array, *struct_landmark;
287 const char *err, *errmsg;
288
289 if (eldbus_message_error_get(reply, &err, &errmsg))
290 {
291 ERR("Error: %s %s", err, errmsg);
292 return;
293 }
294
295 /* Yeah, its quite a horrible message. The POI interface could use a better design */
296 if (!eldbus_message_arguments_get(reply, "ia(iiddddddsssssssssss", &count ,&array))
297 return;
298
299 /* TODO re-check that the parameter ordering is what we expect */
300 while (eldbus_message_iter_get_and_next(array, 'r', &struct_landmark))
301 {
302 eldbus_message_iter_arguments_get(struct_landmark, "iiddddddsssssssssss", &id, &rank,
303 &lat, &lon, &bound_left, &bound_top, &bound_right,
304 &bound_bottom, &name, &icon, &house, &road,
305 &village, &suburb, &postcode, &city, &county,
306 &country, &country_code);
307 DBG("Landmark received: %i, %i, %f, %f, %f, %f, %f, %f, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,",
308 id, rank, lat, lon, bound_left, bound_top, bound_right,
309 bound_bottom, name, icon, house, road, village,
310 suburb, postcode, city, county, country, country_code);
311 }
312
313 /* Send out an event to all interested parties that we have an update */
314 ecore_event_add(ELOCATION_EVENT_POI, NULL, _dummy_free, NULL);
315}
316
317/* Unmarshall a GeoCode message */
318static void
319geocode_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
320{
321 GeocluePositionFields fields;
322 int32_t level;
323 double horizontal = 0.0;
324 double vertical = 0.0;
325 double latitude = 0.0;
326 double longitude = 0.0;
327 double altitude = 0.0;
328 Eldbus_Message_Iter *sub;
329 const char *err, *errmsg;
330
331 if (eldbus_message_error_get(reply, &err, &errmsg))
332 {
333 ERR("Error: %s %s", err, errmsg);
334 return;
335 }
336
337 if (!eldbus_message_arguments_get(reply, "iddd(idd)", &fields,&latitude,
338 &longitude, &altitude, &sub))
339 return;
340
341 /* GeoClue uses some flags to mark position fields as valid. We set invalid
342 * fields to 0.0 */
343 if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
344 pos_geocode->latitude = latitude;
345 else
346 pos_geocode->latitude = 0.0;
347
348 if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
349 pos_geocode->longitude = longitude;
350 else
351 pos_geocode->longitude = 0.0;
352
353 if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
354 pos_geocode->altitude = altitude;
355 else
356 pos_geocode->altitude = 0.0;
357
358 eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical);
359 pos_geocode->accur->level = level;
360 pos_geocode->accur->horizontal = horizontal;
361 pos_geocode->accur->vertical = vertical;
362 /* Send out an event to all interested parties that we have an update */
363 ecore_event_add(ELOCATION_EVENT_GEOCODE, pos_geocode, _dummy_free, NULL);
364}
365
366static void
367address_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
368{
369 const char *err, *errmsg;
370
371 if (eldbus_message_error_get(reply, &err, &errmsg))
372 {
373 ERR("Error: %s %s", err, errmsg);
374 return;
375 }
376
377 if (!unmarshall_address(reply, address))
378 {
379 ERR("Error: Unable to unmarshall address");
380 return;
381 }
382
383 /* Send out an event to all interested parties that we have an update */
384 ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL);
385}
386
387static void
388address_signal_cb(void *data, const Eldbus_Message *reply)
389{
390 const char *err, *errmsg;
391
392 if (eldbus_message_error_get(reply, &err, &errmsg))
393 {
394 ERR("Error: %s %s", err, errmsg);
395 return;
396 }
397
398 if (!unmarshall_address(reply, address))
399 {
400 ERR("Error: Unable to unmarshall address");
401 return;
402 }
403
404 /* Send out an event to all interested parties that we have an update */
405 ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL);
406}
407
408/* Unmarshall a velocity message. This is only available if we use a GPS
409 * provider from GeoClue. None of the other providers offer this currently.
410 */
411static Eina_Bool
412unmarshall_velocity(const Eldbus_Message *reply)
413{
414 GeoclueVelocityFields fields;
415 int32_t timestamp = 0;
416 double speed = 0.0;
417 double direction = 0.0;
418 double climb = 0.0;
419
420 if (!eldbus_message_arguments_get(reply, "iiddd", &fields, &timestamp,
421 &speed, &direction, &climb))
422 return EINA_FALSE;
423
424 velocity->timestamp = timestamp;
425
426 /* GeoClue uses some flags to mark velocity fields as valid. We set invalid
427 * fields to 0.0 */
428 if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED)
429 velocity->speed = speed;
430 else
431 velocity->speed = 0.0;
432
433 if (fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION)
434 velocity->direction = direction;
435 else
436 velocity->direction = 0.0;
437
438 if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB)
439 velocity->climb = climb;
440 else
441 velocity->climb = 0.0;
442
443 return EINA_TRUE;
444}
445
446static void
447velocity_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
448{
449 const char *err, *errmsg;
450
451 if (eldbus_message_error_get(reply, &err, &errmsg))
452 {
453 WARN("Warning: %s %s", err, errmsg);
454 return;
455 }
456
457 if (!unmarshall_velocity(reply))
458 {
459 ERR("Error: Unable to unmarshall velocity");
460 return;
461 }
462
463 /* Send out an event to all interested parties that we have an update */
464 ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL);
465}
466
467static void
468velocity_signal_cb(void *data, const Eldbus_Message *reply)
469{
470 if (!unmarshall_velocity(reply))
471 {
472 ERR("Error: Unable to unmarshall velocity");
473 return;
474 }
475
476 /* Send out an event to all interested parties that we have an update */
477 ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL);
478}
479
480/* Unmarshall an raw NMEA message. It conatins a raw NMEA sentence which we can
481 * pass on to applications that want to use their own NMEA parser. This is not
482 * reommended. Better use the other interfaces to access the needed data.
483 *
484 * This is currently a Tizen only interface and not in GeoClue upstream.
485 */
486static Eina_Bool
487unmarshall_nmea(const Eldbus_Message *reply)
488{
489 int32_t timestamp = 0;
490
491 if (!eldbus_message_arguments_get(reply, "is", &timestamp, &nmea_sentence))
492 return EINA_FALSE;
493
494 return EINA_TRUE;
495}
496
497static void
498nmea_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
499{
500 const char *err, *errmsg;
501
502 if (eldbus_message_error_get(reply, &err, &errmsg))
503 {
504 WARN("Warning: %s %s", err, errmsg);
505 return;
506 }
507
508 if (!unmarshall_nmea(reply))
509 {
510 ERR("Error: Unable to unmarshall nmea");
511 return;
512 }
513
514 /* Send out an event to all interested parties that we have an update */
515 ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL);
516}
517
518static void
519nmea_signal_cb(void *data, const Eldbus_Message *reply)
520{
521 if (!unmarshall_nmea(reply))
522 {
523 ERR("Error: Unable to unmarshall nmea");
524 return;
525 }
526
527 ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL);
528}
529
530/* Unmarshall a satellite information message. This offers GPS specific
531 * information about the used satellites and its properties. It can be used for
532 * applications that rely on GPS and want to show more information like a 3D fix
533 * or used satellites.
534 *
535 * This is currently a Tizen only interface and not available in GeoClue upstream.
536 */
537static Eina_Bool
538unmarshall_satellite(const Eldbus_Message *reply)
539{
540 int32_t timestamp = 0, satellite_used = 0, satellite_visible = 0;
541 int32_t snr = 0, elevation = 0, azimuth = 0, prn = 0, used_prn = 0;
542 Eldbus_Message_Iter *sub_prn, *sub_info, *struct_info;
543
544 if (!eldbus_message_arguments_get(reply, "iiiaia(iiii)", &timestamp, &satellite_used,
545 &satellite_visible, &sub_prn, &sub_info))
546 return EINA_FALSE;
547
548 while (eldbus_message_iter_get_and_next(sub_prn, 'i', &used_prn))
549 {
550 DBG("Satellite used PRN %i", used_prn);
551 }
552
553 /* TODO re-check that the parameter ordering is what we expect */
554 while (eldbus_message_iter_get_and_next(sub_info, 'r', &struct_info))
555 {
556 eldbus_message_iter_arguments_get(struct_info, "iiii", &prn, &elevation, &azimuth, &snr);
557 DBG("Satellite info %i, %i, %i, %i", prn, elevation, azimuth, snr);
558 }
559
560 return EINA_TRUE;
561}
562
563static void
564satellite_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
565{
566 const char *err, *errmsg;
567
568 if (eldbus_message_error_get(reply, &err, &errmsg))
569 {
570 WARN("Warning: %s %s", err, errmsg);
571 return;
572 }
573
574 if (!unmarshall_satellite(reply))
575 {
576 ERR("Error: Unable to unmarshall satellite");
577 return;
578 }
579
580 /* Send out an event to all interested parties that we have an update */
581 ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
582}
583
584static void
585last_satellite_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
586{
587 const char *err, *errmsg;
588
589 if (eldbus_message_error_get(reply, &err, &errmsg))
590 {
591 WARN("Warning: %s %s", err, errmsg);
592 return;
593 }
594
595 if (!unmarshall_satellite(reply))
596 {
597 ERR("Error: Unable to unmarshall last satellite");
598 return;
599 }
600
601 /* Send out an event to all interested parties that we have an update */
602 ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
603}
604
605static void
606satellite_signal_cb(void *data, const Eldbus_Message *reply)
607{
608 if (!unmarshall_satellite(reply))
609 {
610 ERR("Error: Unable to unmarshall satellite");
611 return;
612 }
613
614 /* Send out an event to all interested parties that we have an update */
615 ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
616}
617
618/* Unmarshall position coordination message */
619static Eina_Bool
620unmarshall_position(const Eldbus_Message *reply)
621{
622 GeocluePositionFields fields;
623 int32_t level, timestamp;
624 double horizontal = 0.0;
625 double vertical = 0.0;
626 double latitude = 0.0;
627 double longitude = 0.0;
628 double altitude = 0.0;
629 Eldbus_Message_Iter *sub;
630
631 if (!eldbus_message_arguments_get(reply, "iiddd(idd)", &fields, &timestamp,
632 &latitude, &longitude, &altitude, &sub))
633 return EINA_FALSE;
634
635 if (!eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical))
636 return EINA_FALSE;
637
638 position->timestamp = timestamp;
639
640 /* GeoClue uses some flags to mark position fields as valid. We set invalid
641 * fields to 0.0 */
642 if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
643 position->latitude = latitude;
644 else
645 position->latitude = 0.0;
646
647 if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
648 position->longitude = longitude;
649 else
650 position->longitude = 0.0;
651
652 if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
653 position->altitude = altitude;
654 else
655 position->altitude = 0.0;
656
657 position->accur->level = level;
658 position->accur->horizontal = horizontal;
659 position->accur->vertical = vertical;
660
661 return EINA_TRUE;
662}
663
664static void
665position_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
666{
667 const char *err, *errmsg;
668
669 if (eldbus_message_error_get(reply, &err, &errmsg))
670 {
671 ERR("Error: %s %s", err, errmsg);
672 return;
673 }
674
675 if (!unmarshall_position(reply))
676 {
677 ERR("Error: Unable to unmarshall position");
678 return;
679 }
680
681 /* Send out an event to all interested parties that we have an update */
682 ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL);
683}
684
685static void
686position_signal_cb(void *data, const Eldbus_Message *reply)
687{
688 if (!unmarshall_position(reply))
689 {
690 ERR("Error: Unable to unmarshall position");
691 return;
692 }
693
694 /* Send out an event to all interested parties that we have an update */
695 ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL);
696}
697
698static Eina_Bool
699geoclue_start(void *data, int ev_type, void *event)
700{
701 DBG("GeoClue start event at %s", unique_name);
702 return ECORE_CALLBACK_DONE;
703}
704
705static Eina_Bool
706geoclue_stop(void *data, int ev_type, void *event)
707{
708 DBG("GeoClue stop event");
709 return ECORE_CALLBACK_DONE;
710}
711
712static void
713_reference_add_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
714{
715 const char *err, *errmsg;
716
717 if (eldbus_message_error_get(reply, &err, &errmsg))
718 {
719 ERR("Error: %s %s", err, errmsg);
720 return;
721 }
722
723 DBG("Reference added");
724}
725
726static void
727_reference_del_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
728{
729 /* Dummy callback. We are not waiting for any reply here on shutdown */
730}
731
732static void
733status_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
734{
735 const char *err, *errmsg;
736
737 if (eldbus_message_error_get(reply, &err, &errmsg))
738 {
739 ERR("Error: %s %s", err, errmsg);
740 return;
741 }
742
743 /* We need this to be malloced to be passed to ecore_event_add. Or provide a dummy free callback. */
744 status = malloc(sizeof(*status));
745
746 if (!eldbus_message_arguments_get(reply,"i", status))
747 {
748 ERR("Error: Unable to unmarshall status");
749 return;
750 }
751
752 address_provider->status = position_provider->status = *status;
753 /* Send out an event to all interested parties that we have an update */
754 ecore_event_add(ELOCATION_EVENT_STATUS, status, NULL, NULL);
755}
756
757static void
758status_signal_cb(void *data, const Eldbus_Message *reply)
759{
760 /* We need this to be malloced to be passed to ecore_event_add. Or provide a dummy free callback. */
761 status = malloc(sizeof(*status));
762
763 if (!eldbus_message_arguments_get(reply,"i", status))
764 {
765 ERR("Error: Unable to unmarshall status");
766 return;
767 }
768
769 address_provider->status = position_provider->status = *status;
770 /* Send out an event to all interested parties that we have an update */
771 ecore_event_add(ELOCATION_EVENT_STATUS, status, NULL, NULL);
772}
773
774static void
775_dummy_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
776{
777}
778
779/* We got notified from GeoClue that the meta-provider we asked for is now
780 * ready. That means we can finish up our initialization and set up all
781 * callbacks and handling for the interfaces we use on the meta-provider.
782 *
783 * We also call the interfaces to get an initial set of data that we can provide
784 * to eager aplications.
785 */
786static void
787create_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
788{
789 const char *object_path;
790 Eina_Bool updates;
791 int accur_level, min_time, resources;
792 const char *err, *errmsg;
793
794 if (eldbus_message_error_get(reply, &err, &errmsg))
795 {
796 ERR("Error: %s %s", err, errmsg);
797 return;
798 }
799
800 if (!eldbus_message_arguments_get(reply, "o", &object_path)) return;
801
802 DBG("Object path for client: %s", object_path);
803
804 /* With the created object path we now have a meta provider we can operate on.
805 * Geoclue handles the selection of the best provider internally for the meta
806 * provider */
807 obj_meta = eldbus_object_get(conn, GEOCLUE_DBUS_NAME, object_path);
808 if (!obj_meta)
809 {
810 ERR("Error: could not get object for client");
811 return;
812 }
813
814 meta_geoclue = eldbus_proxy_get(obj_meta, GEOCLUE_GEOCLUE_IFACE);
815 if (!meta_geoclue)
816 {
817 ERR("Error: could not get proxy for geoclue");
818 return;
819 }
820
821 meta_address = eldbus_proxy_get(obj_meta, GEOCLUE_ADDRESS_IFACE);
822 if (!meta_address)
823 {
824 ERR("Error: could not get proxy address");
825 return;
826 }
827
828 meta_position = eldbus_proxy_get(obj_meta, GEOCLUE_POSITION_IFACE);
829 if (!meta_position)
830 {
831 ERR("Error: could not get proxy for position");
832 return;
833 }
834
835 meta_masterclient = eldbus_proxy_get(obj_meta, GEOCLUE_MASTERCLIENT_IFACE);
836 if (!meta_masterclient)
837 {
838 ERR("Error: could not get proxy for master client");
839 return;
840 }
841
842 meta_velocity = eldbus_proxy_get(obj_meta, GEOCLUE_VELOCITY_IFACE);
843 if (!meta_velocity)
844 {
845 ERR("Error: could not get proxy for velocity");
846 return;
847 }
848
849 meta_nmea = eldbus_proxy_get(obj_meta, GEOCLUE_NMEA_IFACE);
850 if (!meta_nmea)
851 {
852 ERR("Error: could not get proxy for nmea");
853 return;
854 }
855
856 meta_satellite = eldbus_proxy_get(obj_meta, GEOCLUE_SATELLITE_IFACE);
857 if (!meta_satellite)
858 {
859 ERR("Error: could not get proxy for satellite");
860 return;
861 }
862
863 /* Send Geoclue a set of requirements we have for the provider and start the address and position
864 * meta provider afterwards. After this we should be ready for operation. */
865 updates = EINA_FALSE; /* Especially the web providers do not offer updates */
866 accur_level = ELOCATION_ACCURACY_LEVEL_COUNTRY;
867 min_time = 0; /* Minimal times between updates (no implemented yet) */
868 resources = ELOCATION_RESOURCE_ALL;
869
870 eldbus_proxy_signal_handler_add(meta_masterclient, "AddressProviderChanged",
871 meta_address_provider_info_signal_cb, address_provider);
872 eldbus_proxy_signal_handler_add(meta_masterclient, "PositionProviderChanged",
873 meta_position_provider_info_signal_cb, position_provider);
874
875 if (!eldbus_proxy_call(meta_masterclient, "SetRequirements", _dummy_cb, NULL, -1, "iibi",
876 accur_level, min_time, updates, resources))
877 {
878 ERR("Error: could not call SetRequirements");
879 return;
880 }
881
882 if (!eldbus_proxy_call(meta_masterclient, "AddressStart", _dummy_cb, NULL, -1, ""))
883 {
884 ERR("Error: could not call AddressStart");
885 return;
886 }
887
888 if (!eldbus_proxy_call(meta_masterclient, "PositionStart", _dummy_cb, NULL, -1, ""))
889 {
890 ERR("Error: could not call PositionStart");
891 return;
892 }
893
894 if (!eldbus_proxy_call(meta_geoclue, "AddReference", _reference_add_cb, NULL, -1, ""))
895 {
896 ERR("Error: could not call AddReference");
897 return;
898 }
899
900 if (!eldbus_proxy_call(meta_address, "GetAddress", address_cb, NULL, -1, ""))
901 {
902 ERR("Error: could not call GetAddress");
903 return;
904 }
905
906 if (!eldbus_proxy_call(meta_position, "GetPosition", position_cb, NULL, -1, ""))
907 {
908 ERR("Error: could not call GetPosition");
909 return;
910 }
911
912 if (!eldbus_proxy_call(meta_geoclue, "GetStatus", status_cb, NULL, -1, ""))
913 {
914 ERR("Error: could not call GetStatus");
915 return;
916 }
917
918 if (!eldbus_proxy_call(meta_velocity, "GetVelocity", velocity_cb, NULL, -1, ""))
919 {
920 ERR("Error: could not call GetVelocity");
921 return;
922 }
923
924 if (!eldbus_proxy_call(meta_nmea, "GetNmea", nmea_cb, NULL, -1, ""))
925 {
926 ERR("Error: could not call GetNmea");
927 return;
928 }
929
930 if (!eldbus_proxy_call(meta_satellite, "GetSatellite", satellite_cb, NULL, -1, ""))
931 {
932 ERR("Error: could not call GetSatellite");
933 return;
934 }
935
936 if (!eldbus_proxy_call(meta_satellite, "GetLastSatellite", last_satellite_cb, NULL, -1, ""))
937 {
938 ERR("Error: could not call GetLastSatellite");
939 return;
940 }
941
942 if (!eldbus_proxy_call(meta_masterclient, "GetAddressProvider", meta_address_provider_info_cb,
943 address_provider, -1, ""))
944 {
945 ERR("Error: could not call GetAddressProvider");
946 return;
947 }
948
949 if (!eldbus_proxy_call(meta_masterclient, "GetPositionProvider", meta_position_provider_info_cb,
950 position_provider, -1, ""))
951 {
952 ERR("Error: could not call GetPositionProvider");
953 return;
954 }
955
956 eldbus_proxy_signal_handler_add(meta_address, "AddressChanged", address_signal_cb, NULL);
957 eldbus_proxy_signal_handler_add(meta_position, "PositionChanged", position_signal_cb, NULL);
958 eldbus_proxy_signal_handler_add(meta_geoclue, "StatusChanged", status_signal_cb, NULL);
959 eldbus_proxy_signal_handler_add(meta_velocity, "VelocityChanged", velocity_signal_cb, NULL);
960 eldbus_proxy_signal_handler_add(meta_nmea, "NmeaChanged", nmea_signal_cb, NULL);
961 eldbus_proxy_signal_handler_add(meta_satellite, "SatelliteChanged", satellite_signal_cb, NULL);
962
963 ecore_event_add(ELOCATION_EVENT_META_READY, NULL, NULL, NULL);
964}
965
966static void
967_name_owner_changed(void *data, const char *bus, const char *old, const char *new)
968{
969 if (old[0] == '\0' && new[0] != '\0')
970 {
971 ecore_event_add(ELOCATION_EVENT_IN, NULL, NULL, NULL);
972 unique_name = strdup(new);
973 }
974 else if (old[0] != '\0' && new[0] == '\0')
975 {
976 if (strcmp(unique_name, old) != 0)
977 WARN("%s was not the known name %s, ignored.", old, unique_name);
978 else
979 ecore_event_add(ELOCATION_EVENT_OUT, NULL, NULL, NULL);
980 }
981 else
982 {
983 DBG("unknow change from %s to %s", old, new);
984 }
985}
986
987/* Public API function to request a landmarks position based on an address object */
988EAPI Eina_Bool
989elocation_landmarks_get(Elocation_Position *position_shadow, Elocation_Address *address_shadow)
990{
991 Eldbus_Message *msg;
992 Eldbus_Message_Iter *iter;
993 const char *keyword = NULL, *lang = NULL, *country_code = NULL;
994 int limit = 0;
995 double left= 0.0, top = 0.0, right = 0.0, bottom = 0.0;
996
997 msg = eldbus_proxy_method_call_new(master_poi, "SearchByPosition");
998 iter = eldbus_message_iter_get(msg);
999 eldbus_message_iter_basic_append(iter, 's', keyword);
1000 eldbus_message_iter_basic_append(iter, 's', lang);
1001 eldbus_message_iter_basic_append(iter, 's', country_code);
1002 eldbus_message_iter_basic_append(iter, 'i', limit);
1003 eldbus_message_iter_basic_append(iter, 'd', left);
1004 eldbus_message_iter_basic_append(iter, 'd', top);
1005 eldbus_message_iter_basic_append(iter, 'd', right);
1006 eldbus_message_iter_basic_append(iter, 'd', bottom);
1007 if (!eldbus_proxy_send(master_poi, msg, poi_cb, NULL, -1))
1008 {
1009 ERR("Error: could not call SearchByPosition");
1010 eldbus_message_unref(msg);
1011 return EINA_FALSE;
1012 }
1013
1014 return EINA_TRUE;
1015}
1016
1017/* Public API function to get an address from a position */
1018EAPI Eina_Bool
1019elocation_position_to_address(Elocation_Position *position_shadow, Elocation_Address *address_shadow)
1020{
1021 Eldbus_Message *msg;
1022 Eldbus_Message_Iter *iter, *structure;
1023
1024 msg = eldbus_proxy_method_call_new(geonames_rgeocode, "PositionToAddress");
1025 iter = eldbus_message_iter_get(msg);
1026 eldbus_message_iter_basic_append(iter, 'd', position_shadow->latitude);
1027 eldbus_message_iter_basic_append(iter, 'd', position_shadow->longitude);
1028 structure = eldbus_message_iter_container_new(iter, 'r', NULL);
1029 eldbus_message_iter_basic_append(structure, 'i', position_shadow->accur->level);
1030 eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->horizontal);
1031 eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->vertical);
1032 eldbus_message_iter_container_close(iter, structure);
1033 if (!eldbus_proxy_send(geonames_rgeocode, msg, rgeocode_cb, NULL, -1))
1034 {
1035 ERR("Error: could not call PositionToAddress");
1036 eldbus_message_unref(msg);
1037 return EINA_FALSE;
1038 }
1039
1040 return EINA_TRUE;
1041}
1042
1043/* Public API function to get a position from and address */
1044EAPI Eina_Bool
1045elocation_address_to_position(Elocation_Address *address_shadow, Elocation_Position *position_shadow)
1046{
1047 Eldbus_Message *msg;
1048 Eldbus_Message_Iter *iter, *array;
1049
1050 /* In function macro to generate a key value pair structure for the dict */
1051 #define ENTRY(key) { #key, address_shadow->key }
1052 struct keyval {
1053 const char *key;
1054 const char *val;
1055 } keyval[] = {
1056 ENTRY(country),
1057 ENTRY(countrycode),
1058 ENTRY(locality),
1059 ENTRY(postalcode),
1060 ENTRY(region),
1061 ENTRY(timezone),
1062 { NULL, NULL }
1063 };
1064 #undef ENTRY
1065
1066 struct keyval *k;
1067
1068 msg = eldbus_proxy_method_call_new(geonames_geocode, "AddressToPosition");
1069 iter = eldbus_message_iter_get(msg);
1070
1071 array = eldbus_message_iter_container_new(iter, 'a', "{ss}");
1072
1073 for (k = keyval; k && k->key; k++)
1074 {
1075 Eldbus_Message_Iter *entry;
1076
1077 if (!k->val) continue;
1078
1079 entry = eldbus_message_iter_container_new(array, 'e', NULL);
1080 eldbus_message_iter_arguments_append(entry, "ss", k->key, k->val);
1081 eldbus_message_iter_container_close(array, entry);
1082 }
1083
1084 eldbus_message_iter_container_close(iter, array);
1085
1086 if (!eldbus_proxy_send(geonames_geocode, msg, geocode_cb, NULL, -1))
1087 {
1088 ERR("Error: could not call AddressToPosition");
1089 eldbus_message_unref(msg);
1090 return EINA_FALSE;
1091 }
1092
1093 return EINA_TRUE;
1094}
1095
1096/* Public API function to get the position from a freeform text input style
1097 * address
1098 */
1099EAPI Eina_Bool
1100elocation_freeform_address_to_position(const char *freeform_address, Elocation_Position *position_shadow)
1101{
1102 if (!eldbus_proxy_call(geonames_geocode, "FreeformAddressToPosition", geocode_cb, NULL, -1, "s", freeform_address))
1103 {
1104 ERR("Error: could not call FreeformAddressToPosition");
1105 return EINA_FALSE;
1106 }
1107 return EINA_TRUE;
1108}
1109
1110/* Public API function to request the current address */
1111EAPI Eina_Bool
1112elocation_address_get(Elocation_Address *address_shadow)
1113{
1114 if (!address) return EINA_FALSE;
1115
1116 address_shadow = address;
1117 return EINA_TRUE;
1118}
1119
1120/* Public API function to request the current position */
1121EAPI Eina_Bool
1122elocation_position_get(Elocation_Position *position_shadow)
1123{
1124 if (!position) return EINA_FALSE;
1125
1126 position_shadow = position;
1127 return EINA_TRUE;
1128}
1129
1130/* Public API function to request the status */
1131EAPI Eina_Bool
1132elocation_status_get(int *status_shadow)
1133{
1134 if (status < 0) return EINA_FALSE;
1135
1136 status_shadow = status;
1137 return EINA_TRUE;
1138}
1139
1140/* Public API function to create a new position object */
1141EAPI Elocation_Position *
1142elocation_position_new(void)
1143{
1144 /* Malloc the global struct we operate on here in this lib. This shadows the
1145 * updated data we are giving to the application */
1146 position = calloc(1, sizeof(Elocation_Position));
1147 if (!position) return NULL;
1148
1149 position->accur = calloc(1, sizeof(Elocation_Accuracy));
1150 if (!position->accur) return NULL;
1151
1152 return position;
1153}
1154
1155/* Public API function to create an new address object */
1156EAPI Elocation_Address *
1157elocation_address_new(void)
1158{
1159 /* Malloc the global struct we operate on here in this lib. This shadows the
1160 * updated data we are giving to the application */
1161 address = calloc(1, sizeof(Elocation_Address));
1162 if (!address) return NULL;
1163
1164 address->accur = calloc(1, sizeof(Elocation_Accuracy));
1165 if (!address->accur) return NULL;
1166
1167 return address;
1168}
1169
1170/* Public API function to free an position object */
1171EAPI void
1172elocation_position_free(Elocation_Position *position_shadow)
1173{
1174 if (position != position_shadow)
1175 {
1176 ERR("Corrupted position object");
1177 return;
1178 }
1179
1180 free(position->accur);
1181 free(position);
1182}
1183
1184/* Public API function to free an address object */
1185EAPI void
1186elocation_address_free(Elocation_Address *address_shadow)
1187{
1188 if (address != address_shadow)
1189 {
1190 ERR("Corrupted address object");
1191 return;
1192 }
1193
1194 if (address)
1195 {
1196 free(address->country);
1197 free(address->countrycode);
1198 free(address->locality);
1199 free(address->postalcode);
1200 free(address->region);
1201 free(address->timezone);
1202 free(address->accur);
1203 free(address);
1204 }
1205}
1206
1207/* Public API funtion to initialize the elocation library */
1208EAPI Eina_Bool
1209elocation_init(void)
1210{
1211 Eldbus_Object *obj_master = NULL;
1212 Eldbus_Object *obj_geonames = NULL;
1213
1214 if (!eina_init()) return EINA_FALSE;
1215 if (!ecore_init()) return EINA_FALSE;
1216 if (!eldbus_init()) return EINA_FALSE;
1217
1218 _elocation_log_dom = eina_log_domain_register("elocation", EINA_COLOR_BLUE);
1219 if (_elocation_log_dom < 0)
1220 {
1221 EINA_LOG_ERR("Could not register 'elocation' log domain.");
1222 }
1223
1224 /* Create objects, one for each kind, we operate on internally */
1225 address_provider = calloc(1, sizeof(Elocation_Provider));
1226 position_provider = calloc(1, sizeof(Elocation_Provider));
1227
1228 addr_geocode = calloc(1, sizeof(Elocation_Address));
1229 if (!addr_geocode) return EINA_FALSE;
1230
1231 addr_geocode->accur = calloc(1, sizeof(Elocation_Accuracy));
1232 if (!addr_geocode->accur) return EINA_FALSE;
1233
1234 pos_geocode = calloc(1, sizeof(Elocation_Position));
1235 if (!pos_geocode) return EINA_FALSE;
1236
1237 pos_geocode->accur = calloc(1, sizeof(Elocation_Accuracy));
1238 if (!pos_geocode->accur) return EINA_FALSE;
1239
1240 conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
1241 if (!conn)
1242 {
1243 ERR("Error: could not connect to session bus.");
1244 return EXIT_FAILURE;
1245 }
1246
1247 /* Create all ecore event types we send out to interested applications */
1248 if (ELOCATION_EVENT_IN == 0)
1249 ELOCATION_EVENT_IN = ecore_event_type_new();
1250
1251 if (ELOCATION_EVENT_OUT == 0)
1252 ELOCATION_EVENT_OUT = ecore_event_type_new();
1253
1254 if (ELOCATION_EVENT_STATUS == 0)
1255 ELOCATION_EVENT_STATUS = ecore_event_type_new();
1256
1257 if (ELOCATION_EVENT_POSITION == 0)
1258 ELOCATION_EVENT_POSITION = ecore_event_type_new();
1259
1260 if (ELOCATION_EVENT_ADDRESS == 0)
1261 ELOCATION_EVENT_ADDRESS = ecore_event_type_new();
1262
1263 if (ELOCATION_EVENT_VELOCITY == 0)
1264 ELOCATION_EVENT_VELOCITY = ecore_event_type_new();
1265
1266 if (ELOCATION_EVENT_GEOCODE == 0)
1267 ELOCATION_EVENT_GEOCODE = ecore_event_type_new();
1268
1269 if (ELOCATION_EVENT_REVERSEGEOCODE == 0)
1270 ELOCATION_EVENT_REVERSEGEOCODE = ecore_event_type_new();
1271
1272 if (ELOCATION_EVENT_NMEA == 0)
1273 ELOCATION_EVENT_NMEA = ecore_event_type_new();
1274
1275 if (ELOCATION_EVENT_SATELLITE == 0)
1276 ELOCATION_EVENT_SATELLITE = ecore_event_type_new();
1277
1278 if (ELOCATION_EVENT_POI == 0)
1279 ELOCATION_EVENT_POI = ecore_event_type_new();
1280
1281 if (ELOCATION_EVENT_META_READY == 0)
1282 ELOCATION_EVENT_META_READY = ecore_event_type_new();
1283
1284 obj_master= eldbus_object_get(conn, GEOCLUE_DBUS_NAME, GEOCLUE_OBJECT_PATH);
1285 if (!obj_master)
1286 {
1287 ERR("Error: could not get object");
1288 return EXIT_FAILURE;
1289 }
1290
1291 manager_master = eldbus_proxy_get(obj_master, GEOCLUE_MASTER_IFACE);
1292 if (!manager_master)
1293 {
1294 ERR("Error: could not get proxy");
1295 return EXIT_FAILURE;
1296 }
1297
1298 /* Create a meta provider for all normal use cases. This will allow GeoClue
1299 * to decide which provider is the best for us internally.
1300 * Right now we don't have the functionality in place to specifically request
1301 * a provider but we maybe need this in the future. We will try without it
1302 * for now.
1303 */
1304 if (!eldbus_proxy_call(manager_master, "Create", create_cb, NULL, -1, ""))
1305 {
1306 ERR("Error: could not call Create");
1307 return EXIT_FAILURE;
1308 }
1309
1310 master_poi = eldbus_proxy_get(obj_master, GEOCLUE_POI_IFACE);
1311 if (!master_poi)
1312 {
1313 ERR("Error: could not get proxy");
1314 return EXIT_FAILURE;
1315 }
1316
1317 /* Geocode and reverse geocode never show up as meta provider. Still we want
1318 * to be able to convert so we keep them around directly here. */
1319 obj_geonames= eldbus_object_get(conn, GEONAMES_DBUS_NAME, GEONAMES_OBJECT_PATH);
1320 if (!obj_geonames)
1321 {
1322 ERR("Error: could not get object for geonames");
1323 return EXIT_FAILURE;
1324 }
1325
1326 geonames_geocode = eldbus_proxy_get(obj_geonames, GEOCLUE_GEOCODE_IFACE);
1327 if (!geonames_geocode)
1328 {
1329 ERR("Error: could not get proxy");
1330 return EXIT_FAILURE;
1331 }
1332
1333 geonames_rgeocode = eldbus_proxy_get(obj_geonames, GEOCLUE_REVERSEGEOCODE_IFACE);
1334 if (!geonames_rgeocode)
1335 {
1336 ERR("Error: could not get proxy");
1337 return EXIT_FAILURE;
1338 }
1339
1340 eldbus_name_owner_changed_callback_add(conn, GEOCLUE_DBUS_NAME, _name_owner_changed,
1341 NULL, EINA_TRUE);
1342
1343 ecore_event_handler_add(ELOCATION_EVENT_IN, geoclue_start, NULL);
1344 ecore_event_handler_add(ELOCATION_EVENT_OUT, geoclue_stop, NULL);
1345
1346 return EINA_TRUE;
1347}
1348
1349/* Public API function to shutdown the elocation library form the application */
1350EAPI void
1351elocation_shutdown(void)
1352{
1353 /* Depending on if the create_cb was successfully received meta_geoclue is
1354 * setup or not. So we * need to check here if this is not the case
1355 */
1356 if (meta_geoclue)
1357 {
1358 /* To allow geoclue freeing unused providers we free our reference on it here */
1359 if (!eldbus_proxy_call(meta_geoclue, "RemoveReference", _reference_del_cb, NULL, -1, ""))
1360 {
1361 ERR("Error: could not call RemoveReference");
1362 }
1363 }
1364
1365 /* Quite a bit of allocated string and generic memory cleanup. This should be
1366 *less when we went away from all this global var business.
1367 */
1368 if (address_provider)
1369 {
1370 free(address_provider->name);
1371 free(address_provider->description);
1372 free(address_provider->service);
1373 free(address_provider->path);
1374 free(address_provider);
1375 }
1376
1377 if (position_provider)
1378 {
1379 free(position_provider->name);
1380 free(position_provider->description);
1381 free(position_provider->service);
1382 free(position_provider->path);
1383 free(position_provider);
1384 }
1385
1386 if (pos_geocode)
1387 {
1388 free(pos_geocode->accur);
1389 free(pos_geocode);
1390 }
1391
1392 if (addr_geocode)
1393 {
1394 free(addr_geocode->country);
1395 free(addr_geocode->countrycode);
1396 free(addr_geocode->locality);
1397 free(addr_geocode->postalcode);
1398 free(addr_geocode->region);
1399 free(addr_geocode->timezone);
1400 free(addr_geocode->accur);
1401 free(addr_geocode);
1402 }
1403
1404 /* Unreference some eldbus strcutures we now longer use. To allow eldbus to
1405 * free them internally.
1406 */
1407 if (manager_master)
1408 eldbus_proxy_unref(manager_master);
1409
1410 eldbus_name_owner_changed_callback_del(conn, GEOCLUE_DBUS_NAME, _name_owner_changed, NULL);
1411 eldbus_connection_unref(conn);
1412 eldbus_shutdown();
1413 ecore_shutdown();
1414 eina_log_domain_unregister(_elocation_log_dom);
1415 eina_shutdown();
1416}
diff --git a/src/lib/elocation/elocation_private.h b/src/lib/elocation/elocation_private.h
new file mode 100644
index 0000000000..1203cc35b1
--- /dev/null
+++ b/src/lib/elocation/elocation_private.h
@@ -0,0 +1,181 @@
1#ifndef _ELOCATION_PRIVATE_H
2#define _ELOCATION_PRIVATE_H
3
4#ifdef HAVE_CONFIG_H
5#include "config.h"
6#endif
7
8#include <stdio.h>
9
10#include <Eina.h>
11#include <Ecore.h>
12#include <Eldbus.h>
13
14#ifndef ELOCATION_COLOR_DEFAULT
15#define ELOCATION_COLOR_DEFAULT EINA_COLOR_BLUE
16#endif
17extern int _elocation_log_dom;
18#ifdef CRI
19#undef CRI
20#endif
21
22#ifdef ERR
23#undef ERR
24#endif
25#ifdef INF
26#undef INF
27#endif
28#ifdef WARN
29#undef WARN
30#endif
31#ifdef DBG
32#undef DBG
33#endif
34
35#define CRI(...) EINA_LOG_DOM_CRIT(_elocation_log_dom, __VA_ARGS__)
36#define DBG(...) EINA_LOG_DOM_DBG(_elocation_log_dom, __VA_ARGS__)
37#define INF(...) EINA_LOG_DOM_INFO(_elocation_log_dom, __VA_ARGS__)
38#define WARN(...) EINA_LOG_DOM_WARN(_elocation_log_dom, __VA_ARGS__)
39#define ERR(...) EINA_LOG_DOM_ERR(_elocation_log_dom, __VA_ARGS__)
40
41/* Provider bus names and object paths. Master is the generic one which should
42 * pick up the best one internally based on given requirements. It is also still
43 * possible to use providers directly */
44#define GEOCLUE_DBUS_NAME "org.freedesktop.Geoclue.Master"
45#define GEOCLUE_OBJECT_PATH "/org/freedesktop/Geoclue/Master"
46#define GSMLOC_DBUS_NAME "org.freedesktop.Geoclue.Providers.Gsmloc"
47#define GSMLOC_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Gsmloc"
48#define HOSTIP_DBUS_NAME "org.freedesktop.Geoclue.Providers.Hostip"
49#define HOSTIP_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Hostip"
50#define SKYHOOK_DBUS_NAME "org.freedesktop.Geoclue.Providers.Skyhook"
51#define SKYHOOK_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Skyhook"
52#define UBUNTU_DBUS_NAME "org.freedesktop.Geoclue.Providers.UbuntuGeoIP"
53#define UBUNTU_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/UbuntuGeoIP"
54#define GEONAMES_DBUS_NAME "org.freedesktop.Geoclue.Providers.Geonames"
55#define GEONAMES_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Geonames"
56#define PLAZES_DBUS_NAME "org.freedesktop.Geoclue.Providers.Plazes"
57#define PLAZES_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Plazes"
58#define YAHOO_DBUS_NAME "org.freedesktop.Geoclue.Providers.Yahoo"
59#define YAHOO_OBJECT_PATH "/org/freedesktop/Geoclue/Providers/Yahoo"
60
61/* Master interfaces to control geoclue */
62#define GEOCLUE_MASTER_IFACE "org.freedesktop.Geoclue.Master"
63#define GEOCLUE_MASTERCLIENT_IFACE "org.freedesktop.Geoclue.MasterClient"
64
65/* Provider interfaces to access location data */
66#define GEOCLUE_GEOCLUE_IFACE "org.freedesktop.Geoclue"
67#define GEOCLUE_POSITION_IFACE "org.freedesktop.Geoclue.Position"
68#define GEOCLUE_ADDRESS_IFACE "org.freedesktop.Geoclue.Address"
69#define GEOCLUE_VELOCITY_IFACE "org.freedesktop.Geoclue.Velocity"
70#define GEOCLUE_GEOCODE_IFACE "org.freedesktop.Geoclue.Geocode"
71#define GEOCLUE_REVERSEGEOCODE_IFACE "org.freedesktop.Geoclue.ReverseGeocode"
72
73/* More provider interfaces. These three are not in upstream geoclue but only
74 * in the Tizen version. Lets hope they get upstream at some point. Right now
75 * we will test at runtime if they are offered and ignore them if not */
76#define GEOCLUE_NMEA_IFACE "org.freedesktop.Geoclue.Nmea"
77#define GEOCLUE_SATELLITE_IFACE "org.freedesktop.Geoclue.Satellite"
78#define GEOCLUE_POI_IFACE "org.freedesktop.Geoclue.Poi"
79
80#define GEOCLUE_ADDRESS_KEY_AREA "area"
81#define GEOCLUE_ADDRESS_KEY_COUNTRY "country"
82#define GEOCLUE_ADDRESS_KEY_COUNTRYCODE "countrycode"
83#define GEOCLUE_ADDRESS_KEY_LOCALITY "locality"
84#define GEOCLUE_ADDRESS_KEY_POSTALCODE "postalcode"
85#define GEOCLUE_ADDRESS_KEY_REGION "region"
86#define GEOCLUE_ADDRESS_KEY_STREET "street"
87
88extern int ELOCATION_EVENT_IN;
89extern int ELOCATION_EVENT_OUT;
90
91/* Some ENUMs that we mimic from GeoClue code as we only access it over the DBus
92 * interface and share no header file for such defines.
93 */
94
95/**
96 * @ingroup Location
97 * @typedef GeocluePositionFields
98 * @since 1.8
99 *
100 * Bitmask to indicate which of the supplied positions fields are valid.
101 *
102 * @{
103 */
104typedef enum {
105 GEOCLUE_POSITION_FIELDS_NONE = 0,
106 GEOCLUE_POSITION_FIELDS_LATITUDE = 1 << 0,
107 GEOCLUE_POSITION_FIELDS_LONGITUDE = 1 << 1,
108 GEOCLUE_POSITION_FIELDS_ALTITUDE = 1 << 2
109} GeocluePositionFields;
110/**@}*/
111
112/**
113 * @ingroup Location
114 * @typedef GeoclueNetworkStatus
115 * @since 1.8
116 *
117 * Status of the network connectivity for GeoClue. Needed for all providers that
118 * access external data to determine the location. For example GeoIP or GeoCode
119 * providers.
120 *
121 * @{
122 */
123typedef enum {
124 GEOCLUE_CONNECTIVITY_UNKNOWN,
125 GEOCLUE_CONNECTIVITY_OFFLINE,
126 GEOCLUE_CONNECTIVITY_ACQUIRING,
127 GEOCLUE_CONNECTIVITY_ONLINE,
128} GeoclueNetworkStatus;
129/**@}*/
130
131/**
132 * @ingroup Location
133 * @typedef GeoclueStatus
134 * @since 1.8
135 *
136 * Status of a GeoClue provider.
137 *
138 * @{
139 */
140typedef enum {
141 GEOCLUE_STATUS_ERROR,
142 GEOCLUE_STATUS_UNAVAILABLE,
143 GEOCLUE_STATUS_ACQUIRING,
144 GEOCLUE_STATUS_AVAILABLE
145} GeoclueStatus;
146/**@}*/
147
148/**
149 * @ingroup Location
150 * @typedef GeoclueVelocityFields
151 * @since 1.8
152 *
153 * Bitmask to indicate which of the supplied velocity fields are valid.
154 *
155 * @{
156 */
157typedef enum {
158 GEOCLUE_VELOCITY_FIELDS_NONE = 0,
159 GEOCLUE_VELOCITY_FIELDS_SPEED = 1 << 0,
160 GEOCLUE_VELOCITY_FIELDS_DIRECTION = 1 << 1,
161 GEOCLUE_VELOCITY_FIELDS_CLIMB = 1 << 2
162} GeoclueVelocityFields;
163/**@}*/
164
165/**
166 * @ingroup Location
167 * @typedef Elocation_Provider
168 * @since 1.8
169 *
170 * Data structure to hold information about a GeoClue provider.
171 *
172 */
173typedef struct _Elocation_Provider
174{
175 char *name;
176 char *description;
177 char *service;
178 char *path;
179 GeoclueStatus status;
180} Elocation_Provider;
181#endif