/* * Copyright (c) 2011 - 2017 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include #include #include #include #include #include #include #include #include "shortcut.h" #include "shortcut_private.h" #include "shortcut_internal.h" #define SHORTCUT_PKGNAME_LEN 512 #define REQUEST_ID_LEN 40 #define TIMEOUT 4000 #define PROVIDER_BUS_NAME "org.tizen.data_provider_service" #define PROVIDER_OBJECT_PATH "/org/tizen/data_provider_service" #define PROVIDER_SHORTCUT_INTERFACE_NAME "org.tizen.data_provider_shortcut_service" #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" #define DBUS_PATH_DBUS "/org/freedesktop/DBus" #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" static GDBusConnection *_gdbus_conn = NULL; static int monitor_id = 0; static int provider_monitor_id = 0; typedef struct _shortcut_request_cb_info { 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); void *data; } shortcut_request_cb_info; typedef struct _shortcut_remove_cb_info { int (*remove_cb)(const char *package_name, const char *name, int sender_pid, void *user_data); void *data; } shortcut_remove_cb_info; static shortcut_request_cb_info _request_callback_info; static shortcut_remove_cb_info _remove_callback_info; static void _shortcut_send_return(int ret_val, const char *request_id) { int ret; GDBusMessage *reply = NULL; ret = _dbus_init(); if (ret != SHORTCUT_ERROR_NONE) { SHORTCUT_ERR("Can't init dbus %d", ret); return; } _send_sync_shortcut(g_variant_new("(is)", ret_val, request_id), &reply, "send_return_value"); if (reply) g_object_unref(reply); } /* LCOV_EXCL_START */ static void _add_shortcut_notify(GVariant *parameters) { int ret = SHORTCUT_ERROR_NONE; const char *appid; const char *name; int type; const char *content; const char *icon; int allow_duplicate; int sender_pid; const char *request_id; g_variant_get(parameters, "(&si&s&si&s&si)", &request_id, &sender_pid, &appid, &name, &type, &content, &icon, &allow_duplicate); SHORTCUT_DBG("_add_shortcut_notify sender pid : [%d] appid : [%s]", sender_pid, appid); if (_request_callback_info.request_cb != NULL) ret = _request_callback_info.request_cb(appid, name, type, content, icon, sender_pid, -1.0f, allow_duplicate, _request_callback_info.data); else SHORTCUT_DBG("request_cb is null."); _shortcut_send_return(ret, request_id); } /* LCOV_EXCL_STOP */ /* LCOV_EXCL_START */ static void _add_shortcut_widget_notify(GVariant *parameters) { int ret = SHORTCUT_ERROR_NONE; const char *appid; const char *name; int type; const char *content; const char *icon; int allow_duplicate; int sender_pid; double period; const char *request_id; g_variant_get(parameters, "(&si&s&si&s&sdi)", &request_id, &sender_pid, &appid, &name, &type, &content, &icon, &period, &allow_duplicate); SHORTCUT_DBG("_add_shortcut_widget_notify sender pid : [%d] appid : [%s]", sender_pid, appid); if (_request_callback_info.request_cb != NULL) ret = _request_callback_info.request_cb(appid, name, type, content, icon, sender_pid, period, allow_duplicate, _request_callback_info.data); else SHORTCUT_DBG("request_cb is null."); _shortcut_send_return(ret, request_id); } /* LCOV_EXCL_STOP */ static void _remove_shortcut_notify(GVariant *parameters) { int ret = SHORTCUT_ERROR_NONE; const char *appid; const char *name; int sender_pid; const char *request_id; g_variant_get(parameters, "(&si&s&s)", &request_id, &sender_pid, &appid, &name); SHORTCUT_DBG("_remove_shortcut_notify sender pid : [%d] appid : [%s]", sender_pid, appid); if (_remove_callback_info.remove_cb != NULL) ret = _remove_callback_info.remove_cb(appid, name, sender_pid, _remove_callback_info.data); else SHORTCUT_DBG("remove_cb is null."); _shortcut_send_return(ret, request_id); } /* LCOV_EXCL_START */ static void _handle_shortcut_notify(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { SHORTCUT_DBG("signal_name : [%s]", signal_name); if (g_strcmp0(signal_name, "add_shortcut_notify") == 0) _add_shortcut_notify(parameters); else if (g_strcmp0(signal_name, "add_shortcut_widget_notify") == 0) _add_shortcut_widget_notify(parameters); else if (g_strcmp0(signal_name, "remove_shortcut_notify") == 0) _remove_shortcut_notify(parameters); } /* LCOV_EXCL_STOP */ int _dbus_init(void) { GError *error = NULL; if (_gdbus_conn == NULL) { _gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (_gdbus_conn == NULL) { /* LCOV_EXCL_START */ if (error != NULL) { SHORTCUT_ERR("Failed to get dbus [%s]", error->message); g_error_free(error); } return SHORTCUT_ERROR_IO_ERROR; /* LCOV_EXCL_STOP */ } shortcut_error_quark(); } return SHORTCUT_ERROR_NONE; } int _dbus_signal_init(void) { int ret = SHORTCUT_ERROR_NONE; int id; if (monitor_id == 0) { id = g_dbus_connection_signal_subscribe( _gdbus_conn, PROVIDER_BUS_NAME, PROVIDER_SHORTCUT_INTERFACE_NAME, /* interface */ NULL, /* member */ PROVIDER_OBJECT_PATH, /* path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, _handle_shortcut_notify, NULL, NULL); SHORTCUT_DBG("subscribe id : %d", id); if (id == 0) { /* LCOV_EXCL_START */ ret = SHORTCUT_ERROR_IO_ERROR; SHORTCUT_ERR("Failed to _register_noti_dbus_interface"); /* LCOV_EXCL_STOP */ } else { SHORTCUT_INFO("get dbus connection success"); monitor_id = id; } } return ret; } char *_shortcut_get_pkgname_by_pid(void) { char pkgname[SHORTCUT_PKGNAME_LEN + 1] = { 0, }; char buf[SHORTCUT_PKGNAME_LEN + 1] = { 0, }; int pid, ret; int fd; char *dup_pkgname; pid = getpid(); ret = aul_app_get_pkgname_bypid(pid, pkgname, sizeof(pkgname)); if (ret != 0) { /* LCOV_EXCL_START */ snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); fd = open(buf, O_RDONLY); if (fd < 0) return NULL; ret = read(fd, pkgname, sizeof(pkgname) - 1); close(fd); if (ret <= 0) return NULL; pkgname[ret] = '\0'; /* LCOV_EXCL_STOP */ /*! * \NOTE * "ret" is not able to be larger than "sizeof(pkgname) - 1", * if the system is not going wrong. */ } else { if (strlen(pkgname) <= 0) return NULL; } dup_pkgname = strdup(pkgname); if (!dup_pkgname) SHORTCUT_ERR("Heap: %d\n", errno); /* LCOV_EXCL_LINE */ return dup_pkgname; } /* * implement user request */ int _send_sync_shortcut(GVariant *body, GDBusMessage **reply, char *cmd) { GError *err = NULL; GDBusMessage *msg; int ret = SHORTCUT_ERROR_NONE; msg = g_dbus_message_new_method_call( PROVIDER_BUS_NAME, PROVIDER_OBJECT_PATH, PROVIDER_SHORTCUT_INTERFACE_NAME, cmd); if (!msg) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Can't allocate new method call"); if (body) g_variant_unref(body); return SHORTCUT_ERROR_OUT_OF_MEMORY; /* LCOV_EXCL_STOP */ } if (body != NULL) g_dbus_message_set_body(msg, body); *reply = g_dbus_connection_send_message_with_reply_sync( _gdbus_conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, TIMEOUT, NULL, NULL, &err); g_object_unref(msg); if (!*reply) { /* LCOV_EXCL_START */ ret = SHORTCUT_ERROR_COMM; if (err != NULL) { SHORTCUT_ERR("No reply. cmd = %s, error = %s", cmd, err->message); if (err->code == G_DBUS_ERROR_ACCESS_DENIED) ret = SHORTCUT_ERROR_PERMISSION_DENIED; g_error_free(err); } return ret; /* LCOV_EXCL_STOP */ } if (g_dbus_message_to_gerror(*reply, &err)) { /* LCOV_EXCL_START */ if (err->code == G_DBUS_ERROR_ACCESS_DENIED) ret = SHORTCUT_ERROR_PERMISSION_DENIED; else ret = err->code; SHORTCUT_ERR("_send_sync_shortcut error %s err code: %d", err->message, ret); g_error_free(err); return ret; /* LCOV_EXCL_STOP */ } SHORTCUT_DBG("_send_sync_shortcut done !!"); return SHORTCUT_ERROR_NONE; } int _send_service_register(void) { GDBusMessage *reply = NULL; int result; result = _send_sync_shortcut(g_variant_new("(i)", getuid()), &reply, "shortcut_service_register"); SHORTCUT_DBG("_send_service_register done"); return result; } static void _send_message_with_reply_sync_cb(GDBusConnection *connection, GAsyncResult *res, gpointer user_data) { int result; GError *err = NULL; GDBusMessage *reply = NULL; GVariant *body; struct result_cb_item *cb_item = (struct result_cb_item *)user_data; if (cb_item == NULL) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Failed to get a callback item"); return; /* LCOV_EXCL_STOP */ } reply = g_dbus_connection_send_message_with_reply_finish( connection, res, &err); if (!reply) { /* LCOV_EXCL_START */ if (err != NULL) { SHORTCUT_ERR("No reply. error = %s", err->message); g_error_free(err); } result = SHORTCUT_ERROR_COMM; /* LCOV_EXCL_STOP */ } if (g_dbus_message_to_gerror(reply, &err)) { /* LCOV_EXCL_START */ if (err->code == G_DBUS_ERROR_ACCESS_DENIED) result = SHORTCUT_ERROR_PERMISSION_DENIED; else result = err->code; SHORTCUT_ERR("_send_message_with_reply_sync_cb error %s err code: %d", err->message, result); g_error_free(err); /* LCOV_EXCL_STOP */ } else { body = g_dbus_message_get_body(reply); g_variant_get(body, "(i)", &result); } if (cb_item->result_internal_cb) cb_item->result_internal_cb(result, getpid(), cb_item->data); /* LCOV_EXCL_LINE */ else if (cb_item->result_cb) cb_item->result_cb(result, cb_item->data); if (reply) g_object_unref(reply); free(cb_item); } int _send_async_shortcut(GVariant *body, struct result_cb_item *cb_item, char *cmd) { GDBusMessage *msg; msg = g_dbus_message_new_method_call( PROVIDER_BUS_NAME, PROVIDER_OBJECT_PATH, PROVIDER_SHORTCUT_INTERFACE_NAME, cmd); if (!msg) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Can't allocate new method call"); return SHORTCUT_ERROR_OUT_OF_MEMORY; /* LCOV_EXCL_STOP */ } if (g_variant_is_floating(body)) g_variant_ref(body); if (body != NULL) g_dbus_message_set_body(msg, body); g_dbus_connection_send_message_with_reply( _gdbus_conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, (GAsyncReadyCallback)_send_message_with_reply_sync_cb, cb_item); if (msg) g_object_unref(msg); SHORTCUT_DBG("_send_async_shortcut done !!"); return SHORTCUT_ERROR_NONE; } int _check_privilege(void) { GDBusMessage *reply = NULL; int ret = SHORTCUT_ERROR_NONE; ret = _send_sync_shortcut(NULL, &reply, "check_privilege"); if (reply) g_object_unref(reply); return ret; } /* LCOV_EXCL_START */ static void _on_name_appeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { SHORTCUT_DBG("name appeared : %s", name); _send_service_register(); } /* LCOV_EXCL_STOP */ /* LCOV_EXCL_START */ static void _on_name_vanished(GDBusConnection *connection, const gchar *name, gpointer user_data) { SHORTCUT_DBG("name vanished : %s", name); } /* LCOV_EXCL_STOP */ void _ipc_monitor_fini(void) { if (provider_monitor_id) { g_bus_unwatch_name(provider_monitor_id); provider_monitor_id = 0; } if (monitor_id) { g_dbus_connection_signal_unsubscribe(_gdbus_conn, monitor_id); monitor_id = 0; } if (_gdbus_conn) { g_object_unref(_gdbus_conn); _gdbus_conn = NULL; } } void _set_request_cb(shortcut_request_cb request_cb, void *data) { _request_callback_info.request_cb = request_cb; _request_callback_info.data = data; } void _unset_request_cb(void) { if (_remove_callback_info.remove_cb == NULL && _remove_callback_info.data == NULL) _ipc_monitor_fini(); _request_callback_info.request_cb = NULL; _request_callback_info.data = NULL; } void _set_remove_cb(shortcut_remove_cb remove_cb, void *data) { _remove_callback_info.remove_cb = remove_cb; _remove_callback_info.data = data; } void _unset_remove_cb(void) { if (_request_callback_info.request_cb == NULL && _request_callback_info.data == NULL) _ipc_monitor_fini(); _remove_callback_info.remove_cb = NULL; _remove_callback_info.data = NULL; } int _dbus_set_watch_name(void) { if (provider_monitor_id == 0) { provider_monitor_id = g_bus_watch_name_on_connection( _gdbus_conn, PROVIDER_BUS_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE, _on_name_appeared, _on_name_vanished, NULL, NULL); if (provider_monitor_id == 0) { /* LCOV_EXCL_START */ SHORTCUT_ERR("watch on name fail"); return SHORTCUT_ERROR_IO_ERROR; /* LCOV_EXCL_STOP */ } } return SHORTCUT_ERROR_NONE; } char *_make_request_id(void) { static int id = 0; char request_id[REQUEST_ID_LEN]; g_atomic_int_inc(&id); snprintf(request_id, sizeof(request_id), "%d@%d", getpid(), id); SHORTCUT_DBG("The request_id of shortcut is [%s]", request_id); return strdup(request_id); } int _ready_to_send(char **appid, char **request_id) { int ret; ret = _dbus_init(); if (ret != SHORTCUT_ERROR_NONE) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Can't init dbus %d", ret); return ret; /* LCOV_EXCL_STOP */ } ret = _check_privilege(); if (ret != SHORTCUT_ERROR_NONE) return ret; *appid = _shortcut_get_pkgname_by_pid(); if (*appid == NULL) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Can't get appid"); return SHORTCUT_ERROR_IO_ERROR; /* LCOV_EXCL_STOP */ } *request_id = _make_request_id(); if (*request_id == NULL) { SHORTCUT_ERR("Can't get request_id"); free(*appid); return SHORTCUT_ERROR_OUT_OF_MEMORY; } return SHORTCUT_ERROR_NONE; } EAPI int shortcut_add_to_home_sync(const char *name, shortcut_type type, const char *uri, const char *icon, int allow_duplicate) { int ret; char *appid; char *request_id = NULL; GVariant *body; GVariant *reply_body; GDBusMessage *reply = NULL; CHECK_SHORTCUT_FEATURE(); if (ADD_TO_HOME_IS_DYNAMICBOX(type)) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Invalid type used for adding a shortcut\n"); return SHORTCUT_ERROR_INVALID_PARAMETER; /* LCOV_EXCL_STOP */ } ret = _ready_to_send(&appid, &request_id); if (ret != SHORTCUT_ERROR_NONE) { /* LCOV_EXCL_START */ SHORTCUT_ERR("ready to send error [%d]", ret); return ret; /* LCOV_EXCL_STOP */ } if (!name) name = ""; if (!uri) uri = ""; if (!icon) icon = ""; body = g_variant_new("(sississi)", request_id, getpid(), appid, name, type, uri, icon, allow_duplicate); ret = _send_sync_shortcut(body, &reply, "add_shortcut"); if (ret == SHORTCUT_ERROR_NONE) { reply_body = g_dbus_message_get_body(reply); g_variant_get(reply_body, "(i)", &ret); } if (appid) free(appid); if (body) g_variant_unref(body); if (request_id) free(request_id); if (reply) g_object_unref(reply); SHORTCUT_DBG("result[%d]", ret); return ret; } EAPI int shortcut_add_to_home_widget_sync(const char *name, shortcut_widget_size_e size, const char *widget_id, const char *icon, double period, int allow_duplicate) { int ret; char *appid; char *request_id = NULL; GVariant *body; GVariant *reply_body; GDBusMessage *reply = NULL; CHECK_SHORTCUT_FEATURE(); if (name == NULL) { SHORTCUT_ERR("AppID is null\n"); return SHORTCUT_ERROR_INVALID_PARAMETER; } if (!SHORTCUT_IS_WIDGET_SIZE(size)) { /* LCOV_EXCL_START */ SHORTCUT_ERR("Invalid type used for adding a widget\n"); return SHORTCUT_ERROR_INVALID_PARAMETER; /* LCOV_EXCL_STOP */ } ret = _ready_to_send(&appid, &request_id); if (ret != SHORTCUT_ERROR_NONE) { /* LCOV_EXCL_START */ SHORTCUT_ERR("ready to send error [%d]", ret); return ret; /* LCOV_EXCL_STOP */ } body = g_variant_new("(sississdi)", request_id, getpid(), widget_id, name, size, NULL, icon, period, allow_duplicate); ret = _send_sync_shortcut(body, &reply, "add_shortcut_widget"); if (ret == SHORTCUT_ERROR_NONE) { reply_body = g_dbus_message_get_body(reply); g_variant_get(reply_body, "(i)", &ret); } if (appid) free(appid); if (body) g_variant_unref(body); if (request_id) free(request_id); if (reply) g_object_unref(reply); SHORTCUT_DBG("result[%d]", ret); return ret; } EAPI int shortcut_remove_from_home_sync(const char *name) { int ret; char *appid; char *request_id = NULL; GVariant *body; GVariant *reply_body; GDBusMessage *reply = NULL; CHECK_SHORTCUT_FEATURE(); if (name == NULL) { SHORTCUT_ERR("name is NULL."); return SHORTCUT_ERROR_INVALID_PARAMETER; } ret = _ready_to_send(&appid, &request_id); if (ret != SHORTCUT_ERROR_NONE) { /* LCOV_EXCL_START */ SHORTCUT_ERR("ready to send error [%d]", ret); return ret; /* LCOV_EXCL_STOP */ } body = g_variant_new("(siss)", request_id, getpid(), appid, name); ret = _send_sync_shortcut(body, &reply, "remove_shortcut"); if (ret == SHORTCUT_ERROR_NONE) { reply_body = g_dbus_message_get_body(reply); g_variant_get(reply_body, "(i)", &ret); } if (appid) free(appid); if (body) g_variant_unref(body); if (request_id) free(request_id); if (reply) g_object_unref(reply); SHORTCUT_DBG("result[%d]", ret); return ret; }