From: Pawel Wasowski Date: Mon, 15 Oct 2018 14:50:07 +0000 (+0200) Subject: [Application] Make launch() and launchAppControl() asynchronous X-Git-Tag: submit/tizen_5.0/20181126.103411~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b6cff5d0cf62f059dc2cf5e64208d05794d31f5d;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Application] Make launch() and launchAppControl() asynchronous launch() and launchAppControl() were implemented synchronously, due to native APIs limitations. Both functions were refactored to use new native function, enabling sending application controls asynchronously. [Verification] TCT pass rate: 100% Change-Id: I6cb809d4b5713ca4a80a1f48649fb680358fa7d4 Signed-off-by: Pawel Wasowski Signed-off-by: Rafal Walczyna --- diff --git a/src/application/application_manager.cc b/src/application/application_manager.cc index 837dd138..f70e53d9 100644 --- a/src/application/application_manager.cc +++ b/src/application/application_manager.cc @@ -212,7 +212,7 @@ class TerminateHandler { return; \ } -void ApplicationManager::AsyncResponse(PlatformResult& result, +void ApplicationManager::AsyncResponse(const PlatformResult& result, std::shared_ptr* response) { ScopeLogger(); LogAndReportError(result, &(*response)->get()); @@ -374,82 +374,52 @@ void ApplicationManager::Kill(const picojson::value& args) { kill(); } -void ApplicationManager::Launch(const picojson::value& args) { +namespace { + +PlatformResult PrepareAppControlForLaunchAppControl(const picojson::value& args, + app_control_h* app_control) { ScopeLogger(); - int callback_id = -1; - const auto& callback = args.get(kCallbackId); - if (callback.is()) { - callback_id = static_cast(callback.get()); + const auto& control = args.get("appControl"); + if (!control.is()) { + return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); } + const picojson::object& app_control_obj = control.get(); - std::shared_ptr response(new picojson::value(picojson::object())); - picojson::object& obj = response->get(); - obj.insert(std::make_pair(kCallbackId, picojson::value(static_cast(callback_id)))); + app_control_h tmp_app_control = nullptr; + auto result = ApplicationUtils::ApplicationControlToService(app_control_obj, &tmp_app_control); + std::unique_ptr::type, decltype(&app_control_destroy)> + app_control_ptr(tmp_app_control, &app_control_destroy); - const auto& app_id = args.get("id"); - if (!app_id.is()) { - PlatformResult ret = - LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); - AsyncResponse(ret, &response); - return; + if (result.IsError()) { + LoggerE("Application control to service failed."); + return result; } - const std::string& id = app_id.get(); - - auto launch = [id](const std::shared_ptr& response) -> void { - ScopeLogger("launch"); - PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); - const char* app_id = id.c_str(); - const int retry_count = 3; - - int retry = 0; - int ret = 0; - while (retry < retry_count) { - ret = aul_open_app(app_id); + std::string app_id; + const auto& id = args.get("id"); + if (id.is()) { + app_id = id.get(); + } - if (ret >= 0) { - break; - } + if (!app_id.empty()) { + LoggerD("app_id: %s", app_id.c_str()); - // delay 300ms for each retry - struct timespec sleep_time = {0, 300L * 1000L * 1000L}; - nanosleep(&sleep_time, nullptr); - ++retry; + int ret = app_control_set_app_id(app_control_ptr.get(), app_id.c_str()); - LoggerD("Retry launch request: %d", retry); + if (APP_CONTROL_ERROR_NONE != ret) { + LoggerE("Failed to set app id: %d (%s)", ret, get_error_message(ret)); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); } + } - if (ret < 0) { - result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unknown error has occurred."); - - LoggerD("Aul open return: %d (%s)", ret, get_error_message(ret)); - switch (ret) { - case AUL_R_EINVAL: - case AUL_R_ERROR: - case AUL_R_ENOAPP: - result = - LogAndCreateResult(ErrorCode::NOT_FOUND_ERR, "Launchpad returns not found error.", - ("aul_open_app returns Not Found error")); - break; - - case AUL_R_ECOMM: - result = LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Internal IPC error has occurred.", - ("aul_open_app returns internal IPC error")); - break; - } - - LogAndReportError(result, &response->get()); - } else { - LoggerD("Launch request success"); - ReportSuccess(response->get()); - } - }; + *app_control = app_control_ptr.release(); - launch(response); - Instance::PostMessage(&this->instance_, response->serialize().c_str()); + return PlatformResult(ErrorCode::NO_ERROR); } +} // namespace + void ApplicationManager::LaunchAppControl(const picojson::value& args) { ScopeLogger(); @@ -464,158 +434,226 @@ void ApplicationManager::LaunchAppControl(const picojson::value& args) { response_obj.insert( std::make_pair(kCallbackId, picojson::value(static_cast(callback_id)))); - PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); - const auto& control = args.get("appControl"); - if (!control.is()) { - result = LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); - AsyncResponse(result, &response); - return; - } - const picojson::object& app_control_obj = control.get(); - - std::string launch_mode_str; - const auto& launch_mode = control.get("launchMode"); - if (launch_mode.is()) { - launch_mode_str = launch_mode.get(); - } - app_control_h app_control = nullptr; - result = ApplicationUtils::ApplicationControlToService(app_control_obj, &app_control); - std::shared_ptr::type> app_control_ptr( - app_control, &app_control_destroy); // automatically release the memory - - if (result.IsError()) { - LoggerE("Application control to service failed."); - AsyncResponse(result, &response); + auto prepare_app_control_result = PrepareAppControlForLaunchAppControl(args, &app_control); + if (prepare_app_control_result.IsError()) { + AsyncResponse(LogAndCreateResult(prepare_app_control_result), &response); return; } - std::string app_id; - const auto& id = args.get("id"); - if (id.is()) { - app_id = id.get(); - } + std::unique_ptr::type, decltype(&app_control_destroy)> + app_control_ptr(app_control, &app_control_destroy); - std::string reply_callback; + std::string reply_callback_id; const auto& reply = args.get("replyCallback"); if (reply.is()) { - reply_callback = reply.get(); + reply_callback_id = reply.get(); } - auto launch = [this, app_control_ptr, app_id, launch_mode_str, - reply_callback](const std::shared_ptr& response) -> void { - ScopeLogger("Entered into asynchronous function, launch"); + struct LaunchAppControlCallbackData { + ApplicationInstance* instance; + std::shared_ptr response; + std::string reply_callback_id; + }* launch_app_user_data = new (std::nothrow) + LaunchAppControlCallbackData{&this->instance_, response, reply_callback_id}; - if (!app_id.empty()) { - LoggerD("app_id: %s", app_id.c_str()); + if (!launch_app_user_data) { + LoggerE("Memory allocation fail!"); + AsyncResponse(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."), &response); + return; + } - int ret = app_control_set_app_id(app_control_ptr.get(), app_id.c_str()); + app_control_reply_cb reply_callback = nullptr; + if (!reply_callback_id.empty()) { + launch_app_user_data->reply_callback_id = reply_callback_id; - if (APP_CONTROL_ERROR_NONE != ret) { - LogAndReportError( - PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."), - &response->get(), - ("Failed to set app id: %d (%s)", ret, get_error_message(ret))); + reply_callback = [](app_control_h request, app_control_h reply, app_control_result_e result, + void* user_data) { + ScopeLogger("reply_callback"); + + LaunchAppControlCallbackData* callback_data = + static_cast(user_data); + if (!callback_data) { + LoggerD("reply_callback failed: user_data is nullptr"); return; } - } - app_control_reply_cb callback = nullptr; - struct ReplayCallbackData { - ApplicationInstance* app_instance; - std::string reply_callback; + picojson::value return_value = picojson::value(picojson::object()); + picojson::object& return_value_obj = return_value.get(); + return_value_obj.insert( + std::make_pair(kListenerId, picojson::value(callback_data->reply_callback_id))); + + if (APP_CONTROL_RESULT_SUCCEEDED == result) { + LoggerD("App started"); + return_value_obj.insert(std::make_pair("data", picojson::value(picojson::array()))); + if (!ApplicationUtils::ServiceToApplicationControlDataArray( + reply, &return_value_obj.find("data")->second.get())) { + return_value_obj.erase("data"); + } + ReportSuccess(return_value_obj); + } else { + ReportError(return_value_obj); + } + + Instance::PostMessage(callback_data->instance, return_value.serialize().c_str()); + delete callback_data; }; + } - ReplayCallbackData* user_data = nullptr; + app_control_result_cb result_callback = [](app_control_h launch_request, + app_control_error_e launch_result, void* user_data) { + ScopeLogger("LaunchAppControl result_callback"); - if (!reply_callback.empty()) { - user_data = new ReplayCallbackData(); - user_data->app_instance = &this->instance_; - user_data->reply_callback = reply_callback; + LaunchAppControlCallbackData* callback_data = + static_cast(user_data); - callback = [](app_control_h request, app_control_h reply, app_control_result_e result, - void* user_data) { - LoggerD("send_launch_request callback"); + auto result = ApplicationUtils::TranslateAppControlError(launch_result); - picojson::value return_value = picojson::value(picojson::object()); - picojson::object& return_value_obj = return_value.get(); - ReplayCallbackData* reply_callback = static_cast(user_data); + if (result.IsError()) { + LogAndReportError(result, &(callback_data->response->get())); + } else { + ReportSuccess(callback_data->response->get()); + } - if (APP_CONTROL_RESULT_SUCCEEDED == result) { - const std::string data = "data"; - return_value_obj.insert(std::make_pair(data, picojson::value(picojson::array()))); - if (!ApplicationUtils::ServiceToApplicationControlDataArray( - reply, &return_value_obj.find(data)->second.get())) { - return_value_obj.erase(data); - } - ReportSuccess(return_value_obj); - } else { - ReportError(return_value_obj); - } + Instance::PostMessage(callback_data->instance, callback_data->response->serialize().c_str()); - return_value_obj.insert( - std::make_pair("listenerId", picojson::value(reply_callback->reply_callback))); - Instance::PostMessage(reply_callback->app_instance, return_value.serialize().c_str()); - delete reply_callback; - }; + if (result.IsError() || (callback_data->reply_callback_id).empty()) { + delete callback_data; } + }; - const int retry_count = 3; + /* + * TODO: Observe how often app_control_send_launch_request_async tries to launch the application. + * Previous implementation, using synchronous app_control_send_launch_request, + * tries to launch the application 3 times, before reporting an error. + * New implementation, using app_control_send_launch_request_async makes only one attempt. + * If problems, such as failed application start occur, multiple attempts may solve the problem. + */ + auto launch_result = + ApplicationUtils::TranslateAppControlError(static_cast(app_control_send_launch_request_async( + app_control_ptr.get(), result_callback, reply_callback, launch_app_user_data))); + + if (launch_result.IsError()) { + delete launch_app_user_data; + AsyncResponse(launch_result, &response); + } else { + LoggerD("App launched"); + } +} - int retry = 0; - int ret = 0; +namespace { - while (retry < retry_count) { - LoggerD("Calling launch request. Attempt number: %d", retry); +PlatformResult TranslateLaunchError(app_control_error_e return_code) { + ScopeLogger(); - ret = app_control_send_launch_request(app_control_ptr.get(), callback, user_data); - LoggerD("App control launch request returned: %d, %s", ret, get_error_message(ret)); - if (APP_CONTROL_ERROR_NONE == ret) { - break; - } + auto result = ApplicationUtils::TranslateAppControlError(return_code); + if (ErrorCode::SECURITY_ERR == result.error_code()) { + result = PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."); + } - // delay 300ms for each retry - struct timespec sleep_time = {0, 300L * 1000L * 1000L}; - nanosleep(&sleep_time, nullptr); - ++retry; - } + return result; +} + +PlatformResult PrepareAppControlForLaunch(const picojson::value& args, app_control_h* app_control) { + ScopeLogger(); + + const auto& app_id = args.get("id"); + if (!app_id.is()) { + return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); + } + const auto app_id_str = app_id.get(); + + app_control_h tmp_app_control = nullptr; + int result = app_control_create(&tmp_app_control); + if (APP_CONTROL_ERROR_NONE != result) { + LoggerD("app_control_create() failed: %d (%s)", result, get_error_message(result)); + return PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occurred."); + } + + std::unique_ptr::type, decltype(&app_control_destroy)> + app_control_ptr(tmp_app_control, &app_control_destroy); + + if (!app_id_str.empty()) { + LoggerD("app_id: %s", app_id_str.c_str()); + + int ret = app_control_set_app_id(app_control_ptr.get(), app_id_str.c_str()); if (APP_CONTROL_ERROR_NONE != ret) { - delete user_data; - - switch (ret) { - case APP_CONTROL_ERROR_INVALID_PARAMETER: - LogAndReportError( - PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter returned."), - &response->get(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_INVALID_PARAMETER")); - return; - case APP_CONTROL_ERROR_OUT_OF_MEMORY: - LogAndReportError( - PlatformResult(ErrorCode::UNKNOWN_ERR, "Out of memory."), - &response->get(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_OUT_OF_MEMORY")); - return; - case APP_CONTROL_ERROR_LAUNCH_REJECTED: - case APP_CONTROL_ERROR_APP_NOT_FOUND: - LogAndReportError( - PlatformResult(ErrorCode::NOT_FOUND_ERR, "No matched application found."), - &response->get(), - ("app_control_send_launch_request returns APP_CONTROL_ERROR_APP_NOT_FOUND")); - return; - default: - LogAndReportError( - PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error."), - &response->get(), - ("app_control_send_launch_request returns: %d (%s)", ret, get_error_message(ret))); - return; - } + LoggerE("Failed to set app id: %d (%s)", ret, get_error_message(ret)); + return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid parameter passed."); + } + } + + *app_control = app_control_ptr.release(); + + return PlatformResult(ErrorCode::NO_ERROR); +} + +} // namespace +void ApplicationManager::Launch(const picojson::value& args) { + ScopeLogger(); + + int callback_id = -1; + const auto& callback = args.get(kCallbackId); + if (callback.is()) { + callback_id = static_cast(callback.get()); + } + + std::shared_ptr response(new picojson::value(picojson::object())); + picojson::object& response_obj = response->get(); + response_obj.insert( + std::make_pair(kCallbackId, picojson::value(static_cast(callback_id)))); + + app_control_h app_control = nullptr; + auto prepare_app_control_result = PrepareAppControlForLaunch(args, &app_control); + if (prepare_app_control_result.IsError()) { + AsyncResponse(LogAndCreateResult(prepare_app_control_result), &response); + return; + } + + std::unique_ptr::type, decltype(&app_control_destroy)> + app_control_ptr(app_control, &app_control_destroy); + + struct LaunchCallbackData { + ApplicationInstance* instance; + std::shared_ptr response; + }* launch_user_data = new (std::nothrow) LaunchCallbackData{&this->instance_, response}; + + app_control_result_cb result_callback = [](app_control_h launch_request, + app_control_error_e launch_result, void* user_data) { + ScopeLogger("Launch result_callback"); + + LaunchCallbackData* callback_data = static_cast(user_data); + + auto result = TranslateLaunchError(launch_result); + + if (result.IsError()) { + LogAndReportError(result, &(callback_data->response->get())); + } else { + ReportSuccess(callback_data->response->get()); } - ReportSuccess(response->get()); + + Instance::PostMessage(callback_data->instance, callback_data->response->serialize().c_str()); + + delete callback_data; }; - launch(response); - Instance::PostMessage(&this->instance_, response->serialize().c_str()); + /* + * TODO: Observe how often app_control_send_launch_request_async tries to launch the application. + * Previous implementation, using synchronous app_control_send_launch_request, + * tries to launch the application 3 times, before reporting an error. + * New implementation, using app_control_send_launch_request_async makes only one attempt. + * If problems, such as failed application start occur, multiple attempts may solve the problem. + */ + auto launch_result = TranslateLaunchError(static_cast(app_control_send_launch_request_async( + app_control_ptr.get(), result_callback, nullptr, launch_user_data))); + + if (launch_result.IsError()) { + delete launch_user_data; + AsyncResponse(launch_result, &response); + } else { + LoggerD("App launched"); + } } // internal impl of app_control_foreach_app_matched() for handling APP_CONTROL_ERROR_APP_NOT_FOUND diff --git a/src/application/application_manager.h b/src/application/application_manager.h index c1fa8ad2..be0ebbf6 100644 --- a/src/application/application_manager.h +++ b/src/application/application_manager.h @@ -64,7 +64,8 @@ class ApplicationManager { void StartAppInfoEventListener(picojson::object* out); void StopAppInfoEventListener(); void GetApplicationInformationSize(const picojson::value& args, picojson::object* out); - void AsyncResponse(common::PlatformResult& result, std::shared_ptr* response); + void AsyncResponse(const common::PlatformResult& result, + std::shared_ptr* response); void BroadcastEventHelper(const picojson::value& args, picojson::object& out, bool trusted); common::PlatformResult StartEventListener(const std::string& event_name, diff --git a/src/application/application_utils.cc b/src/application/application_utils.cc index c1c44726..5b46388b 100644 --- a/src/application/application_utils.cc +++ b/src/application/application_utils.cc @@ -229,7 +229,8 @@ PlatformResult SetAppControlTextField(app_control_h app_control, const std::stri app_control_launch_mode_e LaunchModeStringToEnum(const std::string& launch_mode) { ScopeLogger(); - return launch_mode == kGroupLaunchMode ? APP_CONTROL_LAUNCH_MODE_GROUP : APP_CONTROL_LAUNCH_MODE_SINGLE; + return launch_mode == kGroupLaunchMode ? APP_CONTROL_LAUNCH_MODE_GROUP + : APP_CONTROL_LAUNCH_MODE_SINGLE; } bool LaunchModeIsInvalid(const std::string& launch_mode) { @@ -324,33 +325,38 @@ PlatformResult ApplicationUtils::ApplicationControlToService( auto set_field_result = PlatformResult(ErrorCode::UNKNOWN_ERR); - set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kOperationAppControlField, it_operation); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kOperationAppControlField, it_operation); if (set_field_result.IsError()) { return set_field_result; } - set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kURIAppControlField, it_uri); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kURIAppControlField, it_uri); if (set_field_result.IsError()) { return set_field_result; } - set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kMIMEAppControlField, it_mime); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kMIMEAppControlField, it_mime); if (set_field_result.IsError()) { return set_field_result; } - set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kCategoryAppControlField, it_category); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kCategoryAppControlField, it_category); if (set_field_result.IsError()) { return set_field_result; } - set_field_result = - SetAppControlFieldIfValueSpecified(app_control_tmp, kLaunchModeAppControlField, it_launch_mode); + set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kLaunchModeAppControlField, + it_launch_mode); if (set_field_result.IsError()) { return set_field_result; } - set_field_result = SetAppControlFieldIfValueSpecified(app_control_tmp, kDataAppControlField, it_data); + set_field_result = + SetAppControlFieldIfValueSpecified(app_control_tmp, kDataAppControlField, it_data); if (set_field_result.IsError()) { return set_field_result; } @@ -418,7 +424,8 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, LoggerE("Get operation failed: %d (%s)", ret, get_error_message(ret)); } else if (tmp_str) { LoggerD("operation: %s", tmp_str); - app_control_obj->insert(std::make_pair(kOperationAppControlField, picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kOperationAppControlField, picojson::value(std::string(tmp_str)))); } else { LoggerD("operation field is empty"); } @@ -429,7 +436,8 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, LoggerE("Get URI failed: %d (%s)", ret, get_error_message(ret)); } else if (tmp_str) { LoggerD("URI: %s", tmp_str); - app_control_obj->insert(std::make_pair(kURIAppControlField, picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kURIAppControlField, picojson::value(std::string(tmp_str)))); } else { LoggerD("URI field is empty"); } @@ -440,7 +448,8 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, LoggerE("Get MIME failed: %d (%s)", ret, get_error_message(ret)); } else if (tmp_str) { LoggerD("MIME: %s", tmp_str); - app_control_obj->insert(std::make_pair(kMIMEAppControlField, picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kMIMEAppControlField, picojson::value(std::string(tmp_str)))); } else { LoggerD("MIME field is empty"); } @@ -451,7 +460,8 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, LoggerE("Get category failed: %d (%s)", ret, get_error_message(ret)); } else if (tmp_str) { LoggerD("category: %s", tmp_str); - app_control_obj->insert(std::make_pair(kCategoryAppControlField, picojson::value(std::string(tmp_str)))); + app_control_obj->insert( + std::make_pair(kCategoryAppControlField, picojson::value(std::string(tmp_str)))); } else { LoggerD("category field is empty"); } @@ -465,7 +475,8 @@ void ApplicationUtils::ServiceToApplicationControl(app_control_h app_control, std::string launch_mode_str = launch_mode == APP_CONTROL_LAUNCH_MODE_SINGLE ? kSingleLaunchMode : kGroupLaunchMode; LoggerD("launch mode: %s", launch_mode_str.c_str()); - app_control_obj->insert(std::make_pair(kLaunchModeAppControlField, picojson::value(launch_mode_str))); + app_control_obj->insert( + std::make_pair(kLaunchModeAppControlField, picojson::value(launch_mode_str))); } app_control_obj->insert(std::make_pair(kDataAppControlField, picojson::value(picojson::array())));