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));
102 luc_handler_service_init (LUCHandlerService *service)
104 service->interface = luc_handler_skeleton_new ();
106 /* create GSettings object for the LUC Handler schema */
107 service->settings = g_settings_new ("org.genivi.LUCHandler1");
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);
120 /* implement the Register() handler */
121 g_signal_connect (service->interface, "handle-register",
122 G_CALLBACK (luc_handler_service_handle_register),
125 /* implement the Deregister() handler */
126 g_signal_connect (service->interface, "handle-deregister",
127 G_CALLBACK (luc_handler_service_handle_deregister),
134 luc_handler_service_finalize (GObject *object)
136 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
138 /* release the D-Bus connection object */
139 if (service->connection != NULL)
140 g_object_unref (service->connection);
142 /* release the interface skeleton */
143 g_signal_handlers_disconnect_matched (service->interface,
145 0, 0, NULL, NULL, service);
146 g_object_unref (service->interface);
148 /* release the settings object */
149 g_object_unref (service->settings);
151 (*G_OBJECT_CLASS (luc_handler_service_parent_class)->finalize) (object);
157 luc_handler_service_get_property (GObject *object,
162 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
166 case PROP_CONNECTION:
167 g_value_set_object (value, service->connection);
170 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178 luc_handler_service_set_property (GObject *object,
183 LUCHandlerService *service = LUC_HANDLER_SERVICE (object);
187 case PROP_CONNECTION:
188 service->connection = g_value_dup_object (value);
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199 luc_handler_service_handle_register (LUCHandler *object,
200 GDBusMethodInvocation *invocation,
202 LUCHandlerService *service)
204 GVariantBuilder dict_builder;
205 GHashTableIter hiter;
208 GPtrArray *apps_array;
209 GVariant *current_context;
210 GVariant *current_apps;
211 GVariant *new_context;
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);
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);
227 /* obtain the current content of the last user context */
228 current_context = luc_handler_get_last_user_context (service->interface);
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))
234 g_hash_table_insert (table, g_strdup (luc_type),
235 g_ptr_array_new_with_free_func (g_free));
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))
242 g_hash_table_insert (table, g_strdup (luc_type),
243 g_ptr_array_new_with_free_func (g_free));
246 /* we now have a hash table that has all LUC types involved in the
247 * current context and in the newly registered apps */
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))
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);
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);
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
264 for (n = 0; current_apps != NULL && n < g_variant_n_children (current_apps); n++)
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));
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++)
274 g_variant_get_child (new_apps, n, "&s", &app);
275 g_ptr_array_add (apps_array, g_strdup (app));
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);
285 /* construct a new dictionary variant for the new LUC */
286 g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sas}"));
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)
295 /* get the apps list registered for this LUC type */
296 apps_array = g_hash_table_lookup (table, lp->data);
298 /* NULL-terminate the pointer so that we can treat it as a gchar ** */
299 g_ptr_array_add (apps_array, NULL);
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);
305 /* free the LUC types and our LUC type to apps mapping */
306 g_list_free (luc_types);
307 g_hash_table_unref (table);
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);
313 /* notify the caller that we have handled the register request */
314 g_dbus_method_invocation_return_value (invocation, NULL);
321 luc_handler_service_handle_deregister (LUCHandler *object,
322 GDBusMethodInvocation *invocation,
324 LUCHandlerService *service)
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);
330 g_debug ("Deregister called");
332 /* TODO read the apps parameter and update the "last-user-context" property
335 g_dbus_method_invocation_return_value (invocation, NULL);
343 luc_handler_service_get_last_user_context (GValue *value,
347 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
348 g_return_val_if_fail (variant != NULL, FALSE);
350 g_value_set_variant (value, variant);
357 luc_handler_service_set_last_user_context (const GValue *value,
358 const GVariantType *expected_type,
361 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
362 g_return_val_if_fail (expected_type != G_VARIANT_TYPE_DICTIONARY, FALSE);
364 return g_value_dup_variant (value);
370 luc_handler_service_new (GDBusConnection *connection)
372 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
373 return g_object_new (LUC_HANDLER_TYPE_SERVICE, "connection", connection, NULL);
379 luc_handler_service_start (LUCHandlerService *service,
382 g_return_val_if_fail (LUC_HANDLER_IS_SERVICE (service), FALSE);
383 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
385 /* announce the org.genivi.LUCHandler1 service on the bus */
386 return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
388 "/org/genivi/LUCHandler1",