Add unit test to increase coverage
[platform/core/api/system-settings.git] / src / sst_vconf.c
1 /*
2  * Copyright (c) 2011-2020 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "sst_vconf.h"
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <glib.h>
22 #include <vconf.h>
23
24 #include "sst.h"
25 #include "sst_misc.h"
26 #include "sst_interface.h"
27
28 static GHashTable *sst_vconf_map_old = NULL;
29 static GHashTable *sst_vconf_map = NULL;
30 static GHashTable *sst_vconf_map_new = NULL;
31
32 struct sst_vconf_info_s {
33         system_settings_key_e key;
34         system_settings_changed_cb cb;
35         void *cb_data;
36 };
37
38 int sst_vconf_set_int(sst_interface *iface, int val)
39 {
40         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
41         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
42
43         if (VCONF_OK != vconf_set_int(iface->vconf_key, val)) {
44                 ERR("vconf_set_int(%s, %d) Fail", iface->vconf_key, val);
45                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
46         }
47
48         return SYSTEM_SETTINGS_ERROR_NONE;
49 }
50
51 int sst_vconf_set_bool(sst_interface *iface, bool val)
52 {
53         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
54         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
55
56         if (VCONF_OK != vconf_set_bool(iface->vconf_key, val)) {
57                 ERR("vconf_set_bool(%s, %d) Fail", iface->vconf_key, val);
58                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
59         }
60
61         return SYSTEM_SETTINGS_ERROR_NONE;
62 }
63
64 int sst_vconf_set_str(sst_interface *iface, const char *val)
65 {
66         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
67         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
68
69         if (VCONF_OK != vconf_set_str(iface->vconf_key, val)) {
70                 ERR("vconf_set_str(%s, %s) Fail", iface->vconf_key, val);
71                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
72         }
73
74         return SYSTEM_SETTINGS_ERROR_NONE;
75 }
76
77 int sst_vconf_get_int(sst_interface *iface, int *val)
78 {
79         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
80         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
81
82         int result = 0;
83         if (VCONF_OK != vconf_get_int(iface->vconf_key, &result)) {
84                 ERR("vconf_get_int(%s) Fail", iface->vconf_key);
85                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
86         }
87
88         *val = result;
89
90         return SYSTEM_SETTINGS_ERROR_NONE;
91 }
92
93 int sst_vconf_get_bool(sst_interface *iface, bool *val)
94 {
95         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
96         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
97
98         int result = 0;
99         if (VCONF_OK != vconf_get_bool(iface->vconf_key, &result)) {
100                 ERR("vconf_get_bool(%s) Fail", iface->vconf_key);
101                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
102         }
103
104         *val = result ? true : false;
105
106         return SYSTEM_SETTINGS_ERROR_NONE;
107 }
108
109 int sst_vconf_get_str(sst_interface *iface, char **val)
110 {
111         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
112         RETV_IF(NULL == iface->vconf_key, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
113
114         char *result = vconf_get_str(iface->vconf_key);
115         if (NULL == result) {
116                 ERR("vconf_get_str(%s) Fail(NULL)", iface->vconf_key);
117                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
118         }
119
120         *val = result;
121         return SYSTEM_SETTINGS_ERROR_NONE;
122 }
123
124 int sst_vconf_get_str_ally(const char *vconf_key, char **value)
125 {
126         char *str_value = vconf_get_str(vconf_key);
127         if (NULL == str_value) {
128                 ERR("vconf_get_str(%s) Fail(NULL)", vconf_key);
129                 return SYSTEM_SETTINGS_ERROR_IO_ERROR;
130         }
131
132         *value = str_value;
133         return SYSTEM_SETTINGS_ERROR_NONE;
134 }
135
136 //Because of a lot of parameters, it is an inline function
137 static inline int _subscribe(sst_interface *iface, system_settings_changed_cb cb, void *user_data, GHashTable **vconf_map, GList *list, vconf_callback_fn vconf_cb, system_settings_cb_id *id)
138 {
139         GHashTable *map = *vconf_map;
140         struct sst_vconf_info_s *info = malloc(sizeof(struct sst_vconf_info_s));
141         if (NULL == info) {
142                 ERR("malloc() Fail(%d)", errno);
143                 return SYSTEM_SETTINGS_ERROR_OUT_OF_MEMORY;
144         }
145         info->key = iface->key;
146         info->cb = cb;
147         info->cb_data = user_data;
148
149         GList *new_list = g_list_append(list, info);
150         g_hash_table_replace(map, (char*)iface->vconf_key, new_list);
151
152         if (NULL == list) {
153                 int ret = vconf_notify_key_changed(iface->vconf_key, vconf_cb, NULL);
154                 if (VCONF_OK != ret) {
155                         ERR("vconf_notify_key_changed(%s) Fail", iface->vconf_key);
156                         new_list = g_list_remove(new_list, info);
157                         if (NULL == new_list) {
158                                 g_hash_table_remove(map, iface->vconf_key);
159                                 if (0 == g_hash_table_size(map)) {
160                                         g_hash_table_unref(map);
161                                         *vconf_map = NULL;
162                                 }
163                         }
164                         free(info);
165                         return SYSTEM_SETTINGS_ERROR_IO_ERROR;
166                 }
167         }
168
169         if (id)
170                 *id = info;
171
172         return SYSTEM_SETTINGS_ERROR_NONE;
173 }
174
175 static int _unsubscribe(const char *vconf_key, GList *found, GHashTable **vconf_map, vconf_callback_fn cb)
176 {
177         GHashTable *map = *vconf_map;
178         GList *list = g_list_first(found);
179
180         if (1 == g_list_length(list)) {
181                 int ret = vconf_ignore_key_changed(vconf_key, cb);
182                 if (VCONF_OK != ret) {
183                         ERR("vconf_ignore_key_changed(%s) Fail", vconf_key);
184                         return SYSTEM_SETTINGS_ERROR_IO_ERROR;
185                 }
186         }
187
188         list = g_list_remove_link(list, found);
189         if (list) {
190                 g_hash_table_replace(map, (char*)vconf_key, list);
191         } else {
192                 g_hash_table_remove(map, vconf_key);
193                 if (0 == g_hash_table_size(map)) {
194                         g_hash_table_unref(map);
195                         *vconf_map = NULL;
196                 }
197         }
198
199         free(found->data);
200         g_list_free(found);
201
202         return SYSTEM_SETTINGS_ERROR_NONE;
203 }
204
205 static void _callback_fn_old(keynode_t *node, void *user_data)
206 {
207         RET_IF(NULL == sst_vconf_map_old);
208
209         GList *list = g_hash_table_lookup(sst_vconf_map_old, node->keyname);
210         GList *it;
211         for (it = list; it; it = it->next) {
212                 struct sst_vconf_info_s *info = it->data;
213                 if (info && info->cb)
214                         info->cb(info->key, info->cb_data);
215         }
216 }
217
218 static gint _compare_key(gconstpointer a, gconstpointer b)
219 {
220         const struct sst_vconf_info_s *info = a;
221
222         RETV_IF(NULL == a, 1);
223
224         if (info->key == GPOINTER_TO_INT(b))
225                 return 0;
226         else
227                 return 1;
228 }
229
230 int sst_vconf_set_cb(sst_interface *iface, system_settings_changed_cb cb, void *user_data)
231 {
232         GList *list = NULL;
233
234         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
235         RETV_IF(NULL == cb, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
236
237         if (sst_vconf_map_old) {
238                 list = g_hash_table_lookup(sst_vconf_map_old, iface->vconf_key);
239                 GList *found = g_list_find_custom(list, GINT_TO_POINTER(iface->key), _compare_key);
240                 if (found) {
241                         ERR("callback Already Exist");
242                         return SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER;
243                 }
244         } else {
245                 sst_vconf_map_old = g_hash_table_new(g_str_hash, g_str_equal);
246         }
247
248         return _subscribe(iface, cb, user_data, &sst_vconf_map_old, list, _callback_fn_old, NULL);
249 }
250
251 int sst_vconf_unset_cb(sst_interface *iface)
252 {
253         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
254         RETV_IF(NULL == sst_vconf_map_old, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
255
256         GList *list = g_hash_table_lookup(sst_vconf_map_old, iface->vconf_key);
257         GList *found = g_list_find_custom(list, GINT_TO_POINTER(iface->key), _compare_key);
258         if (NULL == found) {
259                 ERR("No callback");
260                 return SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER;
261         }
262
263         return _unsubscribe(iface->vconf_key, found, &sst_vconf_map_old, _callback_fn_old);
264 }
265
266 static void _callback_fn(keynode_t *node, void *user_data)
267 {
268         RET_IF(NULL == sst_vconf_map);
269
270         GList *list = g_hash_table_lookup(sst_vconf_map, node->keyname);
271         GList *it;
272         for (it = list; it; it = it->next) {
273                 struct sst_vconf_info_s *info = it->data;
274                 if (info && info->cb)
275                         info->cb(info->key, info->cb_data);
276         }
277 }
278
279 static gint _compare_cb(gconstpointer a, gconstpointer b)
280 {
281         const struct sst_vconf_info_s *info = a;
282
283         RETV_IF(NULL == a, 1);
284         RETV_IF(NULL == b, 1);
285
286         if (info->cb == b)
287                 return 0;
288         else
289                 return 1;
290 }
291
292 int sst_vconf_add_multi_cb(sst_interface *iface, system_settings_changed_cb cb, void *user_data)
293 {
294         GList *list = NULL;
295
296         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
297         RETV_IF(NULL == cb, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
298
299         if (sst_vconf_map) {
300                 list = g_hash_table_lookup(sst_vconf_map, iface->vconf_key);
301                 GList *found = g_list_find_custom(list, cb, _compare_cb);
302                 if (found) {
303                         ERR("callback Already Exist");
304                         return SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER;
305                 }
306         } else {
307                 sst_vconf_map = g_hash_table_new(g_str_hash, g_str_equal);
308         }
309
310         return _subscribe(iface, cb, user_data, &sst_vconf_map, list, _callback_fn, NULL);
311 }
312
313 int sst_vconf_del_multi_cb(sst_interface *iface, system_settings_changed_cb cb)
314 {
315         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
316         RETV_IF(NULL == cb, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
317         RETV_IF(NULL == sst_vconf_map, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
318
319         GList *list = g_hash_table_lookup(sst_vconf_map, iface->vconf_key);
320         GList *found = g_list_find_custom(list, cb, _compare_cb);
321         if (NULL == found) {
322                 ERR("No callback");
323                 return SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER;
324         }
325
326         return _unsubscribe(iface->vconf_key, found, &sst_vconf_map, _callback_fn);
327 }
328
329 static void _callback_fn_new(keynode_t *node, void *user_data)
330 {
331         RET_IF(NULL == sst_vconf_map_new);
332
333         GList *list = g_hash_table_lookup(sst_vconf_map_new, node->keyname);
334         GList *it;
335         for (it = list; it; it = it->next) {
336                 struct sst_vconf_info_s *info = it->data;
337                 if (info && info->cb)
338                         info->cb(info->key, info->cb_data);
339         }
340 }
341
342 int sst_vconf_subscribe(sst_interface *iface, system_settings_changed_cb cb, void *user_data, system_settings_cb_id *id)
343 {
344         GList *list = NULL;
345
346         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
347         RETV_IF(NULL == cb, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
348
349         if (sst_vconf_map_new) {
350                 list = g_hash_table_lookup(sst_vconf_map_new, iface->vconf_key);
351         } else {
352                 sst_vconf_map_new = g_hash_table_new(g_str_hash, g_str_equal);
353         }
354
355         return _subscribe(iface, cb, user_data, &sst_vconf_map_new, list, _callback_fn_new, id);
356 }
357
358 int sst_vconf_unsubscribe(sst_interface *iface, system_settings_cb_id id)
359 {
360         RETV_IF(NULL == iface, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
361         RETV_IF(NULL == id, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
362         RETV_IF(NULL == sst_vconf_map_new, SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER);
363
364         GList *list = g_hash_table_lookup(sst_vconf_map_new, iface->vconf_key);
365         GList *found = g_list_find(list, id);
366         if (NULL == found) {
367                 ERR("No callback");
368                 return SYSTEM_SETTINGS_ERROR_INVALID_PARAMETER;
369         }
370
371         return _unsubscribe(iface->vconf_key, found, &sst_vconf_map_new, _callback_fn_new);
372 }