1 /* vi:set et ai sw=2 sts=2 ts=2: */
3 * Copyright (c) 2012 GENIVI.
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/.
14 #include <glib-object.h>
17 #include <common/glib-extensions.h>
19 #include <luc-handler/luc-handler-dbus.h>
20 #include <luc-handler/luc-handler-service.h>
24 /* property identifiers */
33 static void luc_handler_service_finalize (GObject *object);
34 static void luc_handler_service_get_property (GObject *object,
38 static void luc_handler_service_set_property (GObject *object,
42 static gboolean luc_handler_service_handle_register (LUCHandler *interface,
43 GDBusMethodInvocation *invocation,
45 LUCHandlerService *service);
46 static gboolean luc_handler_service_handle_deregister (LUCHandler *interface,
47 GDBusMethodInvocation *invocation,
49 LUCHandlerService *service);
50 static gboolean luc_handler_service_get_last_user_context (GValue *value,
53 static GVariant *luc_handler_service_set_last_user_context (const GValue *value,
54 const GVariantType *expected_type,
59 struct _LUCHandlerServiceClass
61 GObjectClass __parent__;
64 struct _LUCHandlerService
68 GDBusConnection *connection;
69 LUCHandler *interface;
75 G_DEFINE_TYPE (LUCHandlerService, luc_handler_service, G_TYPE_OBJECT);
80 luc_handler_service_class_init (LUCHandlerServiceClass *klass)
82 GObjectClass *gobject_class;
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;
89 g_object_class_install_property (gobject_class,
91 g_param_spec_object ("connection",
94 G_TYPE_DBUS_CONNECTION,
96 G_PARAM_CONSTRUCT_ONLY |
97 G_PARAM_STATIC_STRINGS));
103 luc_handler_service_init (LUCHandlerService *service)
105 service->interface = luc_handler_skeleton_new ();
107 /* create GSettings object for the LUC Handler schema */
108 service->settings = g_settings_new ("org.genivi.LUCHandler1");
110 /* bind the settings key to the D-Bus property so changes are
111 * automatically synchronised; we need to use a custom binding
112 * here because g_settings_bind() alone does not work with dicts */
113 g_settings_bind_with_mapping (service->settings, "last-user-context",
114 service->interface, "last-user-context",
115 G_SETTINGS_BIND_DEFAULT,
116 luc_handler_service_get_last_user_context,
117 luc_handler_service_set_last_user_context,
118 g_object_ref (service),
119 (GDestroyNotify) g_object_unref);
121 /* implement the Register() handler */
122 g_signal_connect (service->interface, "handle-register",
123 G_CALLBACK (luc_handler_service_handle_register),
126 /* implement the Deregister() handler */
127 g_signal_connect (service->interface, "handle-deregister",
128 G_CALLBACK (luc_handler_service_handle_deregister),
135 luc_handler_service_finalize (GObject *object)
137 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
139 /* release the D-Bus connection object */
140 if (service->connection != NULL)
141 g_object_unref (service->connection);
143 /* release the interface skeleton */
144 g_signal_handlers_disconnect_matched (service->interface,
146 0, 0, NULL, NULL, service);
147 g_object_unref (service->interface);
149 /* release the settings object */
150 g_object_unref (service->settings);
152 (*G_OBJECT_CLASS (luc_handler_service_parent_class)->finalize) (object);
158 luc_handler_service_get_property (GObject *object,
163 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
167 case PROP_CONNECTION:
168 g_value_set_object (value, service->connection);
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179 luc_handler_service_set_property (GObject *object,
184 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
188 case PROP_CONNECTION:
189 service->connection = g_value_dup_object (value);
192 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200 luc_handler_service_handle_register (LUCHandler *object,
201 GDBusMethodInvocation *invocation,
203 LUCHandlerService *service)
205 GVariantBuilder dict_builder;
206 GHashTableIter hiter;
209 GPtrArray *apps_array;
210 GVariant *current_context;
211 GVariant *current_apps;
212 GVariant *new_context;
220 g_return_val_if_fail (IS_LUC_HANDLER (object), FALSE);
221 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
222 g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
224 /* create a hash table to merge the current context and the newly registered apps */
225 table = g_hash_table_new_full (g_str_hash, g_str_equal,
226 g_free, (GDestroyNotify) g_ptr_array_unref);
228 /* obtain the current content of the last user context */
229 current_context = luc_handler_get_last_user_context (service->interface);
231 /* prepare app lists for all LUC types present in the current context */
232 g_variant_iter_init (&viter, current_context);
233 while (g_variant_iter_loop (&viter, "{sas}", &luc_type, NULL))
235 g_hash_table_insert (table, g_strdup (luc_type),
236 g_ptr_array_new_with_free_func (g_free));
239 /* add app lists for LUC types that are needed for the newly registered apps */
240 g_variant_iter_init (&viter, apps);
241 while (g_variant_iter_loop (&viter, "{sas}", &luc_type, NULL))
243 g_hash_table_insert (table, g_strdup (luc_type),
244 g_ptr_array_new_with_free_func (g_free));
247 /* we now have a hash table that has all LUC types involved in the
248 * current context and in the newly registered apps */
250 /* fill the app lists for each LUC type involved, make sure that newly registered
251 * apps are added at the end so that they are "prioritized" */
252 g_hash_table_iter_init (&hiter, table);
253 while (g_hash_table_iter_next (&hiter, (gpointer) &luc_type, (gpointer) &apps_array))
255 /* get apps currently registered for the LUC type */
256 current_apps = g_variant_lookup_value (current_context, luc_type,
257 G_VARIANT_TYPE_STRING_ARRAY);
259 /* get apps to be registered for the LUC type now */
260 new_apps = g_variant_lookup_value (apps, luc_type, G_VARIANT_TYPE_STRING_ARRAY);
262 /* add all currently registered apps unless they are to be registered now.
263 * this is because we want apps to be registered now to be moved to the end
265 for (n = 0; current_apps != NULL && n < g_variant_n_children (current_apps); n++)
267 g_variant_get_child (current_apps, n, "&s", &app);
268 if (!g_variant_string_array_has_string (new_apps, app))
269 g_ptr_array_add (apps_array, g_strdup (app));
272 /* add all newly registered apps at the end now */
273 for (n = 0; new_apps != NULL && n < g_variant_n_children (new_apps); n++)
275 g_variant_get_child (new_apps, n, "&s", &app);
276 g_ptr_array_add (apps_array, g_strdup (app));
279 /* release app lists for this LUC type */
280 if (current_apps != NULL)
281 g_variant_unref (current_apps);
282 if (new_apps != NULL)
283 g_variant_unref (new_apps);
286 /* construct a new dictionary variant for the new LUC */
287 g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sas}"));
289 /* copy LUC types and corresponding apps over to the new context. make
290 * sure the order (alphabetic) in which we add LUC types to the context
291 * dict is always the same. this is helpful for testing */
292 luc_types = g_hash_table_get_keys (table);
293 luc_types = g_list_sort (luc_types, (GCompareFunc) g_strcmp0);
294 for (lp = luc_types; lp != NULL; lp = lp->next)
296 /* get the apps list registered for this LUC type */
297 apps_array = g_hash_table_lookup (table, lp->data);
299 /* NULL-terminate the pointer so that we can treat it as a gchar ** */
300 g_ptr_array_add (apps_array, NULL);
302 /* add the LUC type and its apps to the new context */
303 g_variant_builder_add (&dict_builder, "{s^as}", lp->data, apps_array->pdata);
306 /* free the LUC types and our LUC type to apps mapping */
307 g_list_free (luc_types);
308 g_hash_table_unref (table);
310 /* apply the new last user context */
311 new_context = g_variant_builder_end (&dict_builder);
312 luc_handler_set_last_user_context (service->interface, new_context);
314 /* notify the caller that we have handled the register request */
315 g_dbus_method_invocation_return_value (invocation, NULL);
322 luc_handler_service_handle_deregister (LUCHandler *object,
323 GDBusMethodInvocation *invocation,
325 LUCHandlerService *service)
327 GVariantBuilder apps_builder;
328 GVariantBuilder builder;
330 GVariant *current_context;
331 GVariant *new_context;
332 GVariant *current_apps;
333 GVariant *apps_to_remove;
339 g_return_val_if_fail (IS_LUC_HANDLER (object), FALSE);
340 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
341 g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
343 /* obtain the current content of the last user context */
344 current_context = luc_handler_get_last_user_context (service->interface);
346 /* initialise the builder for the new context */
347 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sas}"));
349 /* copy the current context into the new context, drop all apps that
350 * are supposed to be registered */
351 g_variant_iter_init (&viter, current_context);
352 while (g_variant_iter_loop (&viter, "{s@as}", &luc_type, ¤t_apps))
354 /* get a list of apps to be removed from this LUC type */
355 apps_to_remove = g_variant_lookup_value (apps, luc_type,
356 G_VARIANT_TYPE_STRING_ARRAY);
358 if (apps_to_remove == NULL)
360 /* there are no apps to be removed from this LUC type, just copy
361 * all currently registered apps over */
362 g_variant_builder_add (&builder, "{s@as}", luc_type, current_apps);
366 /* we need to remove some apps from the current LUC type, so let's
367 * build a new string array */
368 g_variant_builder_init (&apps_builder, G_VARIANT_TYPE ("as"));
370 /* add all apps currently registered for this LUC type to the string
371 * array unless they are to be removed */
372 for (n = 0, num_apps = 0;
373 current_apps != NULL && n < g_variant_n_children (current_apps);
376 g_variant_get_child (current_apps, n, "&s", &app);
377 if (!g_variant_string_array_has_string (apps_to_remove, app))
379 g_variant_builder_add (&apps_builder, "s", app);
384 /* add the LUC type and its apps to the new context unless there are none */
386 g_variant_builder_add (&builder, "{sas}", luc_type, &apps_builder);
388 /* free resources used for building the string array */
389 g_variant_builder_clear (&apps_builder);
392 /* release the apps to be removed for this LUC type */
393 if (apps_to_remove != NULL)
394 g_variant_unref (apps_to_remove);
397 /* apply the new last user context */
398 new_context = g_variant_builder_end (&builder);
399 luc_handler_set_last_user_context (service->interface, new_context);
401 /* notify the caller that we have handled the deregistration request */
402 g_dbus_method_invocation_return_value (invocation, NULL);
409 luc_handler_service_get_last_user_context (GValue *value,
413 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
414 g_return_val_if_fail (variant != NULL, FALSE);
416 g_value_set_variant (value, variant);
423 luc_handler_service_set_last_user_context (const GValue *value,
424 const GVariantType *expected_type,
427 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
428 g_return_val_if_fail (expected_type != G_VARIANT_TYPE_DICTIONARY, FALSE);
430 return g_value_dup_variant (value);
436 luc_handler_service_new (GDBusConnection *connection)
438 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
439 return g_object_new (LUC_HANDLER_TYPE_SERVICE, "connection", connection, NULL);
445 luc_handler_service_start (LUCHandlerService *service,
448 g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
449 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
451 /* announce the org.genivi.LUCHandler1 service on the bus */
452 return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
454 "/org/genivi/LUCHandler1",