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