c376318c9d0e596894f9c816467d4aad081be831
[profile/ivi/node-startup-controller.git] / luc-handler / luc-handler-service.c
1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /* -
3  * Copyright (c) 2012 GENIVI.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <glib-object.h>
15 #include <gio/gio.h>
16
17 #include <common/glib-extensions.h>
18
19 #include <luc-handler/luc-handler-dbus.h>
20 #include <luc-handler/luc-handler-service.h>
21
22
23
24 /* property identifiers */
25 enum
26 {
27   PROP_0,
28   PROP_CONNECTION,
29 };
30
31
32
33 static void      luc_handler_service_finalize              (GObject               *object);
34 static void      luc_handler_service_get_property          (GObject               *object,
35                                                             guint                  prop_id,
36                                                             GValue                *value,
37                                                             GParamSpec            *pspec);
38 static void      luc_handler_service_set_property          (GObject               *object,
39                                                             guint                  prop_id,
40                                                             const GValue          *value,
41                                                             GParamSpec            *pspec);
42 static gboolean  luc_handler_service_handle_register       (LUCHandler            *interface,
43                                                             GDBusMethodInvocation *invocation,
44                                                             GVariant              *apps,
45                                                             LUCHandlerService     *service);
46 static gboolean  luc_handler_service_handle_deregister     (LUCHandler            *interface,
47                                                             GDBusMethodInvocation *invocation,
48                                                             GVariant              *apps,
49                                                             LUCHandlerService     *service);
50 static gboolean  luc_handler_service_get_last_user_context (GValue                *value,
51                                                             GVariant              *variant,
52                                                             gpointer               user_data);
53 static GVariant *luc_handler_service_set_last_user_context (const GValue          *value,
54                                                             const GVariantType    *expected_type,
55                                                             gpointer               user_data);
56
57
58
59 struct _LUCHandlerServiceClass
60 {
61   GObjectClass __parent__;
62 };
63
64 struct _LUCHandlerService
65 {
66   GObject          __parent__;
67
68   GDBusConnection *connection;
69   LUCHandler      *interface;
70   GSettings       *settings;
71 };
72
73
74
75 G_DEFINE_TYPE (LUCHandlerService, luc_handler_service, G_TYPE_OBJECT);
76
77
78
79 static void
80 luc_handler_service_class_init (LUCHandlerServiceClass *klass)
81 {
82   GObjectClass *gobject_class;
83
84   gobject_class = G_OBJECT_CLASS (klass);
85   gobject_class->finalize = luc_handler_service_finalize;
86   gobject_class->get_property = luc_handler_service_get_property;
87   gobject_class->set_property = luc_handler_service_set_property;
88
89   g_object_class_install_property (gobject_class,
90                                    PROP_CONNECTION,
91                                    g_param_spec_object ("connection",
92                                                         "connection",
93                                                         "connection",
94                                                         G_TYPE_DBUS_CONNECTION,
95                                                         G_PARAM_READWRITE |
96                                                         G_PARAM_CONSTRUCT_ONLY));
97 }
98
99
100
101 static void
102 luc_handler_service_init (LUCHandlerService *service)
103 {
104   service->interface = luc_handler_skeleton_new ();
105
106   /* create GSettings object for the LUC Handler schema */
107   service->settings = g_settings_new ("org.genivi.LUCHandler1");
108
109   /* bind the settings key to the D-Bus property so changes are
110    * automatically synchronised; we need to use a custom binding
111    * here because g_settings_bind() alone does not work with dicts */
112   g_settings_bind_with_mapping (service->settings, "last-user-context",
113                                 service->interface, "last-user-context",
114                                 G_SETTINGS_BIND_DEFAULT,
115                                 luc_handler_service_get_last_user_context,
116                                 luc_handler_service_set_last_user_context,
117                                 g_object_ref (service),
118                                 (GDestroyNotify) g_object_unref);
119
120   /* implement the Register() handler */
121   g_signal_connect (service->interface, "handle-register",
122                     G_CALLBACK (luc_handler_service_handle_register),
123                     service);
124
125   /* implement the Deregister() handler */
126   g_signal_connect (service->interface, "handle-deregister",
127                     G_CALLBACK (luc_handler_service_handle_deregister),
128                     service);
129 }
130
131
132
133 static void
134 luc_handler_service_finalize (GObject *object)
135 {
136   LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
137
138   /* release the D-Bus connection object */
139   if (service->connection != NULL)
140     g_object_unref (service->connection);
141
142   /* release the interface skeleton */
143   g_signal_handlers_disconnect_matched (service->interface,
144                                         G_SIGNAL_MATCH_DATA,
145                                         0, 0, NULL, NULL, service);
146   g_object_unref (service->interface);
147
148   /* release the settings object */
149   g_object_unref (service->settings);
150
151   (*G_OBJECT_CLASS (luc_handler_service_parent_class)->finalize) (object);
152 }
153
154
155
156 static void
157 luc_handler_service_get_property (GObject    *object,
158                                   guint       prop_id,
159                                   GValue     *value,
160                                   GParamSpec *pspec)
161 {
162   LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
163
164   switch (prop_id)
165     {
166     case PROP_CONNECTION:
167       g_value_set_object (value, service->connection);
168       break;
169     default:
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
171       break;
172     }
173 }
174
175
176
177 static void
178 luc_handler_service_set_property (GObject      *object,
179                                   guint         prop_id,
180                                   const GValue *value,
181                                   GParamSpec   *pspec)
182 {
183   LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
184
185   switch (prop_id)
186     {
187     case PROP_CONNECTION:
188       service->connection = g_value_dup_object (value);
189       break;
190     default:
191       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192       break;
193     }
194 }
195
196
197
198 static gboolean
199 luc_handler_service_handle_register (LUCHandler            *object,
200                                      GDBusMethodInvocation *invocation,
201                                      GVariant              *apps,
202                                      LUCHandlerService     *service)
203 {
204   GVariantBuilder dict_builder;
205   GHashTableIter  hiter;
206   GVariantIter    viter;
207   GHashTable     *table;
208   GPtrArray      *apps_array;
209   GVariant       *current_context;
210   GVariant       *current_apps;
211   GVariant       *new_context;
212   GVariant       *new_apps;
213   GList          *lp;
214   GList          *luc_types;
215   gchar          *app;
216   gchar          *luc_type;
217   guint           n;
218
219   g_return_val_if_fail (IS_LUC_HANDLER (object), FALSE);
220   g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
221   g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
222
223   /* create a hash table to merge the current context and the newly registered apps */
224   table = g_hash_table_new_full (g_str_hash, g_str_equal,
225                                  g_free, (GDestroyNotify) g_ptr_array_unref);
226
227   /* obtain the current content of the last user context */
228   current_context = luc_handler_get_last_user_context (service->interface);
229
230   /* prepare app lists for all LUC types present in the current context */
231   g_variant_iter_init (&viter, current_context);
232   while (g_variant_iter_loop (&viter, "{sas}", &luc_type, NULL))
233     {
234       g_hash_table_insert (table, g_strdup (luc_type),
235                            g_ptr_array_new_with_free_func (g_free));
236     }
237
238   /* add app lists for LUC types that are needed for the newly registered apps */
239   g_variant_iter_init (&viter, apps);
240   while (g_variant_iter_loop (&viter, "{sas}", &luc_type, NULL))
241     {
242       g_hash_table_insert (table, g_strdup (luc_type),
243                            g_ptr_array_new_with_free_func (g_free));
244     }
245
246   /* we now have a hash table that has all LUC types involved in the
247    * current context and in the newly registered apps */
248
249   /* fill the app lists for each LUC type involved, make sure that newly registered
250    * apps are added at the end so that they are "prioritized" */
251   g_hash_table_iter_init (&hiter, table);
252   while (g_hash_table_iter_next (&hiter, (gpointer) &luc_type, (gpointer) &apps_array))
253     {
254       /* get apps currently registered for the LUC type */
255       current_apps = g_variant_lookup_value (current_context, luc_type,
256                                              G_VARIANT_TYPE_STRING_ARRAY);
257
258       /* get apps to be registered for the LUC type now */
259       new_apps = g_variant_lookup_value (apps, luc_type, G_VARIANT_TYPE_STRING_ARRAY);
260
261       /* add all currently registered apps unless they are to be registered now.
262        * this is because we want apps to be registered now to be moved to the end
263        * of the lists */
264       for (n = 0; current_apps != NULL && n < g_variant_n_children (current_apps); n++)
265         {
266           g_variant_get_child (current_apps, n, "&s", &app);
267           if (!g_variant_string_array_has_string (new_apps, app))
268             g_ptr_array_add (apps_array, g_strdup (app));
269         }
270
271       /* add all newly registered apps at the end now */
272       for (n = 0; new_apps != NULL && n < g_variant_n_children (new_apps); n++)
273         {
274           g_variant_get_child (new_apps, n, "&s", &app);
275           g_ptr_array_add (apps_array, g_strdup (app));
276         }
277
278       /* release app lists for this LUC type */
279       if (current_apps != NULL)
280         g_variant_unref (current_apps);
281       if (new_apps != NULL)
282         g_variant_unref (new_apps);
283     }
284
285   /* construct a new dictionary variant for the new LUC */
286   g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sas}"));
287
288   /* copy LUC types and corresponding apps over to the new context. make
289    * sure the order (alphabetic) in which we add LUC types to the context
290    * dict is always the same. this is helpful for testing */
291   luc_types = g_hash_table_get_keys (table);
292   luc_types = g_list_sort (luc_types, (GCompareFunc) g_strcmp0);
293   for (lp = luc_types; lp != NULL; lp = lp->next)
294     {
295       /* get the apps list registered for this LUC type */
296       apps_array = g_hash_table_lookup (table, lp->data);
297
298       /* NULL-terminate the pointer so that we can treat it as a gchar ** */
299       g_ptr_array_add (apps_array, NULL);
300
301       /* add the LUC type and its apps to the new context */
302       g_variant_builder_add (&dict_builder, "{s^as}", lp->data, apps_array->pdata);
303     }
304
305   /* free the LUC types and our LUC type to apps mapping */
306   g_list_free (luc_types);
307   g_hash_table_unref (table);
308
309   /* apply the new last user context */
310   new_context = g_variant_builder_end (&dict_builder);
311   luc_handler_set_last_user_context (service->interface, new_context);
312
313   /* notify the caller that we have handled the register request */
314   g_dbus_method_invocation_return_value (invocation, NULL);
315   return TRUE;
316 }
317
318
319
320 static gboolean
321 luc_handler_service_handle_deregister (LUCHandler            *object,
322                                        GDBusMethodInvocation *invocation,
323                                        GVariant              *apps,
324                                        LUCHandlerService     *service)
325 {
326   g_return_val_if_fail (IS_LUC_HANDLER (object), FALSE);
327   g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
328   g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
329
330   g_debug ("Deregister called");
331
332   /* TODO read the apps parameter and update the "last-user-context" property
333    * of the skeleton */
334
335   g_dbus_method_invocation_return_value (invocation, NULL);
336
337   return TRUE;
338 }
339
340
341
342 static gboolean
343 luc_handler_service_get_last_user_context (GValue            *value,
344                                            GVariant          *variant,
345                                            gpointer           user_data)
346 {
347   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
348   g_return_val_if_fail (variant != NULL, FALSE);
349
350   g_value_set_variant (value, variant);
351   return TRUE;
352 }
353
354
355
356 static GVariant *
357 luc_handler_service_set_last_user_context (const GValue       *value,
358                                            const GVariantType *expected_type,
359                                            gpointer           user_data)
360 {
361   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
362   g_return_val_if_fail (expected_type != G_VARIANT_TYPE_DICTIONARY, FALSE);
363
364   return g_value_dup_variant (value);
365 }
366
367
368
369 LUCHandlerService *
370 luc_handler_service_new (GDBusConnection *connection)
371 {
372   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
373   return g_object_new (LUC_HANDLER_TYPE_SERVICE, "connection", connection, NULL);
374 }
375
376
377
378 gboolean
379 luc_handler_service_start (LUCHandlerService *service,
380                            GError           **error)
381 {
382   g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
383   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
384
385   /* announce the org.genivi.LUCHandler1 service on the bus */
386   return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
387                                            service->connection,
388                                            "/org/genivi/LUCHandler1",
389                                            error);
390 }