From: Changgyu Choi Date: Tue, 3 Sep 2024 09:10:27 +0000 (+0900) Subject: Implement rust-app-event X-Git-Tag: accepted/tizen/unified/20241001.004118~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F57%2F317057%2F1;p=platform%2Fcore%2Fapi%2Fapp-event.git Implement rust-app-event rust-app-event has been added. Change-Id: Id22b832977fb6f91d96cf9e5f411d73c19760502 Signed-off-by: Changgyu Choi --- diff --git a/packaging/capi-appfw-event.spec b/packaging/capi-appfw-event.spec index a006fa9..3eeba64 100644 --- a/packaging/capi-appfw-event.spec +++ b/packaging/capi-appfw-event.spec @@ -1,3 +1,6 @@ +%global crate rust_app_event +%global real_crate_name rust_app_event + Name: capi-appfw-event Summary: App event API Version: 0.6.11 @@ -9,11 +12,17 @@ Source1001: capi-appfw-event.manifest BuildRequires: cmake BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(bundle) +BuildRequires: pkgconfig(parcel) BuildRequires: pkgconfig(eventsystem) BuildRequires: pkgconfig(capi-base-common) BuildRequires: pkgconfig(aul) BuildRequires: pkgconfig(gmock) BuildRequires: pkgconfig(pkgmgr-info) +BuildRequires: rust-mockall_double +BuildRequires: rust-mockall +BuildRequires: rust-tizen-bundle +BuildRequires: rust-libc +BuildRequires: rust %if 0%{?gcov:1} BuildRequires: lcov @@ -31,6 +40,14 @@ Requires: %{name} = %{version} %description devel An Application event library in Tizen C API (Development) package. +%package -n rust-app-event +Summary: app-event for rust +Group: Rust/Libraries +Requires: %{name} = %{version}-%{release} + +%description -n rust-app-event +App Event for rust + %if 0%{?gcov:1} %package gcov Summary: App event API(gcov) @@ -56,8 +73,31 @@ MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` %cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} %__make %{?jobs:-j%jobs} +%{rustc_std_build} --crate-type=dylib \ + --crate-name=%{real_crate_name} \ + -L native="./src/app-event/" \ + %{?rustc_edition:--edition=%{rustc_edition}} \ + %rust_dylib_extern tizen_base \ + %rust_dylib_extern libc \ + ./src/rust-app-event/src/lib.rs + +%{rustc_std_build} --test --crate-type=bin \ + --extern mockall=%{_rust_dylibdir}/libmockall.so \ + --extern mockall_double=%{_rust_dylibdir}/libmockall_double.so \ + --crate-name=unittests_%{real_crate_name} \ + -L native="./src/app-event/" \ + %{?rustc_edition:--edition=%{rustc_edition}} \ + %rust_dylib_extern tizen_base \ + ./src/rust-app-event/src/lib.rs + %check -ctest -V +mv %{buildroot}/%{_rust_dylibdir}/lib%{real_crate_name} %{buildroot}/%{_rust_dylibdir}/lib%{real_crate_name}.so + +export LD_LIBRARY_PATH="../../src/app-event" +ctest -V %{?_smp_mflags} + +export LD_LIBRARY_PATH="./src/app-event/:%{_rust_dylibdir}" +RUST_BACKTRACE=1 RUST_TEST_THREADS=1 ./unittests_%{real_crate_name} %if 0%{?gcov:1} lcov -c --ignore-errors mismatch,graph,unused --no-external -b . -d . -o %{name}.info @@ -116,6 +156,9 @@ EOF mkdir -p %{buildroot}%{_bindir}/tizen-unittests/%{name} install -m 0755 run-unittest.sh %{buildroot}%{_bindir}/tizen-unittests/%{name}/ +install -d -m 0755 %{buildroot}%{_rust_dylibdir} +install -m 0644 lib%{real_crate_name}.so %{buildroot}/%{_rust_dylibdir}/lib%{real_crate_name} + %post -p /sbin/ldconfig %postun -p /sbin/ldconfig @@ -144,6 +187,10 @@ GTest for app_event %{_libdir}/pkgconfig/capi-appfw-event.pc %{_libdir}/libcapi-appfw-event.so +%files -n rust-app-event +%manifest %{name}.manifest +%{_rust_dylibdir}/lib%{real_crate_name}.so + %if 0%{?gcov:1} %files gcov %{_datadir}/gcov/* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25f865a..75717a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,65 +1 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) -SET(fw_name "capi-appfw-event") - -PROJECT(${fw_name}) - -SET(CMAKE_INSTALL_PREFIX /usr) -SET(PREFIX ${CMAKE_INSTALL_PREFIX}) - -SET(INC_DIR ${CMAKE_SOURCE_DIR}/include) -INCLUDE_DIRECTORIES(${INC_DIR}) - -SET(requires "glib-2.0 dlog bundle eventsystem capi-base-common aul pkgmgr-info") -SET(pc_requires "capi-base-common") - -INCLUDE(FindPkgConfig) -pkg_check_modules(${fw_name} REQUIRED ${requires}) -FOREACH(flag ${${fw_name}_CFLAGS}) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") -ENDFOREACH(flag) - -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC") -SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -Wall -Werror") - -IF("${ARCH}" STREQUAL "arm") - ADD_DEFINITIONS("-DTARGET") -ENDIF("${ARCH}" STREQUAL "arm") - -ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") -ADD_DEFINITIONS("-DSLP_DEBUG") - -SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}") - -add_library(${fw_name} SHARED - event.c - ) - -TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS}) - -SET_TARGET_PROPERTIES(${fw_name} - PROPERTIES - VERSION ${FULLVER} - SOVERSION ${MAJORVER} - CLEAN_DIRECT_OUTPUT 1 -) - -INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR}) -INSTALL( - DIRECTORY ${INC_DIR}/ DESTINATION include/appfw - FILES_MATCHING - PATTERN "*_private.h" EXCLUDE - PATTERN "${INC_DIR}/*.h" - ) - -SET(PC_NAME ${fw_name}) -SET(PC_REQUIRED ${pc_requires}) -SET(PC_LDFLAGS -l${fw_name}) - -CONFIGURE_FILE( - ${CMAKE_SOURCE_DIR}/${fw_name}.pc.in - ${CMAKE_SOURCE_DIR}/${fw_name}.pc - @ONLY -) -INSTALL(FILES ${CMAKE_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) - - +ADD_SUBDIRECTORY(app-event) diff --git a/src/app-event/CMakeLists.txt b/src/app-event/CMakeLists.txt new file mode 100644 index 0000000..25f865a --- /dev/null +++ b/src/app-event/CMakeLists.txt @@ -0,0 +1,65 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +SET(fw_name "capi-appfw-event") + +PROJECT(${fw_name}) + +SET(CMAKE_INSTALL_PREFIX /usr) +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +SET(INC_DIR ${CMAKE_SOURCE_DIR}/include) +INCLUDE_DIRECTORIES(${INC_DIR}) + +SET(requires "glib-2.0 dlog bundle eventsystem capi-base-common aul pkgmgr-info") +SET(pc_requires "capi-base-common") + +INCLUDE(FindPkgConfig) +pkg_check_modules(${fw_name} REQUIRED ${requires}) +FOREACH(flag ${${fw_name}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -Wall -Werror") + +IF("${ARCH}" STREQUAL "arm") + ADD_DEFINITIONS("-DTARGET") +ENDIF("${ARCH}" STREQUAL "arm") + +ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +ADD_DEFINITIONS("-DSLP_DEBUG") + +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}") + +add_library(${fw_name} SHARED + event.c + ) + +TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS}) + +SET_TARGET_PROPERTIES(${fw_name} + PROPERTIES + VERSION ${FULLVER} + SOVERSION ${MAJORVER} + CLEAN_DIRECT_OUTPUT 1 +) + +INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL( + DIRECTORY ${INC_DIR}/ DESTINATION include/appfw + FILES_MATCHING + PATTERN "*_private.h" EXCLUDE + PATTERN "${INC_DIR}/*.h" + ) + +SET(PC_NAME ${fw_name}) +SET(PC_REQUIRED ${pc_requires}) +SET(PC_LDFLAGS -l${fw_name}) + +CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/${fw_name}.pc.in + ${CMAKE_SOURCE_DIR}/${fw_name}.pc + @ONLY +) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) + + diff --git a/src/app-event/event.c b/src/app-event/event.c new file mode 100644 index 0000000..bfc7faf --- /dev/null +++ b/src/app-event/event.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2015 - 2018 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 +#include +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "CAPI_APPFW_EVENT" +#define MAX_SIZE 100 +#define MAX_APP_ID_LEN 256 +#define SYS_EVENT_NAME_PREFIX "tizen.system.event" + +enum alias_appid_mode { + ALIAS_APPID_MODE_UNKNOWN, + ALIAS_APPID_MODE_OFF, + ALIAS_APPID_MODE_ON +}; + +typedef struct event_handler { + char *event_name; + int event_type; + unsigned int reg_id; + event_cb cb; + void *user_data; +} event_handler_s; + +static pthread_mutex_t __register_sync_lock = PTHREAD_MUTEX_INITIALIZER; +static GHashTable *__event_table; +static pthread_mutex_t __event_handle_lock = PTHREAD_MUTEX_INITIALIZER; +static GList *__event_handle_list; + +#define KEY_ALIAS_APP_ID "http://tizen.org/metadata/app-event/alias-appid-mode" + +static int __check_alias_appid_mode(const char *real_appid) +{ + pkgmgrinfo_appinfo_h handle; + int ret; + char *on_off; + int alias_mode = ALIAS_APPID_MODE_OFF; + + ret = pkgmgrinfo_appinfo_get_appinfo(real_appid, &handle); + if (ret != PMINFO_R_OK) { + LOGE("fail to get %s appinfo %d", real_appid, ret); + return ALIAS_APPID_MODE_UNKNOWN; + } + + ret = pkgmgrinfo_appinfo_get_metadata_value(handle, KEY_ALIAS_APP_ID, &on_off); + if (ret == PMINFO_R_OK && strcmp(on_off, "yes") == 0) + alias_mode = ALIAS_APPID_MODE_ON; + + pkgmgrinfo_appinfo_destroy_appinfo(handle); + LOGD("alias_appid_mode %d", alias_mode); + + return alias_mode; +} + +static int __get_publication_alias_appid_mode(void) +{ + static int alias_appid_mode = ALIAS_APPID_MODE_UNKNOWN; + int ret; + char buffer[MAX_APP_ID_LEN] = {0, }; + + if (alias_appid_mode != ALIAS_APPID_MODE_UNKNOWN) + return alias_appid_mode; + + ret = aul_app_get_appid_bypid(getpid(), buffer, sizeof(buffer)); + if(ret != AUL_R_OK) { + LOGE("Failed to get the application ID: %d", ret); + return alias_appid_mode; + } + + alias_appid_mode = __check_alias_appid_mode(buffer); + return alias_appid_mode; +} + +static bool __get_real_event_name(const char *event_name, + char **real_event_name, bool publish) +{ + char *prefix, *user_defined_name; + char *real_appid, *alias_appid, *real_event; + int ret, len; + + /* event.{sender's appid}.{user-defined name} */ + prefix = strchr(event_name, '.'); + if (prefix == NULL) + return false; + + user_defined_name = strrchr(event_name, '.'); + if (user_defined_name == NULL) + return false; + + if (prefix == user_defined_name) + return false; + + len = strlen(user_defined_name); + if (len <= 1 || len > 128) + return false; + + len = user_defined_name - prefix; + alias_appid = malloc(sizeof(char) * len + 1); + if (alias_appid == NULL) { + LOGE("out of memory"); + return false; + } + + prefix += 1; + snprintf(alias_appid, len, "%s", prefix); + + ret = aul_svc_get_appid_by_alias_appid(alias_appid, &real_appid); + free(alias_appid); + if (ret < 0) + return false; + + if (publish == false + && __check_alias_appid_mode(real_appid) != ALIAS_APPID_MODE_ON) + return false; + + len = 6 /* event. */ + strlen(real_appid) + + 1 /* . */ + strlen(user_defined_name); + real_event = malloc(sizeof(char) * len + 1); + if (real_event == NULL) { + LOGE("out of memory"); + free(real_appid); + return false; + } + + snprintf(real_event, len, "event.%s%s", real_appid, user_defined_name); + free(real_appid); + *real_event_name = real_event; + LOGI("real_event (%s)->(%s)", event_name, real_event); + + return true; + +} + +static int __set_real_event_info(const char *real_event, const char *event) +{ + char *key; + char *value; + + if (g_hash_table_lookup(__event_table, real_event) != NULL) + return ES_R_OK; + + key = strdup(real_event); + if (key == NULL) + return ES_R_ENOMEM; + + value = strdup(event); + if (value == NULL) { + free(key); + return ES_R_ENOMEM; + } + + g_hash_table_insert(__event_table, key, value); + + return ES_R_OK; +} + +/* LCOV_EXCL_START */ +static const char *__event_error_to_string(event_error_e error) +{ + switch (error) { + case EVENT_ERROR_NONE: + return "NONE"; + case EVENT_ERROR_INVALID_PARAMETER: + return "INVALID_PARAMETER"; + case EVENT_ERROR_OUT_OF_MEMORY: + return "OUT_OF_MEMORY"; + case EVENT_ERROR_TIMED_OUT: + return "TIMED_OUT"; + case EVENT_ERROR_IO_ERROR: + return "IO ERROR"; + case EVENT_ERROR_PERMISSION_DENIED: + return "PERMISSION DENIED"; + default: + return "UNKNOWN"; + } +} +/* LCOV_EXCL_STOP */ + +static int __event_error(event_error_e error, + const char *function, const char *description) +{ + if (description) { + LOGE("[%s] %s(0x%08x) : %s", function, __event_error_to_string(error), + error, description); + } else { + LOGE("[%s] %s(0x%08x)", function, __event_error_to_string(error), error); + } + + return error; +} + +static void __event_eventsystem_callback(const char *real_event_name, + bundle_raw *event_data, int len, void *user_data) +{ + event_handler_h handler = (event_handler_h)user_data; + bundle *b; + const char *registered_event_name; + GList *found; + event_cb handler_cb; + void *handler_user_data; + + registered_event_name = g_hash_table_lookup(__event_table, real_event_name); + LOGD("real_event_name(%s , %s)", real_event_name, registered_event_name); + if (registered_event_name == NULL) + registered_event_name = real_event_name; + + pthread_mutex_lock(&__event_handle_lock); + found = g_list_find(__event_handle_list, handler); + if (found == NULL) { + LOGW("%p doesn't exist", handler); + pthread_mutex_unlock(&__event_handle_lock); + return; + } + + handler_cb = handler->cb; + handler_user_data = handler->user_data; + pthread_mutex_unlock(&__event_handle_lock); + + if (handler_cb) { + b = bundle_decode(event_data, len); + if (b == NULL) { + LOGE("bundle_decode failed"); + return; + } + + handler_cb(registered_event_name, b, handler_user_data); + bundle_free(b); + } +} + +int event_add_event_handler(const char *event_name, event_cb callback, + void *user_data, event_handler_h *event_handler) +{ + int ret = 0; + int event_type = 0; + unsigned int reg_id = 0; + event_handler_h handler = NULL; + char *real_event_name; + + if (event_handler == NULL || event_name == NULL || callback == NULL) { + return __event_error(EVENT_ERROR_INVALID_PARAMETER + , __FUNCTION__, NULL); + } + + if (__event_table == NULL) + __event_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); + + handler = calloc(1, sizeof(event_handler_s)); + if (handler == NULL) { + return __event_error(EVENT_ERROR_OUT_OF_MEMORY, + __FUNCTION__, NULL); + } + + if (__get_real_event_name(event_name, &real_event_name, false)) { + handler->event_name = real_event_name; + ret = __set_real_event_info(real_event_name, event_name); + if (ret != ES_R_OK) + goto error; + } else { + handler->event_name = strdup(event_name); + } + + if (handler->event_name == NULL) { + ret = ES_R_ENOMEM; + goto error; + } + + handler->cb = callback; + handler->user_data = user_data; + + pthread_mutex_lock(&__event_handle_lock); + __event_handle_list = g_list_prepend(__event_handle_list, handler); + pthread_mutex_unlock(&__event_handle_lock); + + pthread_mutex_lock(&__register_sync_lock); + ret = eventsystem_register_application_event(handler->event_name, ®_id, + &event_type, (eventsystem_cb)__event_eventsystem_callback, + handler); + pthread_mutex_unlock(&__register_sync_lock); + + if (ret < 0) + goto error; + + handler->reg_id = reg_id; + handler->event_type = event_type; + *event_handler = handler; + + LOGW("event_add_event_handler(%p, %s)", handler, event_name); + return EVENT_ERROR_NONE; + +error: + pthread_mutex_lock(&__event_handle_lock); + __event_handle_list = g_list_remove(__event_handle_list, handler); + pthread_mutex_unlock(&__event_handle_lock); + if (handler) { + if (handler->event_name) + free(handler->event_name); + free(handler); + } + + if (ret == ES_R_ENOTPERMITTED) { + return __event_error(EVENT_ERROR_PERMISSION_DENIED, + __FUNCTION__, NULL); + + } else if (ret == ES_R_ENOMEM) { + return __event_error(EVENT_ERROR_OUT_OF_MEMORY, + __FUNCTION__, NULL); + } else { + return __event_error(EVENT_ERROR_IO_ERROR, + __FUNCTION__, NULL); + } + +} + +int event_remove_event_handler(event_handler_h event_handler) +{ + int ret; + + if (event_handler == NULL) { + return __event_error(EVENT_ERROR_INVALID_PARAMETER, + __FUNCTION__, NULL); + } + + ret = eventsystem_unregister_application_event(event_handler->reg_id); + if (ret < 0) + return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); + + pthread_mutex_lock(&__event_handle_lock); + __event_handle_list = g_list_remove(__event_handle_list, event_handler); + pthread_mutex_unlock(&__event_handle_lock); + + LOGW("event_remove_event_handler(%p, %s)", event_handler, event_handler->event_name); + + free(event_handler->event_name); + free(event_handler); + + return EVENT_ERROR_NONE; +} + +static bool __is_system_event(const char *event_name) +{ + int len = strlen(SYS_EVENT_NAME_PREFIX); + + if (strncmp(event_name, SYS_EVENT_NAME_PREFIX, len) != 0) + return false; + + return true; +} + +int event_publish_app_event(const char *event_name, bundle *event_data) +{ + char *real_event_name; + int ret; + + if (event_data == NULL || event_name == NULL) + return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); + + if (__is_system_event(event_name)) { + ret = eventsystem_send_system_event(event_name, event_data); + if (ret < 0) + return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); + + return EVENT_ERROR_NONE; + } + + ret = __get_publication_alias_appid_mode(); + + if (ret == ALIAS_APPID_MODE_ON + && __get_real_event_name(event_name, &real_event_name, true)) { + ret = eventsystem_send_user_event(real_event_name, event_data, false); + free(real_event_name); + } else { + ret = eventsystem_send_user_event(event_name, event_data, false); + } + + if (ret < 0) + return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); + + return EVENT_ERROR_NONE; +} + +int event_publish_trusted_app_event(const char *event_name, bundle *event_data) +{ + char *real_event_name; + int ret; + + if (event_data == NULL || event_name == NULL) + return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); + + ret = __get_publication_alias_appid_mode(); + + if (ret == ALIAS_APPID_MODE_ON + && __get_real_event_name(event_name, &real_event_name, true)) { + ret = eventsystem_send_user_event(real_event_name, event_data, true); + free(real_event_name); + } else { + ret = eventsystem_send_user_event(event_name, event_data, true); + } + + if (ret < 0) + return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); + + return EVENT_ERROR_NONE; +} + +int event_keep_last_event_data(const char *event_name) +{ + int ret; + char *real_event_name = NULL; + + if (event_name == NULL) + return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); + + if (__get_real_event_name(event_name, &real_event_name, false)) { + ret = __set_real_event_info(real_event_name, event_name); + if (ret != ES_R_OK) + goto out; + ret = eventsystem_keep_last_event_data(real_event_name); + } else { + ret = eventsystem_keep_last_event_data(event_name); + } + + +out: + if (real_event_name) + free(real_event_name); + + if (ret < 0) { + if (ret == ES_R_ENOMEM) + return __event_error(EVENT_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL); + else + return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); + } + + return EVENT_ERROR_NONE; +} diff --git a/src/event.c b/src/event.c deleted file mode 100644 index bfc7faf..0000000 --- a/src/event.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (c) 2015 - 2018 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 -#include -#include -#include - -#ifdef LOG_TAG -#undef LOG_TAG -#endif - -#define LOG_TAG "CAPI_APPFW_EVENT" -#define MAX_SIZE 100 -#define MAX_APP_ID_LEN 256 -#define SYS_EVENT_NAME_PREFIX "tizen.system.event" - -enum alias_appid_mode { - ALIAS_APPID_MODE_UNKNOWN, - ALIAS_APPID_MODE_OFF, - ALIAS_APPID_MODE_ON -}; - -typedef struct event_handler { - char *event_name; - int event_type; - unsigned int reg_id; - event_cb cb; - void *user_data; -} event_handler_s; - -static pthread_mutex_t __register_sync_lock = PTHREAD_MUTEX_INITIALIZER; -static GHashTable *__event_table; -static pthread_mutex_t __event_handle_lock = PTHREAD_MUTEX_INITIALIZER; -static GList *__event_handle_list; - -#define KEY_ALIAS_APP_ID "http://tizen.org/metadata/app-event/alias-appid-mode" - -static int __check_alias_appid_mode(const char *real_appid) -{ - pkgmgrinfo_appinfo_h handle; - int ret; - char *on_off; - int alias_mode = ALIAS_APPID_MODE_OFF; - - ret = pkgmgrinfo_appinfo_get_appinfo(real_appid, &handle); - if (ret != PMINFO_R_OK) { - LOGE("fail to get %s appinfo %d", real_appid, ret); - return ALIAS_APPID_MODE_UNKNOWN; - } - - ret = pkgmgrinfo_appinfo_get_metadata_value(handle, KEY_ALIAS_APP_ID, &on_off); - if (ret == PMINFO_R_OK && strcmp(on_off, "yes") == 0) - alias_mode = ALIAS_APPID_MODE_ON; - - pkgmgrinfo_appinfo_destroy_appinfo(handle); - LOGD("alias_appid_mode %d", alias_mode); - - return alias_mode; -} - -static int __get_publication_alias_appid_mode(void) -{ - static int alias_appid_mode = ALIAS_APPID_MODE_UNKNOWN; - int ret; - char buffer[MAX_APP_ID_LEN] = {0, }; - - if (alias_appid_mode != ALIAS_APPID_MODE_UNKNOWN) - return alias_appid_mode; - - ret = aul_app_get_appid_bypid(getpid(), buffer, sizeof(buffer)); - if(ret != AUL_R_OK) { - LOGE("Failed to get the application ID: %d", ret); - return alias_appid_mode; - } - - alias_appid_mode = __check_alias_appid_mode(buffer); - return alias_appid_mode; -} - -static bool __get_real_event_name(const char *event_name, - char **real_event_name, bool publish) -{ - char *prefix, *user_defined_name; - char *real_appid, *alias_appid, *real_event; - int ret, len; - - /* event.{sender's appid}.{user-defined name} */ - prefix = strchr(event_name, '.'); - if (prefix == NULL) - return false; - - user_defined_name = strrchr(event_name, '.'); - if (user_defined_name == NULL) - return false; - - if (prefix == user_defined_name) - return false; - - len = strlen(user_defined_name); - if (len <= 1 || len > 128) - return false; - - len = user_defined_name - prefix; - alias_appid = malloc(sizeof(char) * len + 1); - if (alias_appid == NULL) { - LOGE("out of memory"); - return false; - } - - prefix += 1; - snprintf(alias_appid, len, "%s", prefix); - - ret = aul_svc_get_appid_by_alias_appid(alias_appid, &real_appid); - free(alias_appid); - if (ret < 0) - return false; - - if (publish == false - && __check_alias_appid_mode(real_appid) != ALIAS_APPID_MODE_ON) - return false; - - len = 6 /* event. */ + strlen(real_appid) - + 1 /* . */ + strlen(user_defined_name); - real_event = malloc(sizeof(char) * len + 1); - if (real_event == NULL) { - LOGE("out of memory"); - free(real_appid); - return false; - } - - snprintf(real_event, len, "event.%s%s", real_appid, user_defined_name); - free(real_appid); - *real_event_name = real_event; - LOGI("real_event (%s)->(%s)", event_name, real_event); - - return true; - -} - -static int __set_real_event_info(const char *real_event, const char *event) -{ - char *key; - char *value; - - if (g_hash_table_lookup(__event_table, real_event) != NULL) - return ES_R_OK; - - key = strdup(real_event); - if (key == NULL) - return ES_R_ENOMEM; - - value = strdup(event); - if (value == NULL) { - free(key); - return ES_R_ENOMEM; - } - - g_hash_table_insert(__event_table, key, value); - - return ES_R_OK; -} - -/* LCOV_EXCL_START */ -static const char *__event_error_to_string(event_error_e error) -{ - switch (error) { - case EVENT_ERROR_NONE: - return "NONE"; - case EVENT_ERROR_INVALID_PARAMETER: - return "INVALID_PARAMETER"; - case EVENT_ERROR_OUT_OF_MEMORY: - return "OUT_OF_MEMORY"; - case EVENT_ERROR_TIMED_OUT: - return "TIMED_OUT"; - case EVENT_ERROR_IO_ERROR: - return "IO ERROR"; - case EVENT_ERROR_PERMISSION_DENIED: - return "PERMISSION DENIED"; - default: - return "UNKNOWN"; - } -} -/* LCOV_EXCL_STOP */ - -static int __event_error(event_error_e error, - const char *function, const char *description) -{ - if (description) { - LOGE("[%s] %s(0x%08x) : %s", function, __event_error_to_string(error), - error, description); - } else { - LOGE("[%s] %s(0x%08x)", function, __event_error_to_string(error), error); - } - - return error; -} - -static void __event_eventsystem_callback(const char *real_event_name, - bundle_raw *event_data, int len, void *user_data) -{ - event_handler_h handler = (event_handler_h)user_data; - bundle *b; - const char *registered_event_name; - GList *found; - event_cb handler_cb; - void *handler_user_data; - - registered_event_name = g_hash_table_lookup(__event_table, real_event_name); - LOGD("real_event_name(%s , %s)", real_event_name, registered_event_name); - if (registered_event_name == NULL) - registered_event_name = real_event_name; - - pthread_mutex_lock(&__event_handle_lock); - found = g_list_find(__event_handle_list, handler); - if (found == NULL) { - LOGW("%p doesn't exist", handler); - pthread_mutex_unlock(&__event_handle_lock); - return; - } - - handler_cb = handler->cb; - handler_user_data = handler->user_data; - pthread_mutex_unlock(&__event_handle_lock); - - if (handler_cb) { - b = bundle_decode(event_data, len); - if (b == NULL) { - LOGE("bundle_decode failed"); - return; - } - - handler_cb(registered_event_name, b, handler_user_data); - bundle_free(b); - } -} - -int event_add_event_handler(const char *event_name, event_cb callback, - void *user_data, event_handler_h *event_handler) -{ - int ret = 0; - int event_type = 0; - unsigned int reg_id = 0; - event_handler_h handler = NULL; - char *real_event_name; - - if (event_handler == NULL || event_name == NULL || callback == NULL) { - return __event_error(EVENT_ERROR_INVALID_PARAMETER - , __FUNCTION__, NULL); - } - - if (__event_table == NULL) - __event_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); - - handler = calloc(1, sizeof(event_handler_s)); - if (handler == NULL) { - return __event_error(EVENT_ERROR_OUT_OF_MEMORY, - __FUNCTION__, NULL); - } - - if (__get_real_event_name(event_name, &real_event_name, false)) { - handler->event_name = real_event_name; - ret = __set_real_event_info(real_event_name, event_name); - if (ret != ES_R_OK) - goto error; - } else { - handler->event_name = strdup(event_name); - } - - if (handler->event_name == NULL) { - ret = ES_R_ENOMEM; - goto error; - } - - handler->cb = callback; - handler->user_data = user_data; - - pthread_mutex_lock(&__event_handle_lock); - __event_handle_list = g_list_prepend(__event_handle_list, handler); - pthread_mutex_unlock(&__event_handle_lock); - - pthread_mutex_lock(&__register_sync_lock); - ret = eventsystem_register_application_event(handler->event_name, ®_id, - &event_type, (eventsystem_cb)__event_eventsystem_callback, - handler); - pthread_mutex_unlock(&__register_sync_lock); - - if (ret < 0) - goto error; - - handler->reg_id = reg_id; - handler->event_type = event_type; - *event_handler = handler; - - LOGW("event_add_event_handler(%p, %s)", handler, event_name); - return EVENT_ERROR_NONE; - -error: - pthread_mutex_lock(&__event_handle_lock); - __event_handle_list = g_list_remove(__event_handle_list, handler); - pthread_mutex_unlock(&__event_handle_lock); - if (handler) { - if (handler->event_name) - free(handler->event_name); - free(handler); - } - - if (ret == ES_R_ENOTPERMITTED) { - return __event_error(EVENT_ERROR_PERMISSION_DENIED, - __FUNCTION__, NULL); - - } else if (ret == ES_R_ENOMEM) { - return __event_error(EVENT_ERROR_OUT_OF_MEMORY, - __FUNCTION__, NULL); - } else { - return __event_error(EVENT_ERROR_IO_ERROR, - __FUNCTION__, NULL); - } - -} - -int event_remove_event_handler(event_handler_h event_handler) -{ - int ret; - - if (event_handler == NULL) { - return __event_error(EVENT_ERROR_INVALID_PARAMETER, - __FUNCTION__, NULL); - } - - ret = eventsystem_unregister_application_event(event_handler->reg_id); - if (ret < 0) - return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); - - pthread_mutex_lock(&__event_handle_lock); - __event_handle_list = g_list_remove(__event_handle_list, event_handler); - pthread_mutex_unlock(&__event_handle_lock); - - LOGW("event_remove_event_handler(%p, %s)", event_handler, event_handler->event_name); - - free(event_handler->event_name); - free(event_handler); - - return EVENT_ERROR_NONE; -} - -static bool __is_system_event(const char *event_name) -{ - int len = strlen(SYS_EVENT_NAME_PREFIX); - - if (strncmp(event_name, SYS_EVENT_NAME_PREFIX, len) != 0) - return false; - - return true; -} - -int event_publish_app_event(const char *event_name, bundle *event_data) -{ - char *real_event_name; - int ret; - - if (event_data == NULL || event_name == NULL) - return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); - - if (__is_system_event(event_name)) { - ret = eventsystem_send_system_event(event_name, event_data); - if (ret < 0) - return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); - - return EVENT_ERROR_NONE; - } - - ret = __get_publication_alias_appid_mode(); - - if (ret == ALIAS_APPID_MODE_ON - && __get_real_event_name(event_name, &real_event_name, true)) { - ret = eventsystem_send_user_event(real_event_name, event_data, false); - free(real_event_name); - } else { - ret = eventsystem_send_user_event(event_name, event_data, false); - } - - if (ret < 0) - return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); - - return EVENT_ERROR_NONE; -} - -int event_publish_trusted_app_event(const char *event_name, bundle *event_data) -{ - char *real_event_name; - int ret; - - if (event_data == NULL || event_name == NULL) - return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); - - ret = __get_publication_alias_appid_mode(); - - if (ret == ALIAS_APPID_MODE_ON - && __get_real_event_name(event_name, &real_event_name, true)) { - ret = eventsystem_send_user_event(real_event_name, event_data, true); - free(real_event_name); - } else { - ret = eventsystem_send_user_event(event_name, event_data, true); - } - - if (ret < 0) - return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); - - return EVENT_ERROR_NONE; -} - -int event_keep_last_event_data(const char *event_name) -{ - int ret; - char *real_event_name = NULL; - - if (event_name == NULL) - return __event_error(EVENT_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); - - if (__get_real_event_name(event_name, &real_event_name, false)) { - ret = __set_real_event_info(real_event_name, event_name); - if (ret != ES_R_OK) - goto out; - ret = eventsystem_keep_last_event_data(real_event_name); - } else { - ret = eventsystem_keep_last_event_data(event_name); - } - - -out: - if (real_event_name) - free(real_event_name); - - if (ret < 0) { - if (ret == ES_R_ENOMEM) - return __event_error(EVENT_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL); - else - return __event_error(EVENT_ERROR_IO_ERROR, __FUNCTION__, NULL); - } - - return EVENT_ERROR_NONE; -} diff --git a/src/rust-app-event/Cargo.toml b/src/rust-app-event/Cargo.toml new file mode 100644 index 0000000..bf9252b --- /dev/null +++ b/src/rust-app-event/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust-app-event" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/rust-app-event/src/lib.rs b/src/rust-app-event/src/lib.rs new file mode 100644 index 0000000..34bb297 --- /dev/null +++ b/src/rust-app-event/src/lib.rs @@ -0,0 +1,133 @@ +extern crate tizen_bundle; + +use std::ffi::{c_void, CStr, CString}; +use std::os::raw::c_char; +pub use std::sync::{Arc, Mutex}; +pub use tizen_bundle::tizen_bundle::Bundle; + +#[cfg(test)] +pub mod tests; + +#[cfg_attr(test, mockall::automock)] +pub mod ffi { + use std::ffi::{c_void, CStr, CString}; + use std::os::raw::c_char; + + #[link(name = "capi-appfw-event")] + extern "C" { + pub fn event_add_event_handler( + event_name: *const c_char, + cb: extern "C" fn(*const c_char, *mut c_void, *mut c_void), + user_data: *mut c_void, + h: *mut *mut c_void, + ) -> i32; + + pub fn event_remove_event_handler(h: *mut c_void) -> i32; + + pub fn event_publish_app_event( + event_name: *const c_char, + event_data: *mut c_void + ) -> i32; + } +} + +#[cfg_attr(test, mockall_double::double)] +use ffi as mffi; + +use std::slice; + +extern "C" fn received_cb_broker( + event_name_ptr: *const c_char, + event_data_ptr: *mut c_void, + user_data: *mut c_void, +) { + let mut app_event: &mut AppEvent = unsafe { &mut *(user_data as *mut AppEvent) }; + if let Some(on_received) = app_event.on_received.as_ref() { + let event_name = unsafe { CStr::from_ptr(event_name_ptr).to_str().unwrap() }; + let mut event_data = Bundle::from_raw_handle(event_data_ptr, false); + on_received.lock().unwrap()(event_name, &mut event_data); + } +} + +#[derive(PartialEq, Debug)] +pub enum EventError { + IoError = -5, + OutOfMemory = -12, + PermissionDenied = -13, + InvalidParameter = -22, +} + +fn convert_err(id: i32) -> EventError { + match id { + -5 => EventError::IoError, + -12 => EventError::OutOfMemory, + -13 => EventError::PermissionDenied, + -22 => EventError::InvalidParameter, + _ => EventError::IoError, + } +} + +pub struct AppEvent { + event_name: String, + on_received: Option>>>, + handle: *mut c_void, +} + +unsafe impl Send for AppEvent {} +unsafe impl Sync for AppEvent {} + +impl AppEvent { + pub fn new(event_name: String) -> Self { + AppEvent { + event_name, + on_received: None, + handle: std::ptr::null_mut(), + } + } + + pub fn on_received(&mut self, cb: Arc>>) -> Result<(), EventError> { + if self.handle.is_null() { + let mut handle = std::ptr::null_mut(); + let event_name_ptr = CString::new(self.event_name.clone()).unwrap(); + let data = self as *mut _ as *mut c_void; + let ret = unsafe { + mffi::event_add_event_handler(event_name_ptr.as_ptr(), received_cb_broker, data, &mut handle) + }; + + if ret != 0 { + return Err(convert_err(ret)); + } + + self.handle = handle; + } + + self.on_received = Some(cb); + Ok(()) + } + + pub fn publish(event_name: &str, bundle: &mut Bundle) -> Result<(), EventError> { + let c_string = CString::new(event_name).unwrap(); + let event_name_ptr = c_string.into_raw(); + let mut bundle_ptr = bundle.get_raw_handle(); + let ret = unsafe { + mffi::event_publish_app_event(event_name_ptr, bundle_ptr) + }; + + match ret { + 0 => Ok(()), + _ => Err(convert_err(ret)), + } + } +} + +impl Drop for AppEvent { + fn drop(&mut self) { + if self.handle.is_null() { + return; + } + + unsafe { + mffi::event_remove_event_handler(self.handle); + } + } +} diff --git a/src/rust-app-event/src/tests.rs b/src/rust-app-event/src/tests.rs new file mode 100644 index 0000000..f79bb78 --- /dev/null +++ b/src/rust-app-event/src/tests.rs @@ -0,0 +1,24 @@ +use super::*; + +#[test] +fn test_app_event_new() { + let app_event = AppEvent::new("Test".to_string()); +} + +#[test] +fn test_app_event_on_received() { + let ctx = mffi::event_add_event_handler_context(); + ctx.expect().times(1).returning(|_, _, _, _| 0); + + let mut app_event = AppEvent::new("Test".to_string()); + app_event.on_received(Arc::new(Mutex::new(Box::new(|event_name: &str, bundle: &Bundle| {})))); +} + +#[test] +fn test_app_event_publish() { + let ctx = mffi::event_publish_app_event_context(); + ctx.expect().times(1).returning(|_, _| 0); + + let mut bundle = Bundle::new(); + AppEvent::publish("Test", &mut bundle); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3c7dfb3..23dd610 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,12 +20,12 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++14") SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") SET(CMAKE_CXX_FLAGS_RELEASE "-O2") -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../src) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../src/app-event) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../include) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../mock) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../) -AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../src LIB_SOURCES) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../src/app-event LIB_SOURCES) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../mock MOCK_SOURCES) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../tests/unittests SOURCES)