Support launch request async API 43/189043/14
authorHwankyu Jhun <h.jhun@samsung.com>
Thu, 13 Sep 2018 03:19:11 +0000 (12:19 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Thu, 20 Sep 2018 22:41:56 +0000 (07:41 +0900)
Change-Id: I834327b75468767e0fe4867f4a4b843ec22c913b
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
include/app_control.h
src/app_control.c

index 52fcfd327bc4f00a0ce2ecff971a6b5374f9a234..7a55be7b05d5f6495a8012151232d013a162ebf1 100755 (executable)
@@ -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);
+
 /**
  * @}
  */
index 1badb0b0318cf57004cd79190aed1f0b220f3046..c645d92493fbfb01489abf966070bccb23545d71 100644 (file)
@@ -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;
+}