Adjust coding rules
[platform/core/appfw/shortcut.git] / lib / src / shortcut_manager.c
1 /*
2  * Copyright (c) 2011 - 2015 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 */
17 #include <gio/gio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <string.h>
23
24 #include <aul.h>
25 #include <dlog.h>
26 #include <glib.h>
27 #include <db-util.h>
28
29 #include "shortcut.h"
30 #include "shortcut_private.h"
31 #include "shortcut_db.h"
32 #include "shortcut_manager.h"
33
34 #define SHORTCUT_PKGNAME_LEN 512
35 #define SHORTCUT_IS_WIDGET_SIZE(size)           (!!((size) & WIDGET_SIZE_DEFAULT))
36
37 #define PROVIDER_BUS_NAME "org.tizen.data_provider_service"
38 #define PROVIDER_OBJECT_PATH "/org/tizen/data_provider_service"
39 #define PROVIDER_SHORTCUT_INTERFACE_NAME "org.tizen.data_provider_shortcut_service"
40
41 #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
42 #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
43 #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
44
45 static GDBusConnection *_gdbus_conn = NULL;
46 static int monitor_id = 0;
47 static int provider_monitor_id = 0;
48
49 static const GDBusErrorEntry dbus_error_entries[] = {
50         {SHORTCUT_ERROR_INVALID_PARAMETER, "org.freedesktop.Shortcut.Error.INVALID_PARAMETER"},
51         {SHORTCUT_ERROR_OUT_OF_MEMORY,     "org.freedesktop.Shortcut.Error.OUT_OF_MEMORY"},
52         {SHORTCUT_ERROR_IO_ERROR,          "org.freedesktop.Shortcut.Error.IO_ERROR"},
53         {SHORTCUT_ERROR_PERMISSION_DENIED, "org.freedesktop.Shortcut.Error.PERMISSION_DENIED"},
54         {SHORTCUT_ERROR_NOT_SUPPORTED,           "org.freedesktop.Shortcut.Error.NOT_SUPPORTED"},
55         {SHORTCUT_ERROR_RESOURCE_BUSY,  "org.freedesktop.Shortcut.Error.RESOURCE_BUSY"},
56         {SHORTCUT_ERROR_NO_SPACE,         "org.freedesktop.Shortcut.Error.NO_SPACE"},
57         {SHORTCUT_ERROR_EXIST,      "org.freedesktop.Shortcut.Error.EXIST"},
58         {SHORTCUT_ERROR_FAULT, "org.freedesktop.Shortcut.Error.FAULT"},
59         {SHORTCUT_ERROR_COMM, "org.freedesktop.Shortcut.Error.COMM"},
60 };
61
62 struct result_cb_item {
63         result_internal_cb_t result_internal_cb;
64         result_cb_t result_cb;
65         void *data;
66 };
67
68 typedef struct _shortcut_cb_info {
69         int (*request_cb)(const char *appid, const char *name, int type, const char *content, const char *icon, pid_t pid, double period, int allow_duplicate, void *data);
70         void *data;
71 } shortcut_cb_info;
72
73 static shortcut_cb_info _callback_info;
74 static int _dbus_init();
75 static char *_shortcut_get_pkgname_by_pid(void);
76
77 EXPORT_API GQuark shortcut_error_quark(void)
78 {
79         static volatile gsize quark_volatile = 0;
80         g_dbus_error_register_error_domain("shortcut-error-quark",
81                         &quark_volatile,
82                         dbus_error_entries,
83                         G_N_ELEMENTS(dbus_error_entries));
84         return (GQuark) quark_volatile;
85 }
86
87 static void _add_shortcut_notify(GVariant *parameters)
88 {
89         const char *appid;
90         const char *name;
91         int type;
92         const char *content;
93         const char *icon;
94         int allow_duplicate;
95         int sender_pid;
96
97         g_variant_get(parameters, "(i&s&si&s&si)", &sender_pid, &appid, &name, &type, &content, &icon, &allow_duplicate);
98         DbgPrint("_add_shortcut_notify sender_pid: %d ", sender_pid);
99         _callback_info.request_cb(appid, name, type, content, icon, sender_pid, -1.0f, allow_duplicate, _callback_info.data);
100 }
101
102 static void _add_shortcut_widget_notify(GVariant *parameters)
103 {
104         const char *appid;
105         const char *name;
106         int type;
107         const char *content;
108         const char *icon;
109         int allow_duplicate;
110         int sender_pid;
111         double period;
112
113         g_variant_get(parameters, "(i&s&si&s&sdi)", &sender_pid, &appid, &name, &type, &content, &icon, &period, &allow_duplicate);
114         _callback_info.request_cb(appid, name, type, content, icon, sender_pid, period, allow_duplicate, _callback_info.data);
115 }
116
117 static void _handle_shortcut_notify(GDBusConnection *connection,
118                 const gchar     *sender_name,
119                 const gchar     *object_path,
120                 const gchar     *interface_name,
121                 const gchar     *signal_name,
122                 GVariant        *parameters,
123                 gpointer         user_data)
124 {
125         DbgPrint("signal_name: %s", signal_name);
126         if (g_strcmp0(signal_name, "add_shortcut_notify") == 0)
127                 _add_shortcut_notify(parameters);
128         else if (g_strcmp0(signal_name, "add_shortcut_widget_notify") == 0)
129                 _add_shortcut_widget_notify(parameters);
130 }
131
132 static int _dbus_init(void)
133 {
134         GError *error = NULL;
135
136         if (_gdbus_conn == NULL) {
137                 _gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
138
139                 if (_gdbus_conn == NULL) {
140                         if (error != NULL) {
141                                 ErrPrint("Failed to get dbus [%s]", error->message);
142                                 g_error_free(error);
143                         }
144                         return SHORTCUT_ERROR_IO_ERROR;
145                 }
146                 shortcut_error_quark();
147         }
148
149         return SHORTCUT_ERROR_NONE;
150 }
151
152 static int _dbus_signal_init()
153 {
154         int ret = SHORTCUT_ERROR_NONE;
155         int id;
156
157         if (monitor_id == 0) {
158                 DbgPrint("get dbus connection success");
159                 id = g_dbus_connection_signal_subscribe(
160                                 _gdbus_conn,
161                                 PROVIDER_BUS_NAME,
162                                 PROVIDER_SHORTCUT_INTERFACE_NAME,       /*    interface */
163                                 NULL,                                   /*    member */
164                                 PROVIDER_OBJECT_PATH,                   /*    path */
165                                 NULL,                                   /*    arg0 */
166                                 G_DBUS_SIGNAL_FLAGS_NONE,
167                                 _handle_shortcut_notify,
168                                 NULL,
169                                 NULL);
170
171                 DbgPrint("subscribe id : %d", id);
172                 if (id == 0) {
173                         ret = SHORTCUT_ERROR_IO_ERROR;
174                         ErrPrint("Failed to _register_noti_dbus_interface");
175                 } else {
176                         monitor_id = id;
177                 }
178         }
179
180         return ret;
181 }
182
183 static char *_shortcut_get_pkgname_by_pid(void)
184 {
185         char pkgname[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
186         char buf[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
187         int pid, ret;
188         int fd;
189         char *dup_pkgname;
190
191         pid = getpid();
192
193         ret = aul_app_get_pkgname_bypid(pid, pkgname, sizeof(pkgname));
194         if (ret != 0) {
195                 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
196
197                 fd = open(buf, O_RDONLY);
198                 if (fd < 0)
199                         return NULL;
200
201                 ret = read(fd, pkgname, sizeof(pkgname) - 1);
202                 close(fd);
203
204                 if (ret <= 0)
205                         return NULL;
206
207                 pkgname[ret] = '\0';
208                 /*!
209                  * \NOTE
210                  * "ret" is not able to be larger than "sizeof(pkgname) - 1",
211                  * if the system is not going wrong.
212                  */
213         } else {
214                 if (strlen(pkgname) <= 0)
215                         return NULL;
216         }
217
218         dup_pkgname = strdup(pkgname);
219         if (!dup_pkgname)
220                 ErrPrint("Heap: %d\n", errno);
221
222         return dup_pkgname;
223 }
224
225 /*
226  * implement user request
227  */
228 static int _send_sync_shortcut(GVariant *body, GDBusMessage **reply, char *cmd)
229 {
230         GError *err = NULL;
231         GDBusMessage *msg ;
232         int ret = SHORTCUT_ERROR_NONE;
233
234         msg = g_dbus_message_new_method_call(
235                         PROVIDER_BUS_NAME,
236                         PROVIDER_OBJECT_PATH,
237                         PROVIDER_SHORTCUT_INTERFACE_NAME,
238                         cmd);
239         if (!msg) {
240                 ErrPrint("Can't allocate new method call");
241                 if (body)
242                         g_variant_unref(body);
243                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
244         }
245
246         if (body != NULL)
247                 g_dbus_message_set_body(msg, body);
248
249         *reply = g_dbus_connection_send_message_with_reply_sync(
250                         _gdbus_conn,
251                         msg,
252                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
253                         -1,
254                         NULL,
255                         NULL,
256                         &err);
257
258         g_object_unref(msg);
259
260         if (!*reply) {
261                 ret = SHORTCUT_ERROR_COMM;
262                 if (err != NULL) {
263                         ErrPrint("No reply. cmd = %s,  error = %s", cmd, err->message);
264                         if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
265                                 ret = SHORTCUT_ERROR_PERMISSION_DENIED;
266                         g_error_free(err);
267                 }
268                 return ret;
269         }
270
271         if (g_dbus_message_to_gerror(*reply, &err)) {
272                 if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
273                         ret = SHORTCUT_ERROR_PERMISSION_DENIED;
274                 else
275                         ret = err->code;
276
277                 ErrPrint("_send_sync_shortcut error %s err code: %d", err->message, ret);
278                 g_error_free(err);
279                 return ret;
280         }
281         DbgPrint("_send_sync_shortcut done !!");
282         return SHORTCUT_ERROR_NONE;
283 }
284
285 static int _send_service_register()
286 {
287         GDBusMessage *reply = NULL;
288         int result;
289
290         result = _send_sync_shortcut(NULL, &reply, "shortcut_service_register");
291         ErrPrint("_send_service_register done");
292         return result;
293 }
294
295 static void _send_message_with_reply_sync_cb(GDBusConnection *connection,
296                 GAsyncResult *res,
297                 gpointer user_data)
298 {
299         int result = SHORTCUT_ERROR_NONE;
300         GError *err = NULL;
301         GDBusMessage *reply = NULL;
302         struct result_cb_item *cb_item = (struct result_cb_item *)user_data;
303
304         if (cb_item == NULL) {
305                 ErrPrint("Failed to get a callback item");
306                 return;
307         }
308
309         reply = g_dbus_connection_send_message_with_reply_finish(
310                         connection,
311                         res,
312                         &err);
313
314         if (!reply) {
315                 if (err != NULL) {
316                         ErrPrint("No reply. error = %s", err->message);
317                         g_error_free(err);
318                 }
319                 result = SHORTCUT_ERROR_COMM;
320
321         } else if (g_dbus_message_to_gerror(reply, &err)) {
322                 if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
323                         result = SHORTCUT_ERROR_PERMISSION_DENIED;
324                 else
325                         result = err->code;
326
327                 ErrPrint("_send_message_with_reply_sync_cb error %s err code: %d", err->message, result);
328                 g_error_free(err);
329         }
330
331         if (cb_item->result_internal_cb)
332                 result = cb_item->result_internal_cb(result, getpid(), cb_item->data);
333         else if (cb_item->result_cb)
334                 result = cb_item->result_cb(result, cb_item->data);
335
336         if (reply)
337                 g_object_unref(reply);
338
339         free(cb_item);
340 }
341
342 static int _send_async_noti(GVariant *body, struct result_cb_item *cb_item, char *cmd)
343 {
344         GDBusMessage *msg;
345         msg = g_dbus_message_new_method_call(
346                         PROVIDER_BUS_NAME,
347                         PROVIDER_OBJECT_PATH,
348                         PROVIDER_SHORTCUT_INTERFACE_NAME,
349                         cmd);
350         if (!msg) {
351                 ErrPrint("Can't allocate new method call");
352                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
353         }
354
355         if (body != NULL)
356                 g_dbus_message_set_body(msg, body);
357
358         g_dbus_connection_send_message_with_reply(
359                         _gdbus_conn,
360                         msg,
361                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
362                         -1,
363                         NULL,
364                         NULL,
365                         (GAsyncReadyCallback)_send_message_with_reply_sync_cb,
366                         cb_item);
367
368         g_object_unref(msg);
369
370         DbgPrint("_send_async_noti done !!");
371         return SHORTCUT_ERROR_NONE;
372 }
373
374 static void _on_name_appeared(GDBusConnection *connection,
375                 const gchar     *name,
376                 const gchar     *name_owner,
377                 gpointer         user_data)
378 {
379         DbgPrint("name appeared : %s", name);
380         _send_service_register();
381 }
382
383 static void _on_name_vanished(GDBusConnection *connection,
384                 const gchar     *name,
385                 gpointer         user_data)
386 {
387         DbgPrint("name vanished : %s", name);
388 }
389
390 EAPI int shortcut_set_request_cb(shortcut_request_cb request_cb, void *data)
391 {
392         int ret = _dbus_init();
393
394         if (ret != SHORTCUT_ERROR_NONE) {
395                 ErrPrint("Can't init dbus %d", ret);
396                 return ret;
397         }
398
399         ret = _dbus_signal_init();
400         if (ret != SHORTCUT_ERROR_NONE) {
401                 ErrPrint("Can't init dbus_signal %d", ret);
402                 return ret;
403         }
404
405         ret = _send_service_register();
406         if (ret != SHORTCUT_ERROR_NONE) {
407                 ErrPrint("Can't init ipc_monitor_register %d", ret);
408                 return ret;
409         }
410
411         if (request_cb == NULL)
412                 return SHORTCUT_ERROR_INVALID_PARAMETER;
413
414         if (provider_monitor_id == 0) {
415                 provider_monitor_id = g_bus_watch_name_on_connection(
416                                 _gdbus_conn,
417                                 PROVIDER_BUS_NAME,
418                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
419                                 _on_name_appeared,
420                                 _on_name_vanished,
421                                 NULL,
422                                 NULL);
423
424                 if (provider_monitor_id == 0) {
425                         ErrPrint("watch on name fail");
426                         return SHORTCUT_ERROR_IO_ERROR;
427                 }
428         }
429
430         _callback_info.request_cb = request_cb;
431         _callback_info.data = data;
432
433         return SHORTCUT_ERROR_NONE;
434 }
435
436 EAPI int shortcut_add_to_home(const char *name, shortcut_type type, const char *uri,
437                 const char *icon, int allow_duplicate, result_cb_t result_cb, void *data)
438 {
439         struct result_cb_item *item;
440         char *appid;
441         int ret;
442         GVariant *body;
443
444         if (ADD_TO_HOME_IS_DYNAMICBOX(type)) {
445                 ErrPrint("Invalid type used for adding a shortcut\n");
446                 return SHORTCUT_ERROR_INVALID_PARAMETER;
447         }
448
449         ret = _dbus_init();
450         if (ret != SHORTCUT_ERROR_NONE) {
451                 ErrPrint("Can't init dbus %d", ret);
452                 return ret;
453         }
454
455         appid = _shortcut_get_pkgname_by_pid();
456         item = malloc(sizeof(struct result_cb_item));
457         if (!item) {
458                 if (appid)
459                         free(appid);
460
461                 ErrPrint("Heap: %d\n", errno);
462                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
463         }
464
465         item->result_cb = result_cb;
466         item->result_internal_cb = NULL;
467         item->data = data;
468
469         if (!name)
470                 name = "";
471
472         if (!uri)
473                 uri = "";
474
475         if (!icon)
476                 icon = "";
477
478         body = g_variant_new("(ississi)", getpid(), appid, name, type, uri, icon, allow_duplicate);
479         ret = _send_async_noti(body, item, "add_shortcut");
480         if (ret != SHORTCUT_ERROR_NONE) {
481                 free(item);
482                 item = NULL;
483         }
484
485         if (appid)
486                 free(appid);
487         if (body)
488                 g_variant_unref(body);
489
490         return ret;
491 }
492
493 EAPI int shortcut_add_to_home_widget(const char *name, shortcut_widget_size_e size, const char *widget_id,
494                 const char *icon, double period, int allow_duplicate, result_cb_t result_cb, void *data)
495 {
496         struct result_cb_item *item;
497         char *appid;
498         int ret;
499         GVariant *body;
500
501         if (name == NULL) {
502                 ErrPrint("AppID is null\n");
503                 return SHORTCUT_ERROR_INVALID_PARAMETER;
504         }
505
506         if (!SHORTCUT_IS_WIDGET_SIZE(size)) {
507                 ErrPrint("Invalid type used for adding a widget\n");
508                 return SHORTCUT_ERROR_INVALID_PARAMETER;
509         }
510
511         ret = _dbus_init();
512         if (ret != SHORTCUT_ERROR_NONE) {
513                 ErrPrint("Can't init dbus %d", ret);
514                 return ret;
515         }
516
517         appid = _shortcut_get_pkgname_by_pid();
518         item = malloc(sizeof(struct result_cb_item));
519         if (!item) {
520                 if (appid)
521                         free(appid);
522
523                 ErrPrint("Heap: %d\n", errno);
524                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
525         }
526
527         item->result_cb = result_cb;
528         item->result_internal_cb = NULL;
529         item->data = data;
530
531         body = g_variant_new("(ississdi)", getpid(), widget_id, name, size, NULL, icon, period, allow_duplicate);
532         ret = _send_async_noti(body, item, "add_shortcut_widget");
533
534         if (ret != SHORTCUT_ERROR_NONE) {
535                 free(item);
536                 item = NULL;
537         }
538
539         if (appid)
540                 free(appid);
541         if (body)
542                 g_variant_unref(body);
543
544         return ret;
545 }
546
547 EAPI int add_to_home_shortcut(const char *appid, const char *name, int type, const char *content,
548                 const char *icon, int allow_duplicate, result_internal_cb_t result_cb, void *data)
549 {
550         /*Deprecated API */
551         return SHORTCUT_ERROR_NONE;
552 }
553
554 EAPI int add_to_home_dynamicbox(const char *appid, const char *name, int type, const char *content, const char *icon, double period, int allow_duplicate, result_internal_cb_t result_cb, void *data)
555 {
556         /*Deprecated API */
557         return SHORTCUT_ERROR_NONE;
558 }
559
560
561 EAPI int shortcut_get_list(const char *package_name, shortcut_list_cb list_cb, void *data)
562 {
563         GDBusMessage *reply = NULL;
564         int result;
565         int count = 0;
566         GVariant *body;
567         GVariant *reply_body;
568         GVariant *iter_body;
569         GVariantIter *iter;
570         GVariantBuilder *b;
571         shortcut_info_s shortcut;
572
573         if (list_cb == NULL)
574                 return SHORTCUT_ERROR_INVALID_PARAMETER;
575
576         result = _dbus_init();
577         if (result != SHORTCUT_ERROR_NONE) {
578                 ErrPrint("Can't init dbus %d", result);
579                 return result;
580         }
581
582         b = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
583         if (package_name)
584                 g_variant_builder_add(b, "{sv}", "package_name", g_variant_new_string(package_name));
585         body = g_variant_builder_end(b);
586         result = _send_sync_shortcut(g_variant_new("(v)", body), &reply, "get_list");
587
588         if (result == SHORTCUT_ERROR_NONE) {
589                 reply_body = g_dbus_message_get_body(reply);
590                 g_variant_get(reply_body, "(ia(v))", &count, &iter);
591                 DbgPrint("shortcut count : %d", count);
592                 while (g_variant_iter_loop(iter, "(v)", &iter_body)) {
593                         g_variant_get(iter_body, "(&s&s&s&s&s)",
594                                 &shortcut.package_name, &shortcut.icon, &shortcut.name, &shortcut.extra_key, &shortcut.extra_data);
595                         DbgPrint("call calback : %s", shortcut.package_name);
596                         list_cb(shortcut.package_name, shortcut.icon, shortcut.name, shortcut.extra_key, shortcut.extra_data, data);
597                 }
598                 g_variant_iter_free(iter);
599         }
600
601         if (reply)
602                 g_object_unref(reply);
603
604         return count;
605 }
606
607 /* End of a file */