/* * Copyright (c) 2019 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "alarm.h" #include "alarm-internal.h" #include "alarm-manager-dbus.h" #include "alarm-manager-util.h" /* GDBus Declaration */ #define ALARM_MGR_DBUS_PATH "/org/tizen/alarm/manager" #define ALARM_MGR_DBUS_NAME "org.tizen.alarm.manager" extern __alarm_server_context_t alarm_context; extern GSList *g_expired_alarm_list; struct watch_info_t { int watch_id; alarm_id_t alarm_id; }; static GDBusNodeInfo *introspection_data; static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; static gboolean __send_noti_to_session_bus(char *service_name, alarm_id_t alarm_id, int msec, uid_t uid) { int fd; int ret; int len; struct sockaddr_un saddr; uint8_t *data; GVariant *gv; uint8_t *gv_data; fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (fd < 0) return FALSE; saddr.sun_family = AF_UNIX; snprintf(saddr.sun_path, sizeof(saddr.sun_path), "/run/alarm_agent/%d", uid); ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { LOGE("connect failed - (errno %d)", errno); close(fd); return FALSE; } gv = g_variant_new("(iis)", alarm_id, msec, service_name); if (!gv) { close(fd); return FALSE; } len = g_variant_get_size(gv); gv_data = NULL; if (len > 0) gv_data = (uint8_t *)malloc(len); if (!gv_data) { g_variant_unref(gv); close(fd); return FALSE; } g_variant_store(gv, gv_data); g_variant_unref(gv); data = (uint8_t *)malloc(len + 4); if (!data) { close(fd); free(gv_data); return FALSE; } memcpy(data, &len, 4); memcpy(data + 4, gv_data, len); free(gv_data); if (send(fd, data, len + 4, 0) == -1) { LOGE("sendto() failed (errno %d)", errno); free(data); close(fd); return FALSE; } free(data); close(fd); return TRUE; } static void __on_name_appeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { SECURE_LOGW("alarm sender name appeared : %s %s", name, name_owner); struct watch_info_t *watch_info = (struct watch_info_t *)user_data; GSList *gs_iter; __alarm_info_t *entry; alarm_id_t alarm_id = watch_info->alarm_id; g_bus_unwatch_name(watch_info->watch_id); free(watch_info); for (gs_iter = alarm_context.alarms; gs_iter != NULL; gs_iter = g_slist_next(gs_iter)) { entry = gs_iter->data; if (alarm_id == entry->alarm_id) { entry->zombie_mode = false; _alarm_disable_timer(alarm_context); _clear_scheduled_alarm_list(); _alarm_schedule(); _rtc_set(); return; } } } static void __alarm_send_noti_to_application_reply( GObject *source_object, GAsyncResult *res, gpointer user_data) { GVariant *message; GDBusConnection *conn; GError *error = NULL; alarm_id_t *reply_alarm_id = (alarm_id_t *)user_data; char *service_name = NULL; char *app_unique_name = NULL; struct watch_info_t *watch_info; char log_message[ALARMMGR_LOG_MESSAGE_SIZE] = {0,}; GSList *gs_iter; __alarm_info_t *entry; bool is_existed = false; conn = G_DBUS_CONNECTION(source_object); message = g_dbus_connection_call_finish(conn, res, &error); if (error != NULL && error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) { for (gs_iter = alarm_context.alarms; gs_iter != NULL; gs_iter = g_slist_next(gs_iter) ) { entry = gs_iter->data; if (*reply_alarm_id == entry->alarm_id) { is_existed = true; app_unique_name = entry->app_unique_name; if (entry->dst_service_name == NULL) service_name = entry->app_service_name_mod; else service_name = entry->dst_service_name_mod; break; } } LOGW("failed to send alarm expired noti [%d: %s] [id : %d, %s -> %s]", error->code, error->message, *reply_alarm_id, app_unique_name, service_name); snprintf(log_message, sizeof(log_message), "failed to send alarm expired noti [%d: %s] [id : %d, %s -> %s]", error->code, error->message, *reply_alarm_id, app_unique_name, service_name); _save_module_log("EXPIRED_FAIL", log_message); g_error_free(error); if (is_existed) { entry->zombie_mode = true; watch_info = (struct watch_info_t *)calloc(1, sizeof(struct watch_info_t)); watch_info->alarm_id = entry->alarm_id; watch_info->watch_id = g_bus_watch_name_on_connection( alarm_context.connection, service_name, G_BUS_NAME_WATCHER_FLAGS_NONE, __on_name_appeared, NULL, watch_info, NULL); alarm_context.c_due_time = -1; _alarm_disable_timer(alarm_context); _clear_scheduled_alarm_list(); _alarm_schedule(); _rtc_set(); } } else { g_variant_unref(message); } free(reply_alarm_id); } void _alarm_send_noti_to_application_by_dbus(const char *app_service_name, alarm_id_t alarm_id, int msec, uid_t uid) { char service_name[MAX_SERVICE_NAME_LEN] = {0,}; gboolean ret; alarm_id_t *reply_alarm_id; if (app_service_name == NULL || strlen(app_service_name) == 0) { LOGE("This alarm destination is invalid."); return; } if (_can_skip_expired_cb(alarm_id)) return; strncpy(service_name, app_service_name, sizeof(service_name) - 1); SECURE_LOGI("[send expired_alarm(alarm_id=%d) to app_service_name(%s)]", alarm_id, service_name); if (uid >= REGULAR_UID_MIN) { ret = __send_noti_to_session_bus(service_name, alarm_id, msec, uid); if (ret != TRUE) LOGE("failed to send alarm expired noti for %d, %s", alarm_id, service_name); } else { reply_alarm_id = (alarm_id_t *)calloc(1, sizeof(alarm_id_t)); *reply_alarm_id = alarm_id; g_dbus_connection_call(alarm_context.connection, service_name, "/org/tizen/alarm/client", "org.tizen.alarm.client", "alarm_expired", g_variant_new("(iis)", alarm_id, msec, service_name), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, __alarm_send_noti_to_application_reply, reply_alarm_id); } } static uid_t __get_caller_uid(const char *name) { guint uid; GVariant *ret; GError *error = NULL; ret = g_dbus_connection_call_sync( alarm_context.connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionUnixUser", g_variant_new("(s)", name), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { LOGE("failed to get caller uid"); if (error) { LOGE("dbus error message : %s", error->message); g_error_free(error); } return -1; } g_variant_get(ret, "(u)", &uid); g_variant_unref(ret); return uid; } static pid_t __get_caller_pid(const char *name) { guint pid; GVariant *ret; GError *error = NULL; ret = g_dbus_connection_call_sync(alarm_context.connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionUnixProcessID", g_variant_new("(s)", name), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!ret) { LOGE("failed to get caller pid"); if (error) { LOGE("dbus error message : %s", error->message); g_error_free(error); } return -1; } g_variant_get(ret, "(u)", &pid); g_variant_unref(ret); return pid; } int _display_lock_state(char *state, char *flag, unsigned int timeout) { GDBusMessage *msg = NULL; GDBusMessage *reply = NULL; GVariant *body = NULL; int ret = ALARMMGR_RESULT_SUCCESS; int val = -1; msg = g_dbus_message_new_method_call( DEVICED_BUS_NAME, DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY, DEVICED_LOCK_STATE); if (!msg) { LOGE("g_dbus_message_new_method_call() is failed. (%s:%s-%s)", DEVICED_BUS_NAME, DEVICED_INTERFACE_DISPLAY, DEVICED_LOCK_STATE); return ERR_ALARM_SYSTEM_FAIL; } g_dbus_message_set_body( msg, g_variant_new("(sssi)", state, flag, "NULL", timeout)); reply = g_dbus_connection_send_message_with_reply_sync( alarm_context.connection, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, DEVICED_DBUS_REPLY_TIMEOUT, NULL, NULL, NULL); if (!reply) { LOGE("No reply.\ g_dbus_connection_send_message_with_reply_sync() is failed."); ret = ERR_ALARM_SYSTEM_FAIL; } else { body = g_dbus_message_get_body(reply); if (!body) { LOGE("g_dbus_message_get_body() is failed."); ret = ERR_ALARM_SYSTEM_FAIL; } else { g_variant_get(body, "(i)", &val); if (val != 0) { LOGE("Failed to lock display"); ret = ERR_ALARM_SYSTEM_FAIL; } else { LOGD("Lock LCD OFF is successfully done"); } } } g_dbus_connection_flush_sync(alarm_context.connection, NULL, NULL); g_object_unref(msg); if (reply) g_object_unref(reply); return ret; } int _display_unlock_state(char *state, char *flag) { GDBusMessage *msg = NULL; GDBusMessage *reply = NULL; GVariant *body = NULL; int ret = ALARMMGR_RESULT_SUCCESS; int val = -1; msg = g_dbus_message_new_method_call( DEVICED_BUS_NAME, DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY, DEVICED_UNLOCK_STATE); if (!msg) { LOGE("g_dbus_message_new_method_call() is failed. (%s:%s-%s)", DEVICED_BUS_NAME, DEVICED_INTERFACE_DISPLAY, DEVICED_UNLOCK_STATE); return ERR_ALARM_SYSTEM_FAIL; } g_dbus_message_set_body(msg, g_variant_new("(ss)", state, flag)); reply = g_dbus_connection_send_message_with_reply_sync( alarm_context.connection, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, DEVICED_DBUS_REPLY_TIMEOUT, NULL, NULL, NULL); if (!reply) { LOGE("No reply.\ g_dbus_connection_send_message_with_reply_sync() is failed."); ret = ERR_ALARM_SYSTEM_FAIL; } else { body = g_dbus_message_get_body(reply); if (!body) { LOGE("g_dbus_message_get_body() is failed."); ret = ERR_ALARM_SYSTEM_FAIL; } else { g_variant_get(body, "(i)", &val); if (val != 0) { LOGE("Failed to unlock display"); ret = ERR_ALARM_SYSTEM_FAIL; } else { LOGD("Unlock LCD OFF is successfully done"); } } } g_dbus_connection_flush_sync(alarm_context.connection, NULL, NULL); g_object_unref(msg); if (reply) g_object_unref(reply); return ret; } static void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invoc, gpointer user_data) { int ret; int num_of_alarm; int alarm_id; const char *name = g_dbus_method_invocation_get_sender(invoc); uid_t uid = __get_caller_uid(name); pid_t pid = __get_caller_pid(name); if (g_strcmp0(method_name, "alarm_create_periodic") == 0) { ret = alarm_manager_alarm_create_periodic(parameters, uid, pid, &alarm_id); g_dbus_method_invocation_return_value(invoc, g_variant_new("(ii)", alarm_id, ret)); } else if (g_strcmp0(method_name, "alarm_create") == 0) { ret = alarm_manager_alarm_create(parameters, uid, pid, &alarm_id); g_dbus_method_invocation_return_value(invoc, g_variant_new("(ii)", alarm_id, ret)); } else if (g_strcmp0(method_name, "alarm_create_appsvc") == 0) { ret = alarm_manager_alarm_create_appsvc(parameters, uid, pid, &alarm_id); g_dbus_method_invocation_return_value(invoc, g_variant_new("(ii)", alarm_id, ret)); } else if (g_strcmp0(method_name, "alarm_create_noti") == 0) { const char *notification_priv = "http://tizen.org/privilege/notification"; ret = _cynara_check(invoc, notification_priv, pid); if (ret == ALARMMGR_RESULT_SUCCESS) { ret = alarm_manager_alarm_create_noti(parameters, uid, pid, &alarm_id); } g_dbus_method_invocation_return_value(invoc, g_variant_new("(ii)", alarm_id, ret)); } else if (g_strcmp0(method_name, "alarm_delete") == 0) { ret = alarm_manager_alarm_delete(parameters, uid, pid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_delete_all") == 0) { ret = alarm_manager_alarm_delete_all(parameters, uid, pid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_update") == 0) { ret = alarm_manager_alarm_update(parameters, uid, pid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_get_number_of_ids") == 0) { ret = alarm_manager_alarm_get_number_of_ids(uid, pid, &num_of_alarm); g_dbus_method_invocation_return_value(invoc, g_variant_new("(ii)", num_of_alarm, ret)); } else if (g_strcmp0(method_name, "alarm_get_list_of_ids") == 0) { GVariant *arr = NULL; ret = alarm_manager_alarm_get_list_of_ids(parameters, uid, pid, &arr, &num_of_alarm); g_dbus_method_invocation_return_value(invoc, g_variant_new("(@aiii)", arr, num_of_alarm, ret)); } else if (g_strcmp0(method_name, "alarm_get_appsvc_info") == 0) { gchar *b_data = NULL; ret = alarm_manager_alarm_get_appsvc_info(parameters, uid, &b_data); g_dbus_method_invocation_return_value(invoc, g_variant_new("(si)", b_data, ret)); if (b_data) free(b_data); } else if (g_strcmp0(method_name, "alarm_get_noti_info") == 0) { gchar *noti_data = NULL; ret = alarm_manager_alarm_get_noti_info(parameters, uid, ¬i_data); g_dbus_method_invocation_return_value(invoc, g_variant_new("(si)", noti_data, ret)); if (noti_data) free(noti_data); } else if (g_strcmp0(method_name, "alarm_get_info") == 0) { alarm_info_t alarm_info = { 0, }; ret = alarm_manager_alarm_get_info(parameters, uid, &alarm_info); g_dbus_method_invocation_return_value( invoc, g_variant_new("(iiiiiiiiiiiixi)", alarm_info.start.year, alarm_info.start.month, alarm_info.start.day, alarm_info.start.hour, alarm_info.start.min, alarm_info.start.sec, alarm_info.end.year, alarm_info.end.month, alarm_info.end.day, alarm_info.mode.u_interval.day_of_week, alarm_info.mode.repeat, alarm_info.alarm_type, (gint64)alarm_info.reserved_info, ret)); } else if (g_strcmp0(method_name, "alarm_get_next_duetime") == 0) { time_t duetime; ret = alarm_manager_alarm_get_next_duetime(parameters, uid, &duetime); g_dbus_method_invocation_return_value(invoc, g_variant_new("(xi)", (gint64)duetime, ret)); } else if (g_strcmp0(method_name, "alarm_get_all_info") == 0) { char *db_path = NULL; ret = alarm_manager_alarm_get_all_info(uid, &db_path); g_dbus_method_invocation_return_value(invoc, g_variant_new("(si)", db_path, ret)); if (db_path) free(db_path); } else if (g_strcmp0(method_name, "alarm_set_rtc_time") == 0) { ret = alarm_manager_alarm_set_rtc_time(parameters); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_set_time") == 0) { ret = alarm_manager_alarm_set_time(parameters, pid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_set_time_with_propagation_delay") == 0) { ret = alarm_manager_alarm_set_time_with_propagation_delay(parameters, pid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_set_timezone") == 0) { ret = alarm_manager_alarm_set_timezone(parameters); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_set_global") == 0) { ret = alarm_manager_alarm_set_global(parameters, uid); g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); } else if (g_strcmp0(method_name, "alarm_get_global") == 0) { gboolean global; ret = alarm_manager_alarm_get_global(parameters, &global); g_dbus_method_invocation_return_value(invoc, g_variant_new("(bi)", global, ret)); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, NULL }; static void __on_bus_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { LOGD("bus acquired(%s)", name); guint reg_id = 0; GError *error = NULL; reg_id = g_dbus_connection_register_object(connection, ALARM_MGR_DBUS_PATH, introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &error); if (reg_id == 0) { LOGE("g_dbus_connection_register_object error(%s)", error->message); g_error_free(error); } } static void __on_bus_name_owner_changed(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GSList *entry = NULL; __expired_alarm_t *expire_info = NULL; char *service_name = NULL; /* On expiry, the killed app can be launched by aul. Then, the owner of the bus name which was registered by the app is changed. * In this case, "NameOwnerChange" signal is broadcasted. */ if (signal_name && strcmp(signal_name, "NameOwnerChanged") == 0) { g_variant_get(parameters, "(&sss)", &service_name, NULL, NULL); for (entry = g_expired_alarm_list; entry; entry = entry->next) { if (entry->data) { expire_info = (__expired_alarm_t *) entry->data; SECURE_LOGD("expired service(%s), owner changed service(%s)", expire_info->service_name, service_name); if (strcmp(expire_info->service_name, service_name) == 0) { SECURE_LOGE("expired service name(%s) alarm_id (%d)", expire_info->service_name, expire_info->alarm_id); _alarm_send_noti_to_application_by_dbus( expire_info->service_name, expire_info->alarm_id, 0, expire_info->uid); g_expired_alarm_list = g_slist_remove(g_expired_alarm_list, entry->data); g_free(expire_info); } } } } } bool _initialize_dbus(void) { GDBusConnection *connection = NULL; GError *error = NULL; guint subsc_id; guint owner_id; LOGD("__initialize_dbus Enter"); connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (connection == NULL) { LOGE("g_bus_get_sync() is failed"); if (error) { LOGE("dbus error message : %s", error->message); g_error_free(error); } return false; } subsc_id = g_dbus_connection_signal_subscribe(connection, "org.freedesktop.DBus", "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus", NULL, G_DBUS_SIGNAL_FLAGS_NONE, __on_bus_name_owner_changed, NULL, NULL); if (subsc_id == 0) { LOGE("Subscribing to signal for invoking callback is failed."); g_object_unref(connection); return false; } introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, &error); if (!introspection_data) { LOGE("g_dbus_node_info_new_for_xml error(%s)", error->message); g_object_unref(connection); g_error_free(error); return false; } owner_id = g_bus_own_name_on_connection(connection, ALARM_MGR_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, __on_bus_name_acquired, NULL, NULL, NULL); if (owner_id == 0) { LOGE("Acquiring the own name is failed."); g_dbus_node_info_unref(introspection_data); g_object_unref(connection); return false; } alarm_context.connection = connection; return true; }