From e41c7e41feebc46fdf8c0724517fb32e6aff9ef4 Mon Sep 17 00:00:00 2001 From: Hwankyu Jhun Date: Thu, 13 Sep 2018 12:19:11 +0900 Subject: [PATCH] Support launch request async API Change-Id: I834327b75468767e0fe4867f4a4b843ec22c913b Signed-off-by: Hwankyu Jhun --- include/app_control.h | 49 +++++++ src/app_control.c | 317 +++++++++++++++++++++++++++++++++--------- 2 files changed, 299 insertions(+), 67 deletions(-) diff --git a/include/app_control.h b/include/app_control.h index 52fcfd3..7a55be7 100755 --- a/include/app_control.h +++ b/include/app_control.h @@ -692,6 +692,18 @@ typedef bool (*app_control_extra_data_cb)(app_control_h app_control, const char */ typedef bool (*app_control_app_matched_cb)(app_control_h app_control, const char *appid, void *user_data); +/** + * @brief Called when the result of the launch request is delivered. + * + * @since_tizen 5.0 + * @remakrs The @a request is the same object for which app_control_send_launch_request_async() was called. + * @param[in] request The app_control handle of the launch request that has been sent + * @param[in] result The result value of the launch request + * @param[in] user_data The user data passed from the callback registration function + * @see app_control_send_launch_request_async() + */ +typedef void (*app_control_result_cb)(app_control_h request, app_control_result_e result, void *user_data); + typedef int (*app_control_host_res_fn)(void *data); @@ -1250,6 +1262,43 @@ int app_control_get_launch_mode(app_control_h app_control, */ int app_control_enable_app_started_result_event(app_control_h app_control); +/** + * @brief Sends the launch request asynchronously. + * + * @details The operation is mandatory information for the launch request. \n + * If the operation is not specified, #APP_CONTROL_OPERATION_DEFAULT is used by default. + * If the operation is #APP_CONTROL_OPERATION_DEFAULT, the application ID is mandatory to explicitly launch the application. + * @details After the callee application is initialized or the launch request is delivered to the running application successfully, the result callback function will be invoked. + * @since_tizen 5.0 + * @privlevel public + * @privilege %http://tizen.org/privilege/appmanager.launch + * @remarks The function returns #APP_CONTROL_ERROR_LAUNCH_REJECTED if the operation value is #APP_CONTROL_OPERATION_LAUNCH_ON_EVENT which is only for handling the event from the platform or other application, refer to the @a Event Module. + * @remarks The launch request of the service application over out of packages is restricted by the platform. Also, implicit launch requests are NOT delivered to service applications since @if MOBILE 2.4 @elseif WEARABLE 3.0 @endif. To launch a service application, an explicit launch request with application ID given by the app_control_set_app_id() must be sent. + * @param[in] app_control The app_control handle + * @param[in] result_cb The callback function to be called when the result is delivered + * @param[in] reply_cb The callback function to be called when the reply is delivered + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value + * @retval #APP_CONTROL_ERROR_NONE Successful + * @retval #APP_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #APP_CONTROL_ERROR_OUT_OF_MEMORY Out of memory + * @retval #APP_CONTROL_ERROR_APP_NOT_FOUND The application to run the given launch request is not found + * @retval #APP_CONTROL_ERROR_LAUNCH_REJECTED The application cannot be launched in current context + * @retval #APP_CONTROL_ERROR_LAUNCH_FAILED Failed to launch the application + * @retval #APP_CONTROL_ERROR_TIMED_OUT Failed due to timeout. The application that handles @a app_control may be busy + * @retval #APP_CONTROL_ERROR_PERMISSION_DENIED Permission denied + * @post If the launch request is sent for the result, the result will come back through the app_control_reply_cb() from the callee application. Additional replies may be delivered if app_control_enable_app_started_result_event() was called. + * @see app_control_result_cb() + * @see app_control_reply_to_launch_request() + * @see app_control_reply_cb() + * @see app_control_enable_app_started_result_event() + */ +int app_control_send_launch_request_async(app_control_h app_control, + app_control_result_cb result_cb, + app_control_reply_cb reply_cb, + void *user_data); + /** * @} */ diff --git a/src/app_control.c b/src/app_control.c index 1badb0b..c645d92 100644 --- a/src/app_control.c +++ b/src/app_control.c @@ -54,6 +54,8 @@ #define LAUNCH_MODE_SINGLE "single" #define LAUNCH_MODE_GROUP "group" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + typedef enum { APP_CONTROL_TYPE_REQUEST, APP_CONTROL_TYPE_EVENT, @@ -69,10 +71,22 @@ struct app_control_s { typedef struct app_control_request_context_s { app_control_h app_control; + app_control_result_cb result_cb; app_control_reply_cb reply_cb; void *user_data; } *app_control_request_context_h; +struct launch_request_s { + bool implicit_default_operation; + app_control_request_context_h request_context; + app_control_h app_control; + app_control_result_cb result_cb; + app_control_reply_cb reply_cb; + void *user_data; +}; + +typedef int (*launch_request_handler)(struct launch_request_s *req); + static int app_control_create_reply(bundle *data, struct app_control_s **app_control); static const char *app_control_error_to_string(app_control_error_e error) @@ -155,7 +169,7 @@ int app_control_validate_internal_key(const char *key) } /* LCOV_EXCL_START */ -static void app_control_request_result_broker(bundle *appsvc_bundle, int appsvc_request_code, aul_svc_result_val appsvc_result, void *appsvc_data) +static void app_control_request_reply_broker(bundle *appsvc_bundle, int appsvc_request_code, aul_svc_result_val appsvc_result, void *appsvc_data) { app_control_request_context_h request_context; app_control_h request; @@ -708,105 +722,256 @@ static void __handle_launch_result(int launched_pid, void *data) app_control_destroy(reply); } -int app_control_send_launch_request(app_control_h app_control, app_control_reply_cb callback, void *user_data) +static app_control_error_e __launch_request_convert_error(int res) { - const char *operation; - bool implicit_default_operation = false; - int launch_pid; - app_control_request_context_h request_context = NULL; + switch (res) { + case AUL_SVC_RET_OK: + return APP_CONTROL_ERROR_NONE; + case AUL_SVC_RET_ENOMATCH: + return APP_CONTROL_ERROR_APP_NOT_FOUND; + case AUL_SVC_RET_EILLACC: + return APP_CONTROL_ERROR_PERMISSION_DENIED; + case AUL_SVC_RET_EINVAL: + return APP_CONTROL_ERROR_INVALID_PARAMETER; + default: + return APP_CONTROL_ERROR_LAUNCH_REJECTED; + } +} + +static void __handle_app_started_result(app_control_h app_control, + app_control_request_context_h request_context) +{ + char callee[256] = { 0, }; + const char *str; int ret; - if (app_control_validate(app_control)) - return app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); + str = bundle_get_val(app_control->data, + AUL_SVC_K_LAUNCH_RESULT_APP_STARTED); + if (!str) { + aul_add_caller_cb(app_control->launch_pid, + __update_launch_pid, app_control); + return; + } + + ret = aul_app_get_appid_bypid(app_control->launch_pid, + callee, sizeof(callee)); + if (ret != AUL_R_OK) { + LOGE("Failed to get appliation ID. pid(%d)", + app_control->launch_pid); + } + + aul_add_caller_cb(app_control->launch_pid, + __handle_launch_result, request_context); + + if (strcmp(callee, APP_SELECTOR) && + strcmp(callee, SHARE_PANEL)) + aul_invoke_caller_cb(request_context); +} + +static void app_control_request_result_broker(int request_code, int result, + void *user_data) +{ + app_control_request_context_h request_context; + app_control_error_e error = APP_CONTROL_ERROR_NONE; + app_control_h app_control; + + request_context = (app_control_request_context_h)user_data; + if (request_context == NULL) { + LOGE("Invalid request"); + return; + } + + app_control = request_context->app_control; + app_control->launch_pid = result; + if (request_context->result_cb) { + if (result < 0) + error = __launch_request_convert_error(result); + request_context->result_cb(app_control, error, + request_context->user_data); + request_context->result_cb = NULL; + } + + if (result > 0) { + __handle_app_started_result(app_control, + request_context->reply_cb ? request_context : NULL); + } + + if (result < 0 || !request_context->reply_cb) { + app_control_destroy(request_context->app_control); + free(request_context); + } +} + +static int __launch_request_verify_operation(struct launch_request_s *req) +{ + app_control_h app_control = req->app_control; + const char *operation; + const char *appid; operation = aul_svc_get_operation(app_control->data); - if (operation == NULL) { - implicit_default_operation = true; + if (!operation) { + req->implicit_default_operation = true; operation = APP_CONTROL_OPERATION_DEFAULT; } - if (!strcmp(operation, APP_CONTROL_OPERATION_LAUNCH_ON_EVENT)) - return app_control_error(APP_CONTROL_ERROR_LAUNCH_REJECTED, __FUNCTION__, - "Not supported operation value"); - - /* TODO: Check the privilege for call operation */ + if (!strcmp(operation, APP_CONTROL_OPERATION_LAUNCH_ON_EVENT)) { + return app_control_error(APP_CONTROL_ERROR_LAUNCH_REJECTED, + __FUNCTION__, "Not supported operation value"); + } - /* operation : default */ if (!strcmp(operation, APP_CONTROL_OPERATION_DEFAULT)) { - const char *appid = aul_svc_get_appid(app_control->data); - if (appid == NULL) - return app_control_error(APP_CONTROL_ERROR_APP_NOT_FOUND, __FUNCTION__, "package must be specified if the operation is default value"); + appid = aul_svc_get_appid(app_control->data); + if (!appid) { + return app_control_error(APP_CONTROL_ERROR_APP_NOT_FOUND, + __FUNCTION__, + "Application ID must be specified"); + } } - if (callback != NULL) { - app_control_h request_clone = NULL; + return APP_CONTROL_ERROR_NONE; +} - request_context = calloc(1, sizeof(struct app_control_request_context_s)); - if (request_context == NULL) - return app_control_error(APP_CONTROL_ERROR_OUT_OF_MEMORY, __FUNCTION__, NULL); +static int __launch_request_prepare_request_context(struct launch_request_s *req) +{ + app_control_h app_control = req->app_control; + app_control_h request_clone; + int ret; - request_context->reply_cb = callback; + if (!req->result_cb && !req->reply_cb) + return APP_CONTROL_ERROR_NONE; - ret = app_control_clone(&request_clone, app_control); - if (ret != APP_CONTROL_ERROR_NONE) { - free(request_context); - return app_control_error(ret, __FUNCTION__, "failed to clone the app_control request handle"); - } + req->request_context = calloc(1, + sizeof(struct app_control_request_context_s)); + if (!req->request_context) { + return app_control_error(APP_CONTROL_ERROR_OUT_OF_MEMORY, + __FUNCTION__, "Ouf of memory"); + } - request_context->app_control = request_clone; - request_context->user_data = user_data; + ret = app_control_clone(&request_clone, app_control); + if (ret != APP_CONTROL_ERROR_NONE) { + return app_control_error(ret, __FUNCTION__, + "Failed to duplicate app control handle"); } - if (implicit_default_operation == true) - aul_svc_set_operation(app_control->data, APP_CONTROL_OPERATION_DEFAULT); + req->request_context->app_control = request_clone; + req->request_context->result_cb = req->result_cb; + req->request_context->reply_cb = req->reply_cb; + req->request_context->user_data = req->user_data; - launch_pid = aul_svc_run_service_for_uid(app_control->data, app_control->id, callback ? app_control_request_result_broker : NULL, request_context, getuid()); - if (implicit_default_operation == true) - bundle_del(app_control->data, BUNDLE_KEY_OPERATION); + return APP_CONTROL_ERROR_NONE; +} - if (launch_pid < 0) { - if (request_context) { - if (request_context->app_control) - app_control_destroy(request_context->app_control); +static int __launch_request_send(struct launch_request_s *req) +{ + app_control_h app_control = req->app_control; + aul_svc_res_fn reply_cb; + aul_svc_err_cb result_cb; + int ret; - free(request_context); - } + reply_cb = req->reply_cb ? app_control_request_reply_broker : NULL; + result_cb = req->result_cb ? app_control_request_result_broker : NULL; - if (launch_pid == AUL_SVC_RET_ENOMATCH) - return app_control_error(APP_CONTROL_ERROR_APP_NOT_FOUND, __FUNCTION__, NULL); - else if (launch_pid == AUL_SVC_RET_EILLACC) - return app_control_error(APP_CONTROL_ERROR_PERMISSION_DENIED, __FUNCTION__, NULL); - else if (launch_pid == AUL_SVC_RET_EINVAL) - return app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, __FUNCTION__, NULL); - else - return app_control_error(APP_CONTROL_ERROR_LAUNCH_REJECTED, __FUNCTION__, NULL); + if (req->implicit_default_operation) { + aul_svc_set_operation(app_control->data, + APP_CONTROL_OPERATION_DEFAULT); } - app_control->launch_pid = launch_pid; - /* app_control_enable_app_started_result_event called */ - if (bundle_get_val(app_control->data, AUL_SVC_K_LAUNCH_RESULT_APP_STARTED)) { - char callee[255] = {0,}; - if (aul_app_get_appid_bypid(launch_pid, callee, sizeof(callee)) != AUL_R_OK) - LOGE("aul_app_get_appid_bypid failed: %d", launch_pid); + if (req->result_cb) { + ret = aul_svc_send_launch_request_for_uid(app_control->data, + app_control->id, reply_cb, result_cb, + req->request_context, getuid()); - if (request_context && request_context->app_control) - request_context->app_control->launch_pid = launch_pid; + } else { + ret = aul_svc_run_service_for_uid(app_control->data, + app_control->id, reply_cb, + req->request_context, getuid()); + } - aul_add_caller_cb(launch_pid, __handle_launch_result, request_context); + if (req->implicit_default_operation) + bundle_del(req->app_control->data, BUNDLE_KEY_OPERATION); - /* launched without app selector */ - if ((strcmp(callee, APP_SELECTOR) != 0) && - (strcmp(callee, SHARE_PANEL) != 0)) + if (ret < 0) { + return app_control_error(__launch_request_convert_error(ret), + __FUNCTION__, NULL); + } - aul_invoke_caller_cb(request_context); + app_control->launch_pid = ret; - } else { /* default case */ - aul_add_caller_cb(launch_pid, __update_launch_pid, app_control); - } + return APP_CONTROL_ERROR_NONE; +} + +static int __launch_request_complete(struct launch_request_s *req) +{ + app_control_h app_control = req->app_control; + app_control_request_context_h request_context = req->request_context; + + if (req->result_cb) + return APP_CONTROL_ERROR_NONE; + + if (!request_context) + return APP_CONTROL_ERROR_NONE; + + __handle_app_started_result(app_control, + request_context->reply_cb ? request_context : NULL); return APP_CONTROL_ERROR_NONE; } +static int __send_launch_request(app_control_h app_control, + app_control_result_cb result_cb, + app_control_reply_cb reply_cb, + void *user_data) +{ + static launch_request_handler handlers[] = { + __launch_request_verify_operation, + __launch_request_prepare_request_context, + __launch_request_send, + __launch_request_complete, + }; + struct launch_request_s req = { + .implicit_default_operation = false, + .request_context = NULL, + .app_control = app_control, + .result_cb = result_cb, + .reply_cb = reply_cb, + .user_data = user_data + }; + int ret; + int i; + + if (app_control_validate(app_control)) { + return app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, + __FUNCTION__, NULL); + } + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (handlers[i]) { + ret = handlers[i](&req); + if (ret != APP_CONTROL_ERROR_NONE) + break; + } + } + + if (ret != APP_CONTROL_ERROR_NONE && req.request_context) { + if (req.request_context->app_control) + app_control_destroy(req.request_context->app_control); + + free(req.request_context); + } + + return ret; +} + +int app_control_send_launch_request(app_control_h app_control, + app_control_reply_cb callback, void *user_data) +{ + int ret; + + ret = __send_launch_request(app_control, NULL, callback, user_data); + + return ret; +} + int app_control_send_terminate_request(app_control_h app_control) { if (app_control_validate(app_control)) @@ -1289,3 +1454,21 @@ int app_control_get_instance_id(app_control_h app_control, char **instance_id) return APP_CONTROL_ERROR_NONE; } + +int app_control_send_launch_request_async(app_control_h app_control, + app_control_result_cb result_cb, + app_control_reply_cb reply_cb, + void *user_data) +{ + int ret; + + if (!app_control || !result_cb) { + return app_control_error(APP_CONTROL_ERROR_INVALID_PARAMETER, + __FUNCTION__, "Invalid parameter"); + } + + ret = __send_launch_request(app_control, result_cb, + reply_cb, user_data); + + return ret; +} -- 2.34.1