From: Ji-hoon Lee Date: Mon, 13 Apr 2020 10:27:35 +0000 (+0900) Subject: Add multi-assistant audio processing client library X-Git-Tag: submit/tizen/20200528.071502~8 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9d8c48e462ba0940a114a85439f6e282cdebc027;p=platform%2Fcore%2Fuifw%2Fmulti-assistant.git Add multi-assistant audio processing client library To support voice assistants that have separated processes each for rendering UI and processing utterance audio data, provide a client library that enables receiving audio data on behalf of the process that is linked with multi-assistant client library. For now this feature is provided internally but might be open to public as well in the future. Change-Id: Ied125f8260ee3923c49443f66ecfcdb808bb2684 --- diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 87fb2cb..5c106e4 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -14,6 +14,12 @@ SET(UI_SRCS ../common/multi_assistant_settings.c ) +SET(AP_SRCS + ma_ap.c + ma_ap_client.c + ma_ap_dbus.c +) + FOREACH(flag ${pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) @@ -28,6 +34,11 @@ TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS}) ADD_LIBRARY("${PROJECT_NAME}_ui" SHARED ${UI_SRCS}) TARGET_LINK_LIBRARIES("${PROJECT_NAME}_ui" ${pkgs_LDFLAGS}) +## multi assistant ap library ## +ADD_LIBRARY("${PROJECT_NAME}_ap" SHARED ${AP_SRCS}) +TARGET_LINK_LIBRARIES("${PROJECT_NAME}_ap" ${pkgs_LDFLAGS}) + ## Install library files ## -INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIBDIR} COMPONENT RuntimeLibraries) +INSTALL(TARGETS "${PROJECT_NAME}" DESTINATION ${LIBDIR} COMPONENT RuntimeLibraries) INSTALL(TARGETS "${PROJECT_NAME}_ui" DESTINATION ${LIBDIR} COMPONENT RuntimeLibraries) +INSTALL(TARGETS "${PROJECT_NAME}_ap" DESTINATION ${LIBDIR} COMPONENT RuntimeLibraries) diff --git a/client/ma_ap.c b/client/ma_ap.c new file mode 100644 index 0000000..6c11150 --- /dev/null +++ b/client/ma_ap.c @@ -0,0 +1,596 @@ +/** + * Copyright (c) 2020 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 "multi_assistant_ap.h" +#include "ma_ap_client.h" +#include "ma_ap_dbus.h" +#include "ma_main.h" +#include "ma_defs.h" + +/* Sound buf save for test */ +#if 0 +#define BUF_SAVE_MODE +#endif + +static ma_h g_ma_ap = NULL; + +static int g_feature_enabled = -1; +static int g_recorder_privilege_allowed = -1; +static cynara *p_cynara = NULL; + +static void __ma_ap_notify_error(void* data); + +static int __ma_ap_get_feature_enabled() +{ + if (0 == g_feature_enabled) { + //LCOV_EXCL_START + MAAP_SLOGE("[ERROR] Multi-assistant feature NOT supported"); + return MA_ERROR_NOT_SUPPORTED; + //LCOV_EXCL_STOP + } else if (-1 == g_feature_enabled) { + bool ma_supported = false; + bool mic_supported = false; + if (0 == system_info_get_platform_bool(MA_FEATURE_PATH, &ma_supported)) { + if (0 == system_info_get_platform_bool(MA_MIC_FEATURE_PATH, &mic_supported)) { + if (false == ma_supported || false == mic_supported) { + MAAP_SLOGE("[ERROR] Multi-assistant feature NOT supported"); + g_feature_enabled = 0; + return MA_ERROR_NOT_SUPPORTED; + } + + g_feature_enabled = 1; + } else { + MAAP_SLOGE("[ERROR] Fail to get feature value"); //LCOV_EXCL_LINE + return MA_ERROR_NOT_SUPPORTED; + } + } else { + MAAP_SLOGE("[ERROR] Fail to get feature value"); //LCOV_EXCL_LINE + return MA_ERROR_NOT_SUPPORTED; + } + } + return 0; +} + +static int __check_privilege_initialize() +{ + int ret = cynara_initialize(&p_cynara, NULL); + if (CYNARA_API_SUCCESS != ret) + MAAP_SLOGE("[ERROR] fail to initialize"); //LCOV_EXCL_LINE + + return ret == CYNARA_API_SUCCESS; +} + +static int __check_privilege(const char* uid, const char * privilege) +{ + FILE *fp = NULL; + char label_path[1024] = "/proc/self/attr/current"; + char smack_label[1024] = {'\0',}; + + if (!p_cynara) { + return false; + } + + fp = fopen(label_path, "r"); + if (fp != NULL) { + if (0 >= fread(smack_label, 1, sizeof(smack_label), fp)) + MAAP_SLOGE("[ERROR] fail to fread"); //LCOV_EXCL_LINE + + fclose(fp); + } + + pid_t pid = getpid(); + char *session = cynara_session_from_pid(pid); + int ret = cynara_check(p_cynara, smack_label, session, uid, privilege); + MAAP_SLOGD("[Client]cynara_check returned %d(%s)", ret, (CYNARA_API_ACCESS_ALLOWED == ret) ? "Allowed" : "Denied"); + if (session) + free(session); + + if (ret != CYNARA_API_ACCESS_ALLOWED) + return false; + return true; +} + +static void __check_privilege_deinitialize() +{ + if (p_cynara) + cynara_finish(p_cynara); + p_cynara = NULL; +} + +static int __ma_ap_check_recorder_privilege() +{ + char uid[16]; + + if (0 == g_recorder_privilege_allowed) { + //LCOV_EXCL_START + MAAP_SLOGE("[ERROR] Permission for recording is denied"); + return MA_ERROR_PERMISSION_DENIED; + //LCOV_EXCL_STOP + } else if (-1 == g_recorder_privilege_allowed) { + if (false == __check_privilege_initialize()) { + MAAP_SLOGE("[ERROR] privilege initialize is failed"); //LCOV_EXCL_LINE + return MA_ERROR_PERMISSION_DENIED; + } + snprintf(uid, 16, "%d", getuid()); + if (false == __check_privilege(uid, MA_RECORDER_PRIVILEGE)) { + MAAP_SLOGE("[ERROR] Permission for recording is denied"); + g_recorder_privilege_allowed = 0; + __check_privilege_deinitialize(); + return MA_ERROR_PERMISSION_DENIED; + } + __check_privilege_deinitialize(); + } + + g_recorder_privilege_allowed = 1; + return MA_ERROR_NONE; +} + +int ma_ap_initialize(void) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + if (0 != __ma_ap_check_recorder_privilege()) { + return MA_ERROR_PERMISSION_DENIED; + } + + MAAP_SLOGI("[Client DEBUG] Initialize"); + + /* check handle */ + if (true == ma_ap_client_is_valid(g_ma_ap)) { + MAAP_SLOGD("[DEBUG] Already initialized"); //LCOV_EXCL_LINE + return MA_ERROR_NONE; //LCOV_EXCL_LINE + } + + if (0 < ma_ap_client_get_count()) { + MAAP_SLOGD("[DEBUG] Already initialized"); //LCOV_EXCL_LINE + return MA_ERROR_NONE; //LCOV_EXCL_LINE + } + + if (0 != ma_ap_dbus_open_connection()) { + MAAP_SLOGE("[ERROR] Fail to open connection"); //LCOV_EXCL_LINE + return MA_ERROR_OPERATION_FAILED; //LCOV_EXCL_LINE + } + + if (0 != ma_ap_client_create(&g_ma_ap)) { + MAAP_SLOGE("[ERROR] Fail to create client!!!!!"); //LCOV_EXCL_LINE + return MA_ERROR_OUT_OF_MEMORY; //LCOV_EXCL_LINE + } + + return MA_ERROR_NONE; +} + +static void __ma_ap_internal_unprepare(void) +{ + return; +} + +int ma_ap_deinitialize(void) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + if (0 != __ma_ap_check_recorder_privilege()) { + return MA_ERROR_PERMISSION_DENIED; + } + + MAAP_SLOGI("[Client DEBUG] Deinitialize"); + + if (false == ma_ap_client_is_valid(g_ma_ap)) { + MAAP_SLOGE("[ERROR] NOT initialized"); + return MA_ERROR_INVALID_STATE; + } + + ma_state_e state; + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + } + + /* check state */ + switch (state) { + case MA_STATE_READY: + __ma_ap_internal_unprepare(); + /* no break. need to next step*/ + case MA_STATE_INITIALIZED: + /* Free client resources */ + ma_ap_client_destroy(g_ma_ap); + g_ma_ap = NULL; + break; + case MA_STATE_NONE: + break; + } + + MAAP_SLOGD("Success: destroy"); + + if (0 != ma_ap_dbus_close_connection()) { + MAAP_SLOGE("[ERROR] Fail to close connection"); //LCOV_EXCL_LINE + } + + return MA_ERROR_NONE; +} + +int ma_ap_prepare(void) +{ + return MA_ERROR_NONE; +} + +int ma_ap_unprepare(void) +{ + return MA_ERROR_NONE; +} + +static Eina_Bool __ma_ap_notify_state_changed(void* data) +{ + ma_state_changed_cb callback = NULL; + void* user_data; + + ma_ap_client_get_state_changed_cb(g_ma_ap, &callback, &user_data); + + ma_state_e current_state; + ma_state_e previous_state; + + ma_ap_client_get_previous_state(g_ma_ap, ¤t_state, &previous_state); + + if (NULL != callback) { + ma_ap_client_use_callback(g_ma_ap); + callback(previous_state, current_state, user_data); + ma_ap_client_not_use_callback(g_ma_ap); + MAAP_SLOGD("[DEBUG] State changed callback is called"); + } else { + MAAP_SLOGD("[WARNING] State changed callback is NULL"); + } + + return EINA_FALSE; +} + +int __ma_ap_cb_audio_streaming(int event, char* buffer, int len) +{ + ma_audio_streaming_cb callback = NULL; + void* user_data; + +#ifdef BUF_SAVE_MODE + static char g_temp_file_name[128] = {'\0',}; + static FILE* g_pFile = NULL; + static int g_count = 1; + if (MA_AUDIO_STREAMING_EVENT_START == event) { + if (g_pFile) { + fclose(g_pFile); + g_pFile = NULL; + } else { + MA_LOGD("[Recorder Info] File not found!"); + } + + while (1) { + snprintf(g_temp_file_name, sizeof(g_temp_file_name), "/tmp/ma_%d_%d", getpid(), g_count); + int ret = access(g_temp_file_name, 0); + + if (0 == ret) { + MA_LOGD("[Recorder ERROR] File is already exist"); + if (0 == remove(g_temp_file_name)) { + MA_LOGD("[Recorder] Remove file"); + break; + } else { + g_count++; + } + } else { + break; + } + } + + MA_LOGD("[Recorder] Temp file name=[%s]", g_temp_file_name); + + /* open test file */ + g_pFile = fopen(g_temp_file_name, "wb+x"); + if (!g_pFile) { + MA_LOGD("[Recorder ERROR] File not found!"); + } + g_count++; + } + + if (g_pFile) + fwrite(buffer, 1, len, g_pFile); + + if (MA_AUDIO_STREAMING_EVENT_FINISH == event) { + if (g_pFile) { + MA_LOGE("[Recorder SUCCESS] File Close"); + fclose(g_pFile); + g_pFile = NULL; + } else { + MA_LOGE("[Recorder ERROR] File not found!"); + } + } +#endif + + ma_ap_client_get_audio_streaming_cb(g_ma_ap, &callback, &user_data); + + if (NULL != callback) { + static ma_audio_streaming_event_e last_event = MA_AUDIO_STREAMING_EVENT_FAIL; + ma_ap_client_use_callback(g_ma_ap); + callback((ma_audio_streaming_event_e)event, buffer, len, user_data); + ma_ap_client_not_use_callback(g_ma_ap); + if (last_event != event) { + last_event = event; + MAAP_SLOGI("[INFO] Audio streaming callback is called, event(%d)", event); + } + } else { + MAAP_SLOGD("[WARNING] Audio streaming callback is NULL"); + } + + return 0; +} + +int __ma_ap_cb_error(int reason, char* msg) +{ + ma_state_e state; + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] Invalid client"); + return MA_ERROR_INVALID_PARAMETER; + } + + /* check state */ + if (state != MA_STATE_READY) { + MAAP_SLOGD("[DEBUG] Current state is not 'READY'"); //LCOV_EXCL_LINE + if (MA_ERROR_SERVICE_RESET != reason) { + MAAP_SLOGE("[ERROR] client is not connected yet"); + return MA_ERROR_INVALID_STATE; + } + return MA_ERROR_NONE; + } + + if (MA_ERROR_SERVICE_RESET == reason) { + MAAP_SLOGE("[ERROR] multi-assistant service reset"); + + ma_ap_client_set_client_state(g_ma_ap, MA_STATE_INITIALIZED); + __ma_ap_notify_state_changed((void*)g_ma_ap); + } + + MAAP_SLOGE("[ERROR] Error reason(%d), msg(%s)", reason, msg); + + ma_ap_client_set_error(g_ma_ap, reason); + __ma_ap_notify_error((void*)g_ma_ap); + + if (MA_ERROR_SERVICE_RESET == reason) { + if (0 != ma_ap_prepare()) { + MAAP_SLOGE("[ERROR] Fail to prepare"); + } + } + + return MA_ERROR_NONE; +} + +static void __ma_ap_notify_error(void* data) +{ + ma_error_cb callback = NULL; + void* user_data; + ma_error_e reason; + + ma_ap_client_get_error_cb(g_ma_ap, &callback, &user_data); + ma_ap_client_get_error(g_ma_ap, &reason); + + if (NULL != callback) { + ma_ap_client_use_callback(g_ma_ap); + callback(reason, user_data); + ma_ap_client_not_use_callback(g_ma_ap); + MAUI_SLOGD("[DEBUG] Error callback is called"); + } else { + MAUI_SLOGD("[WARNING] Error callback is NULL"); + } +} + +int __ma_ap_cb_audio_streaming_data_section_changed(ma_audio_streaming_data_section_e section) +{ + ma_audio_streaming_data_section_changed_cb callback = NULL; + void* user_data; + + ma_ap_client_get_audio_streaming_data_section_changed_cb(g_ma_ap, &callback, &user_data); + + if (NULL != callback) { + ma_ap_client_use_callback(g_ma_ap); + callback(section, user_data); + ma_ap_client_not_use_callback(g_ma_ap); + MAAP_SLOGD("[DEBUG] Audio streaming data section changed callback is called, (%d)", section); + } else { + MAAP_SLOGD("[WARNING] Audio streaming data section changed callback is NULL"); + } + + return 0; +} + +int ma_ap_set_error_cb(ma_error_cb callback, void* user_data) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Set Multi-assistant client error cb"); + + + if (NULL == callback) { + MAAP_SLOGE("[ERROR] Invalid parameter"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_PARAMETER; + } + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized' (%d)", state); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_error_cb(g_ma_ap, callback, user_data); + + return MA_ERROR_NONE; +} + +int ma_ap_unset_error_cb(void) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Unset Multi-assistant client error cb"); + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized' (%d)", state); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_error_cb(g_ma_ap, NULL, NULL); + + return MA_ERROR_NONE; +} + +int ma_ap_set_audio_streaming_cb(ma_audio_streaming_cb callback, void* user_data) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Set Multi-assistant audio streaming cb"); + + if (NULL == callback) { + MAAP_SLOGE("[ERROR] Invalid parameter"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_PARAMETER; + } + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized' (%d)", state); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_audio_streaming_cb(g_ma_ap, callback, user_data); + + return MA_ERROR_NONE; +} + +int ma_ap_unset_audio_streaming_cb(void) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Unset Multi-assistant audio streaming cb"); + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized'"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_audio_streaming_cb(g_ma_ap, NULL, NULL); + + return MA_ERROR_NONE; +} + + +int ma_ap_set_audio_streaming_data_section_changed_cb(ma_audio_streaming_data_section_changed_cb callback, void* user_data) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Set Multi-assistant audio streaming data section changed cb"); + + if (NULL == callback) { + MAAP_SLOGE("[ERROR] Invalid parameter"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_PARAMETER; + } + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized' (%d)", state); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_audio_streaming_data_section_changed_cb(g_ma_ap, callback, user_data); + + return MA_ERROR_NONE; +} + +int ma_ap_unset_audio_streaming_data_section_changed_cb(void) +{ + if (0 != __ma_ap_get_feature_enabled()) { + return MA_ERROR_NOT_SUPPORTED; + } + + MAAP_SLOGD("[Client DEBUG] Unset Multi-assistant audio streaming data section changed cb"); + + ma_state_e state; + + if (0 != ma_ap_client_get_client_state(g_ma_ap, &state)) { + MAAP_SLOGE("[ERROR] A handle is not available"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + /* check state */ + if (state != MA_STATE_INITIALIZED) { + MAAP_SLOGE("[ERROR] Invalid State: Current state is not 'Initialized'"); //LCOV_EXCL_LINE + return MA_ERROR_INVALID_STATE; + } + + ma_ap_client_set_audio_streaming_data_section_changed_cb(g_ma_ap, NULL, NULL); + + return MA_ERROR_NONE; +} + +//LCOV_EXCL_STOP diff --git a/client/ma_ap_client.c b/client/ma_ap_client.c new file mode 100644 index 0000000..f4dcf24 --- /dev/null +++ b/client/ma_ap_client.c @@ -0,0 +1,376 @@ +/** + * Copyright (c) 2020 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 "ma_ap_client.h" + + +typedef struct { + ma_h ma; + int pid; + int uid; /*<< unique id = pid + handle */ + + ma_error_cb error_cb; + void* error_user_data; + ma_state_changed_cb state_changed_cb; + void* state_changed_user_data; + ma_audio_streaming_cb audio_streaming_cb; + void* audio_streaming_user_data; + ma_audio_streaming_data_section_changed_cb audio_streaming_data_section_changed_cb; + void* audio_streaming_data_section_changed_user_data; + + /* state */ + ma_state_e previous_state; + ma_state_e current_state; + + /* mutex */ + int cb_ref_count; + + /* error data */ + int reason; + char* err_msg; +} ma_ap_client_s; + +static GSList* g_ap_client_list = NULL; + +static ma_ap_client_s* __ap_client_get(ma_h ma) +{ + if (NULL == ma) { + MAAP_SLOGE("[ERROR] Input parameter is NULL"); + return NULL; + } + + if (NULL == g_ap_client_list) { + MAAP_SLOGE("[ERROR] Client List is NULL"); + return NULL; + } + + ma_ap_client_s* data = NULL; + int count = g_slist_length(g_ap_client_list); + int i; + + for (i = 0 ; i < count ; i++) { + data = g_slist_nth_data(g_ap_client_list, i); + + if (NULL != data) { + if (ma->handle == data->ma->handle) { + return data; + } + } + } + + MAAP_SLOGD("[DEBUG] Fail to get ui client"); + return NULL; +} + +int ma_ap_client_create(ma_h* ma) +{ + ma_ap_client_s *client = NULL; + + client = (ma_ap_client_s*)calloc(1, sizeof(ma_ap_client_s)); + if (NULL == client) { + MAAP_SLOGE("[ERROR] Fail to allocate memory"); + return MA_ERROR_OUT_OF_MEMORY; + } + + ma_h temp = (ma_h)calloc(1, sizeof(struct ma_s)); + if (NULL == temp) { + MAAP_SLOGE("[ERROR] Fail to allocate memory"); + free(client); + return MA_ERROR_OUT_OF_MEMORY; + } + + temp->handle = getpid(); + + /* initialize client data */ + client->ma = temp; + client->pid = getpid(); + client->uid = temp->handle; + + client->error_cb = NULL; + client->error_user_data = NULL; + client->state_changed_cb = NULL; + client->state_changed_user_data = NULL; + client->audio_streaming_cb = NULL; + client->audio_streaming_user_data = NULL; + client->audio_streaming_data_section_changed_cb = NULL; + client->audio_streaming_data_section_changed_user_data = NULL; + + client->previous_state = MA_STATE_INITIALIZED; + client->current_state = MA_STATE_INITIALIZED; + + client->reason = 0; + client->err_msg = NULL; + + client->cb_ref_count = 0; + + g_ap_client_list = g_slist_append(g_ap_client_list, client); + + *ma = temp; + + return 0;} + +int ma_ap_client_destroy(ma_h ma) +{ + if (ma == NULL) { + MAAP_SLOGE("Input parameter is NULL"); + return 0; + } + + ma_ap_client_s *data = NULL; + + int count = g_slist_length(g_ap_client_list); + int i; + + for (i = 0; i < count; i++) { + data = g_slist_nth_data(g_ap_client_list, i); + + if (NULL != data) { + if (ma->handle == data->ma->handle) { + g_ap_client_list = g_slist_remove(g_ap_client_list, data); + + while (0 != data->cb_ref_count) { + /* wait for release callback function */ + } + + if (NULL != data->err_msg) { + free(data->err_msg); + } + + free(data); + free(ma); + + data = NULL; + ma = NULL; + + return 0; + } + } + } + + MAAP_SLOGE("[ERROR] client Not found"); + + return -1;} + +bool ma_ap_client_is_valid(ma_h ma) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) { + MAAP_SLOGE("[ERROR] ma handle is not valid"); + return false; + } + + return true; +} + +int ma_ap_client_get_count(void) +{ + return g_slist_length(g_ap_client_list); +} + +int ma_ap_client_use_callback(ma_h ma) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->cb_ref_count++; + + return MA_ERROR_NONE; +} + +int ma_ap_client_not_use_callback(ma_h ma) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->cb_ref_count--; + + return MA_ERROR_NONE; +} + + +int ma_ap_client_set_client_state(ma_h ma, ma_state_e state) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->previous_state = client->current_state; + client->current_state = state; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_client_state(ma_h ma, ma_state_e* state) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *state = client->current_state; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_previous_state(ma_h ma, ma_state_e* current_state, ma_state_e* previous_state) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + if (current_state) *current_state = client->current_state; + if (previous_state) *previous_state = client->previous_state; + + return MA_ERROR_NONE; +} + +int ma_ap_client_set_error(ma_h ma, ma_error_e reason) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->reason = reason; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_error(ma_h ma, ma_error_e* reason) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *reason = client->reason; + + return MA_ERROR_NONE; +} + +int ma_ap_client_set_error_cb(ma_h ma, ma_error_cb callback, void* user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->error_cb = callback; + client->error_user_data = user_data; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_error_cb(ma_h ma, ma_error_cb* callback, void** user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *callback = client->error_cb; + *user_data = client->error_user_data; + + return MA_ERROR_NONE; +} + +int ma_ap_client_set_state_changed_cb(ma_h ma, ma_state_changed_cb callback, void* user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->state_changed_cb = callback; + client->state_changed_user_data = user_data; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_state_changed_cb(ma_h ma, ma_state_changed_cb* callback, void** user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *callback = client->state_changed_cb; + *user_data = client->state_changed_user_data; + + return MA_ERROR_NONE; +} + +int ma_ap_client_set_audio_streaming_cb(ma_h ma, ma_audio_streaming_cb callback, void* user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->audio_streaming_cb = callback; + client->audio_streaming_user_data = user_data; + + return MA_ERROR_NONE; +} + +int ma_ap_client_get_audio_streaming_cb(ma_h ma, ma_audio_streaming_cb* callback, void** user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *callback = client->audio_streaming_cb; + *user_data = client->audio_streaming_user_data; + + return MA_ERROR_NONE; +} +//LCOV_EXCL_STOP + +int ma_ap_client_set_audio_streaming_data_section_changed_cb(ma_h ma, ma_audio_streaming_data_section_changed_cb callback, void* user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + client->audio_streaming_data_section_changed_cb = callback; + client->audio_streaming_data_section_changed_user_data = user_data; + + return MA_ERROR_NONE; +} + +//LCOV_EXCL_START +int ma_ap_client_get_audio_streaming_data_section_changed_cb(ma_h ma, ma_audio_streaming_data_section_changed_cb* callback, void** user_data) +{ + ma_ap_client_s* client = __ap_client_get(ma); + + if (NULL == client) + return MA_ERROR_INVALID_PARAMETER; + + *callback = client->audio_streaming_data_section_changed_cb; + *user_data = client->audio_streaming_data_section_changed_user_data; + + return MA_ERROR_NONE; +} diff --git a/client/ma_ap_client.h b/client/ma_ap_client.h new file mode 100644 index 0000000..2f81656 --- /dev/null +++ b/client/ma_ap_client.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2020 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. + */ + + +#ifndef __MA_AP_CLIENT_H__ +#define __MA_AP_CLIENT_H__ + +#include "ma_main.h" +#include "multi_assistant_common.h" +#include "multi_assistant_ui.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +int ma_ap_client_create(ma_h* ma); + +int ma_ap_client_destroy(ma_h ma); + +bool ma_ap_client_is_valid(ma_h ma); + +int ma_ap_client_get_count(void); + +int ma_ap_client_use_callback(ma_h ma); + +int ma_ap_client_not_use_callback(ma_h ma); + + +int ma_ap_client_set_client_state(ma_h ma, ma_state_e state); + +int ma_ap_client_get_client_state(ma_h ma, ma_state_e* state); + +int ma_ap_client_get_previous_state(ma_h ma, ma_state_e* current_state, ma_state_e* previous_state); + +int ma_ap_client_set_error(ma_h ma, ma_error_e reason); + +int ma_ap_client_get_error(ma_h ma, ma_error_e* reason); + + +int ma_ap_client_set_error_cb(ma_h ma, ma_error_cb callback, void* user_data); + +int ma_ap_client_get_error_cb(ma_h ma, ma_error_cb* callback, void** user_data); + +int ma_ap_client_set_state_changed_cb(ma_h ma, ma_state_changed_cb callback, void* user_data); + +int ma_ap_client_get_state_changed_cb(ma_h ma, ma_state_changed_cb* callback, void** user_data); + +int ma_ap_client_set_audio_streaming_cb(ma_h ma, ma_audio_streaming_cb callback, void* user_data); + +int ma_ap_client_get_audio_streaming_cb(ma_h ma, ma_audio_streaming_cb* callback, void** user_data); + +int ma_ap_client_set_audio_streaming_data_section_changed_cb(ma_h ma, ma_audio_streaming_data_section_changed_cb callback, void* user_data); + +int ma_ap_client_get_audio_streaming_data_section_changed_cb(ma_h ma, ma_audio_streaming_data_section_changed_cb* callback, void** user_data); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __MA_AP_CLIENT_H__ */ + diff --git a/client/ma_ap_dbus.c b/client/ma_ap_dbus.c new file mode 100644 index 0000000..9d3e267 --- /dev/null +++ b/client/ma_ap_dbus.c @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2020 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 "ma_ap_dbus.h" +#include "ma_defs.h" +#include "ma_main.h" + +#include + +static bool g_streaming_requested = false; + +extern int __ma_ap_cb_error(int reason, char* msg); +extern int __ma_ap_cb_audio_streaming(int event, char* buffer, int len); +extern int __ma_ap_cb_audio_streaming_data_section_changed(ma_audio_streaming_data_section_e section); + +#define STREAMING_BUFFER_SIZE 4096 + +typedef enum { + streaming_data_type_audio_data, + streaming_data_type_streaming_section +} streaming_data_type_e; + +typedef struct { + unsigned int streaming_data_size; + int streaming_data_type; + int streaming_data_serial; +} streaming_data_header; + +typedef struct { + int event; + unsigned int data_size; +} streaming_data_audio_data_header; + +typedef struct { + int section; +} streaming_data_streaming_section_header; + +const char *message_port = "ma_streaming_port"; +static int g_local_port_id = -1; + +static void message_port_cb(int local_port_id, + const char *remote_app_id, const char *remote_port, + bool trusted_remote_port, bundle *message, void *user_data) +{ + static char pending_buffer[STREAMING_BUFFER_SIZE * 2]; + static unsigned int pending_buffer_size = 0; + + char buffer[STREAMING_BUFFER_SIZE]; + size_t size; + + unsigned char *v = NULL; + bundle_get_byte(message, "content", (void**)(&v), &size); + + if (size > STREAMING_BUFFER_SIZE) { + MA_SLOGE("[ERROR] bundle contains data bigger than %d : %zu", STREAMING_BUFFER_SIZE, size); + return; + } else { + memcpy(buffer, v, size); + } + + memcpy(pending_buffer + pending_buffer_size, buffer, size); + pending_buffer_size += size; + + while (pending_buffer_size >= sizeof(streaming_data_header)) { + streaming_data_header header; + memcpy(&header, pending_buffer, sizeof(streaming_data_header)); + if (pending_buffer_size >= header.streaming_data_size) { + static int last_serial = 0; + if (header.streaming_data_serial != last_serial + 1) { + MA_SLOGE("[ERROR] SERIAL MISMATCH in [%d] : %d => %d", + header.streaming_data_type, last_serial, header.streaming_data_serial); + } + last_serial = header.streaming_data_serial; + + if (streaming_data_type_audio_data == header.streaming_data_type) { + streaming_data_audio_data_header audio_data_header; + memcpy(&audio_data_header, pending_buffer + sizeof(streaming_data_header), + sizeof(streaming_data_audio_data_header)); + + /* Don't invoke audio streaming callback for CONTINUE audio events + if the streaming request is no more valid, since it would not provide + any useful information to the client application */ + if (g_streaming_requested || MA_AUDIO_STREAMING_EVENT_CONTINUE != audio_data_header.event) { + __ma_ap_cb_audio_streaming(audio_data_header.event, + pending_buffer + sizeof(streaming_data_header) + sizeof(streaming_data_audio_data_header), + audio_data_header.data_size); + if (0 == header.streaming_data_serial % 50) { + MA_SLOGI("__ma_ap_cb_audio_streaming() called, serial : %d", header.streaming_data_serial); + } + } + } else if (streaming_data_type_streaming_section == header.streaming_data_type) { + streaming_data_streaming_section_header streaming_section_header; + memcpy(&streaming_section_header, pending_buffer + sizeof(streaming_data_header), + sizeof(streaming_data_streaming_section_header)); + + __ma_ap_cb_audio_streaming_data_section_changed(streaming_section_header.section); + } + + memmove(pending_buffer, pending_buffer + header.streaming_data_size, + sizeof(pending_buffer) - header.streaming_data_size); + pending_buffer_size -= header.streaming_data_size; + } else { + break; + } + } +} + +static int streaming_ipc_initialize() +{ + int port_id = message_port_register_local_port(message_port, message_port_cb, NULL); + if (port_id < 0) { + MA_SLOGD("Port register error: %d", port_id); + } else { + MA_SLOGD("port_id: %d", port_id); + g_local_port_id = port_id; + } + + return 0; +} + +static int streaming_ipc_deinitialize() +{ + if (-1 != g_local_port_id) message_port_unregister_local_port(g_local_port_id); + g_local_port_id = -1; + return 0; +} + +int ma_ap_dbus_open_connection() +{ + /* For now assume streaming is requested when initialized */ + g_streaming_requested = true; + return streaming_ipc_initialize(); +} + +int ma_ap_dbus_close_connection() +{ + g_streaming_requested = false; + return streaming_ipc_deinitialize(); +} + diff --git a/client/ma_ap_dbus.h b/client/ma_ap_dbus.h new file mode 100644 index 0000000..f565e06 --- /dev/null +++ b/client/ma_ap_dbus.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2020 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. + */ + + +#ifndef __MA_AP_DBUS_H__ +#define __MA_AP_DBUS_H__ + +#include "multi_assistant_common.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +int ma_ap_dbus_open_connection(); + +int ma_ap_dbus_close_connection(); + + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __MA_AP_DBUS_H__ */ + diff --git a/common/ma_main.h b/common/ma_main.h index 0027fbd..dce2337 100644 --- a/common/ma_main.h +++ b/common/ma_main.h @@ -40,6 +40,7 @@ extern "C" *************************************************************************************/ #define TAG_MAC "mac" /* Multi-assistant client log tag */ #define TAG_MAUI "maui" /* Multi-assistant UI client log tag */ +#define TAG_MAAP "maap" /* Multi-assistant AP client log tag */ /************************************************************************************** *** Logging macros @@ -74,6 +75,16 @@ extern "C" #define MAUI_SLOGW(fmt, args...) MA_SECURE_LOG_(DLOG_WARN, TAG_MAUI, fmt, ##args) #define MAUI_SLOGE(fmt, args...) MA_SECURE_LOG_(DLOG_ERROR, TAG_MAUI, fmt, ##args) +#define MAAP_LOGD(fmt, args...) MA_LOG_(DLOG_DEBUG, TAG_MAAP, fmt, ##args) +#define MAAP_LOGI(fmt, args...) MA_LOG_(DLOG_INFO, TAG_MAAP, fmt, ##args) +#define MAAP_LOGW(fmt, args...) MA_LOG_(DLOG_WARN, TAG_MAAP, fmt, ##args) +#define MAAP_LOGE(fmt, args...) MA_LOG_(DLOG_ERROR, TAG_MAAP, fmt, ##args) + +#define MAAP_SLOGD(fmt, args...) MA_SECURE_LOG_(DLOG_DEBUG, TAG_MAAP, fmt, ##args) +#define MAAP_SLOGI(fmt, args...) MA_SECURE_LOG_(DLOG_INFO, TAG_MAAP, fmt, ##args) +#define MAAP_SLOGW(fmt, args...) MA_SECURE_LOG_(DLOG_WARN, TAG_MAAP, fmt, ##args) +#define MAAP_SLOGE(fmt, args...) MA_SECURE_LOG_(DLOG_ERROR, TAG_MAAP, fmt, ##args) + /************************************************************************************** *** Structures for multi-assistant handle *************************************************************************************/ diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 2d2056a..923b3b3 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,14 +1,17 @@ ## configure pkgconfig files ## CONFIGURE_FILE(multi-assistant.pc.in multi-assistant.pc @ONLY) CONFIGURE_FILE(multi-assistant-ui.pc.in multi-assistant-ui.pc @ONLY) +CONFIGURE_FILE(multi-assistant-ap.pc.in multi-assistant-ap.pc @ONLY) ## Install pc files ## INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi-assistant.pc DESTINATION ${LIBDIR}/pkgconfig) INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi-assistant-ui.pc DESTINATION ${LIBDIR}/pkgconfig) +INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi-assistant-ap.pc DESTINATION ${LIBDIR}/pkgconfig) ## Install header files ## INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant.h DESTINATION ${INCLUDEDIR}) INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant_ui.h DESTINATION ${INCLUDEDIR}) +INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant_ap.h DESTINATION ${INCLUDEDIR}) INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant_common.h DESTINATION ${INCLUDEDIR}) INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant_settings.h DESTINATION ${INCLUDEDIR}) INSTALL(FILES ${CMAKE_BINARY_DIR}/include/multi_assistant_internal.h DESTINATION ${INCLUDEDIR}) diff --git a/packaging/multi-assistant.spec b/packaging/multi-assistant.spec index 9f4477a..3aa6f4c 100644 --- a/packaging/multi-assistant.spec +++ b/packaging/multi-assistant.spec @@ -51,6 +51,15 @@ Requires: %{name} = %{version}-%{release} Multi assistant manager header files for MA development. +%package ap-devel +Summary: Multi assistant manager header files for MA AP development +Group: libdevel +Requires: %{name} = %{version}-%{release} + +%description ap-devel +Multi assistant manager header files for MA AP development. + + %if 0%{?gcov:1} %package gcov Summary: Multi assistant (gcov) @@ -106,6 +115,7 @@ mkdir -p %{_libdir}/multiassistant/ma %defattr(-,root,root,-) %{_libdir}/libma.so %{_libdir}/libma_ui.so +%{_libdir}/libma_ap.so %{TZ_SYS_RO_SHARE}/multiassistant/ma/1.0/ma-config.xml %{TZ_SYS_RO_SHARE}/dbus-1/services/org.tizen.multiassistant* %{TZ_SYS_RO_SHARE}/parser-plugins/multi-assistant.info @@ -117,6 +127,7 @@ mkdir -p %{_libdir}/multiassistant/ma %{_libdir}/pkgconfig/multi-assistant.pc %{_includedir}/multi_assistant.h %{_includedir}/multi_assistant_ui.h +%{_includedir}/multi_assistant_ap.h %{_includedir}/multi_assistant_common.h %{_includedir}/multi_assistant_settings.h %{_includedir}/multi_assistant_internal.h @@ -129,6 +140,12 @@ mkdir -p %{_libdir}/multiassistant/ma %{_includedir}/multi_assistant_common.h %{_includedir}/multi_assistant_settings.h +%files ap-devel +%defattr(-,root,root,-) +%{_libdir}/pkgconfig/multi-assistant-ap.pc +%{_includedir}/multi_assistant_ap.h +%{_includedir}/multi_assistant_common.h + %if 0%{?gcov:1} %files gcov %{_datadir}/gcov/obj/*