diff --git a/common/include/variant_util.h b/common/include/variant_util.h index 347ba8da8e10edb8bb84add3ef6745411b54d321..f8966f6109b2db6eba4be0d17cf9f9ca2c754382 100644 --- a/common/include/variant_util.h +++ b/common/include/variant_util.h @@ -21,6 +21,7 @@ #include "global.h" #include "input_client_info.h" #include "input_method_utils.h" +#include "imf_event_notification.h" namespace OHOS { namespace MiscServices { class VariantUtil { @@ -45,6 +46,16 @@ public: output = std::get(input); return true; } + + template + static bool GetValue(NotificationValue &input, T &output) + { + if (!std::holds_alternative(input)) { + return false; + } + output = std::get(input); + return true; + } }; } // namespace MiscServices } // namespace OHOS diff --git a/frameworks/js/napi/inputmethodclient/async_call.h b/frameworks/js/napi/inputmethodclient/async_call.h index c2f228d03e30824bf3c08d3b8c029df0d6e7ef82..7c5d97a0cba87e6f14aa00c211076fc21e9978e7 100644 --- a/frameworks/js/napi/inputmethodclient/async_call.h +++ b/frameworks/js/napi/inputmethodclient/async_call.h @@ -160,13 +160,13 @@ protected: }; static void OnExecuteAsync(napi_env env, AsyncContext *context, Context::CallBackAction cb); static void OnComplete(napi_env env, napi_status status, void *data); - -private: + + private: + static void OnExecute(napi_env env, void *data); virtual void CallImpl(napi_env env, AsyncContext *context, const std::string &resourceName); private: enum Arg : int { ARG_ERROR, ARG_DATA, ARG_BUTT }; - static void OnExecute(napi_env env, void *data); static void OnExecuteSeq(napi_env env, void *data); static void DeleteContext(napi_env env, AsyncContext *context); diff --git a/frameworks/js/napi/inputmethodclient/imc_async_call.h b/frameworks/js/napi/inputmethodclient/imc_async_call.h new file mode 100644 index 0000000000000000000000000000000000000000..4134be26fe0f09f68e6f939fc4a5f4136f11d0b2 --- /dev/null +++ b/frameworks/js/napi/inputmethodclient/imc_async_call.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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 IMC_ASYN_CALL_H +#define IMC_ASYN_CALL_H + +#include +#include "async_call.h" +#include "task_manager.h" +#include "input_method_controller.h" +namespace OHOS { +namespace MiscServices { +class IMCAsyncCall : public AsyncCall { +public: + IMCAsyncCall(napi_env env, napi_callback_info info, std::shared_ptr context, size_t maxParamCount) + : AsyncCall(env, info, context, maxParamCount) {}; + virtual ~IMCAsyncCall(){}; + +public: + static void Request(std::function work, + AsyncCall::Context::CallBackAction completeFunc, std::shared_ptr ctxt, + const std::string errMsg, std::function fillResultCb) + { + auto callback = [ctxt, completeFunc, errMsg, fillResultCb](int32_t code) -> void { + ProcessResult(code, ctxt, completeFunc, errMsg, fillResultCb); + }; + int32_t ret = ErrorCode::NO_ERROR; + if (work == nullptr){ + IMSA_HILOGE("work function is nullptr"); + ret = ErrorCode::ERROR_NULL_POINTER; + ProcessResult(ret, ctxt, completeFunc, "work function is nullptr", fillResultCb); + } else { + ret = work(callback); + } + if (ret != ErrorCode::NO_ERROR) { + ProcessResult(ret, ctxt, completeFunc, errMsg, fillResultCb); + } + } + +private: + static void ProcessResult(int32_t code, std::shared_ptr ctxt, + AsyncCall::Context::CallBackAction completeFunc, + const std::string& errMsg, std::function fillResultCb) { + if (fillResultCb != nullptr) { + fillResultCb(code); + } else { + if (code == ErrorCode::NO_ERROR) { + ctxt->SetState(napi_ok); + } else { + ctxt->SetErrorCode(code); + ctxt->SetErrorMessage(errMsg); + } + } + completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr"); + } + + void CallImpl(napi_env env, AsyncContext *context, const std::string &resourceName) override + { + if (context == nullptr || context->ctx == nullptr) { + IMSA_HILOGE("context is nullptr!"); + return; + } + auto cb = [env, context, resourceName]() -> void { + auto task = [env, context]() -> void { + AsyncCall::OnComplete(env, context->ctx->GetState(), context); + }; + napi_status status = napi_send_event(env, task, napi_eprio_high); + if (status != napi_status::napi_ok) { + IMSA_HILOGE("napi_send_event failed with status %d, resourceName: %s", status, resourceName.c_str()); + } + }; + AsyncCall::OnExecuteAsync(env, context, cb); + } +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMC_ASYN_CALL_H diff --git a/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.cpp b/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.cpp index 2465a4852fd6bbc40b56b3566683cc7eec9c14e0..dc3d54b69a9aec17b1cb24402a5ad94d91f54835 100644 --- a/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.cpp +++ b/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.cpp @@ -58,7 +58,6 @@ std::shared_ptr JsGetInputMethodController::controll std::mutex JsGetInputMethodController::eventHandlerMutex_; std::shared_ptr JsGetInputMethodController::handler_{ nullptr }; BlockQueue JsGetInputMethodController::messageHandlerQueue_{ MAX_WAIT_TIME_MESSAGE_HANDLER }; -BlockQueue JsGetInputMethodController::attachQueue_{ MAX_WAIT_TIME_ATTACH }; napi_value JsGetInputMethodController::Init(napi_env env, napi_value info) { napi_property_descriptor descriptor[] = { @@ -521,7 +520,7 @@ napi_value JsGetInputMethodController::CreateSelectMovement(napi_env env, int32_ } napi_value JsGetInputMethodController::HandleSoftKeyboard(napi_env env, napi_callback_info info, - std::function callback, bool isOutput, bool needThrowException) + std::function work, bool isOutput, bool needThrowException) { auto ctxt = std::make_shared(); auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { @@ -535,22 +534,25 @@ napi_value JsGetInputMethodController::HandleSoftKeyboard(napi_env env, napi_cal IMSA_HILOGE("output get boolean != nullptr[%{public}d]", result != nullptr); return status; }; - auto exec = [ctxt, callback, needThrowException](AsyncCall::Context *ctx) { - int errCode = callback(); - if (errCode == ErrorCode::NO_ERROR) { - IMSA_HILOGI("exec success."); - ctxt->status = napi_ok; - ctxt->isHandle = true; - ctxt->SetState(ctxt->status); - return; - } - if (needThrowException) { - ctxt->SetErrorCode(errCode); - } + auto exec = [ctxt, work, needThrowException](AsyncCall::Context *ctx, + AsyncCall::Context::CallBackAction completeFunc) { + auto handler = [ctxt, needThrowException](int32_t code) { + if (code == ErrorCode::NO_ERROR) { + IMSA_HILOGI("exec success."); + ctxt->status = napi_ok; + ctxt->isHandle = true; + ctxt->SetState(ctxt->status); + return; + } + if (needThrowException) { + ctxt->SetErrorCode(code); + } + }; + IMCAsyncCall::Request(work, completeFunc, ctxt, "handleSoftKeyboard error", handler); }; ctxt->SetAction(std::move(input), std::move(output)); // 1 means JsAPI has 1 param at most. - AsyncCall asyncCall(env, info, ctxt, 1); + IMCAsyncCall asyncCall(env, info, ctxt, 1); return asyncCall.Call(env, exec, "handleSoftKeyboard"); } @@ -653,43 +655,42 @@ napi_value JsGetInputMethodController::Attach(napi_env env, napi_callback_info i } } ctxt->info = { std::chrono::system_clock::now(), ctxt->attribute}; - attachQueue_.Push(ctxt->info); ctxt->textListener = JsGetInputMethodTextChangedListener::GetTextListener(GetEventHandler(), ctxt->textConfig.newEditBox); return napi_ok; }; - auto exec = [ctxt, env](AsyncCall::Context *ctx) { - attachQueue_.Wait(ctxt->info); + auto exec = [ctxt, env](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) { OHOS::MiscServices::AttachOptions attachOptions; attachOptions.isShowKeyboard = ctxt->showKeyboard; attachOptions.requestKeyboardReason = static_cast(ctxt->requestKeyboardReason); - int32_t status = ErrorCode::ERROR_CLIENT_NULL_POINTER; - auto instance = InputMethodController::GetInstance(); - if (instance != nullptr) { - status = instance->Attach(ctxt->textListener, attachOptions, ctxt->textConfig, ClientType::JS); - } - attachQueue_.Pop(); - ctxt->SetErrorCode(status); - CHECK_RETURN_VOID(status == ErrorCode::NO_ERROR, "attach return error!"); - ctxt->SetState(napi_ok); + auto work = [ctxt, &attachOptions](AsyncCallback callback) -> int32_t { + int32_t status = ErrorCode::ERROR_CLIENT_NULL_POINTER; + auto instance = InputMethodController::GetInstance(); + if (instance != nullptr && callback != nullptr) { + status = instance->AttachAsync(ctxt->textListener, attachOptions, ctxt->textConfig, ClientType::JS, + callback); + } + return status; + }; + IMCAsyncCall::Request(work, completeFunc, ctxt, "attach return error!", nullptr); }; ctxt->SetAction(std::move(input)); // 3 means JsAPI:attach has 3 params at most. - AsyncCall asyncCall(env, info, ctxt, 3); + IMCAsyncCall asyncCall(env, info, ctxt, 3); return asyncCall.Call(env, exec, "attach"); } napi_value JsGetInputMethodController::Detach(napi_env env, napi_callback_info info) { return HandleSoftKeyboard( - env, info, [] () -> int32_t { + env, info, [] (AsyncCallback callback) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->Close(); + return instance->CloseAsync(callback); }, false, true); } @@ -701,13 +702,13 @@ napi_value JsGetInputMethodController::ShowTextInput(napi_env env, napi_callback InputMethodSyncTrace tracer("JsGetInputMethodController_ShowTextInput"); return HandleSoftKeyboard( env, info, - [attachOptions] () -> int32_t { + [attachOptions] (AsyncCallback callback) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->ShowTextInput(attachOptions, ClientType::JS); + return instance->ShowTextInputAsync(callback, attachOptions, ClientType::JS); }, false, true); } @@ -741,13 +742,13 @@ napi_value JsGetInputMethodController::HideTextInput(napi_env env, napi_callback InputMethodSyncTrace tracer("JsGetInputMethodController_HideTextInput"); return HandleSoftKeyboard( env, info, - [] () -> int32_t { + [] (AsyncCallback callbac) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->HideTextInput(); + return instance->HideTextInputAsync(callbac); }, false, true); } @@ -755,17 +756,29 @@ napi_value JsGetInputMethodController::HideTextInput(napi_env env, napi_callback napi_value JsGetInputMethodController::DiscardTypingText(napi_env env, napi_callback_info info) { InputMethodSyncTrace tracer("JsGetInputMethodController_DiscardTypingText"); - return HandleSoftKeyboard( - env, info - , [] { - auto instance = InputMethodController::GetInstance(); - if (instance == nullptr) { - IMSA_HILOGE("GetInstance return nullptr!"); - return static_cast(ErrorCode::ERROR_CLIENT_NULL_POINTER); - } - return instance->DiscardTypingText(); - }, - false, true); + auto ctxt = std::make_shared(); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + return napi_ok; + }; + auto exec = [ctxt](AsyncCall::Context *ctx) { + int32_t errCode = ErrorCode::ERROR_CLIENT_NULL_POINTER; + auto instance = InputMethodController::GetInstance(); + if (instance != nullptr) { + errCode = instance->DiscardTypingText(); + } + if (errCode == ErrorCode::NO_ERROR) { + IMSA_HILOGI("exec success."); + ctxt->status = napi_ok; + ctxt->isHandle = true; + ctxt->SetState(ctxt->status); + } else { + ctxt->SetErrorCode(errCode); + } + }; + ctxt->SetAction(std::move(input)); + // 1 means JsAPI has 1 param at most. + AsyncCall asyncCall(env, info, ctxt, 1); + return asyncCall.Call(env, exec, "discardTypingText"); } napi_value JsGetInputMethodController::SetCallingWindow(napi_env env, napi_callback_info info) @@ -905,13 +918,13 @@ napi_value JsGetInputMethodController::ShowSoftKeyboard(napi_env env, napi_callb InputMethodSyncTrace tracer("JsGetInputMethodController_ShowSoftKeyboard"); return HandleSoftKeyboard( env, info, - [] () -> int32_t { + [] (AsyncCallback callback) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->ShowSoftKeyboard(ClientType::JS); + return instance->ShowSoftKeyboardAsync(callback, ClientType::JS); }, false, true); } @@ -921,13 +934,13 @@ napi_value JsGetInputMethodController::HideSoftKeyboard(napi_env env, napi_callb InputMethodSyncTrace tracer("JsGetInputMethodController_HideSoftKeyboard"); return HandleSoftKeyboard( env, info, - [] () -> int32_t { + [] (AsyncCallback callbac) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->HideSoftKeyboard(); + return instance->HideSoftKeyboardAsync(callbac); }, false, true); } @@ -936,13 +949,13 @@ napi_value JsGetInputMethodController::StopInputSession(napi_env env, napi_callb { return HandleSoftKeyboard( env, info, - [] () -> int32_t { + [] (AsyncCallback callbac) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->StopInputSession(); + return instance->StopInputSessionAsync(callbac); }, true, true); } @@ -951,13 +964,13 @@ napi_value JsGetInputMethodController::StopInput(napi_env env, napi_callback_inf { return HandleSoftKeyboard( env, info, - []() -> int32_t { + [](AsyncCallback callbac) -> int32_t { auto instance = InputMethodController::GetInstance(); if (instance == nullptr) { IMSA_HILOGE("GetInstance return nullptr!"); return ErrorCode::ERROR_CLIENT_NULL_POINTER; } - return instance->HideCurrentInput(); + return instance->HideCurrentInputAsync(callbac); }, true, false); } diff --git a/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.h b/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.h index da9589ba8ffa2d9cfb2d62308c5fedd121c608f8..76f9391a657169338c22f33ee038575f4bab2e14 100644 --- a/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.h +++ b/frameworks/js/napi/inputmethodclient/js_get_input_method_controller.h @@ -15,7 +15,7 @@ #ifndef INTERFACE_KITS_JS_GET_INPUT_METHOD_CONTROLLER_H #define INTERFACE_KITS_JS_GET_INPUT_METHOD_CONTROLLER_H -#include "async_call.h" +#include "imc_async_call.h" #include "controller_listener.h" #include "event_handler.h" #include "global.h" @@ -172,8 +172,8 @@ public: static napi_value GetController(napi_env env, napi_callback_info cbInfo); static napi_value GetInputMethodController(napi_env env, napi_callback_info cbInfo); static std::shared_ptr GetInstance(); - static napi_value HandleSoftKeyboard(napi_env env, napi_callback_info info, std::function callback, - bool isOutput, bool needThrowException); + static napi_value HandleSoftKeyboard(napi_env env, napi_callback_info info, + std::function work, bool isOutput, bool needThrowException); static napi_value Attach(napi_env env, napi_callback_info info); static napi_value Detach(napi_env env, napi_callback_info info); static napi_value ShowTextInput(napi_env env, napi_callback_info info); @@ -278,7 +278,6 @@ private: static constexpr size_t PARAM_POS_TWO = 2; static constexpr size_t PARAM_POS_THREE = 3; static BlockQueue messageHandlerQueue_; - static BlockQueue attachQueue_; }; } // namespace MiscServices } // namespace OHOS diff --git a/frameworks/native/inputmethod_ability/IInputControlChannel.idl b/frameworks/native/inputmethod_ability/IInputControlChannel.idl index af5015425d1bab2aa4c840e51b5926d838f64f97..554db75d094678ae1fcc0a5f339171fba6672c21 100644 --- a/frameworks/native/inputmethod_ability/IInputControlChannel.idl +++ b/frameworks/native/inputmethod_ability/IInputControlChannel.idl @@ -12,7 +12,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + +sequenceable OHOS.IRemoteObject; interface OHOS.MiscServices.IInputControlChannel { - void HideKeyboardSelf(); + [ipccode 0] void HideKeyboardSelf(); + [oneway] void RespondOnConnectSystemCmd([in] long id, [in] int code, [in] IRemoteObject agent); + [oneway] void RespondIsEnable([in] long id, [in] int code, [in] boolean resultValue); + [oneway] void RespondIsPanelShown([in] long id, [in] int code, [in] boolean isShown); } diff --git a/frameworks/native/inputmethod_ability/IInputMethodCore.idl b/frameworks/native/inputmethod_ability/IInputMethodCore.idl index aa7aef25795e5398ac454885bbe5faaad9aa7954..4095b6cde261bf97aa94237f1dcffc9f19558f87 100644 --- a/frameworks/native/inputmethod_ability/IInputMethodCore.idl +++ b/frameworks/native/inputmethod_ability/IInputMethodCore.idl @@ -27,10 +27,10 @@ interface OHOS.MiscServices.IInputMethodCore { void InitInputControlChannel([in] IInputControlChannel inputControlChannel); void StopInputService([in] boolean isTerminateIme); void SetSubtype([in] SubProperty property); - void IsEnable([out] boolean resultValue); - void IsPanelShown([in] PanelInfo panelInfo, [out] boolean isShown); + [oneway] void IsProxyImeEnable([in] long id); + [oneway] void IsPanelShown([in] long id, [in] PanelInfo panelInfo); void OnSecurityChange([in] int security); - void OnConnectSystemCmd([in] IRemoteObject channel, [out] IRemoteObject agent); + [oneway] void OnConnectSystemCmd([in] long id, [in] IRemoteObject channel); [oneway] void OnClientInactive([in] IRemoteObject channel); [oneway] void OnSetInputType([in] int inputType); void OnCallingDisplayIdChanged([in] unsigned long dispalyId); diff --git a/frameworks/native/inputmethod_ability/include/input_method_ability.h b/frameworks/native/inputmethod_ability/include/input_method_ability.h index a94f600aeca42a081e932302f24979c084414653..1b2820cb704a3f2c426a4e7c998f35e8f5010fa3 100644 --- a/frameworks/native/inputmethod_ability/include/input_method_ability.h +++ b/frameworks/native/inputmethod_ability/include/input_method_ability.h @@ -109,6 +109,9 @@ public: int32_t OnSendPrivateData(const std::unordered_map &privateCommand); bool HandleUnconsumedKey(const std::shared_ptr &keyEvent); int32_t OnResponse(uint64_t msgId, int32_t code, const ResponseData &data); + void ReplyIsEnable(int64_t id, int32_t code, bool &enbale); + void ReplyIsPanelShown(int64_t id, int32_t code, bool &isShown); + void ReplyOnConnectSystemCmd(int64_t id, int32_t code, const sptr &agent); int32_t IsCapacitySupport(int32_t capacity, bool &isSupport); AttachOptions GetAttachOptions(); diff --git a/frameworks/native/inputmethod_ability/include/input_method_core_service_impl.h b/frameworks/native/inputmethod_ability/include/input_method_core_service_impl.h index dda07ef7a592d3582bd75ba32ffd801c35e55891..02a1fb1779ebfc432461b37490222d35d69ef289 100644 --- a/frameworks/native/inputmethod_ability/include/input_method_core_service_impl.h +++ b/frameworks/native/inputmethod_ability/include/input_method_core_service_impl.h @@ -39,10 +39,10 @@ public: ErrCode InitInputControlChannel(const sptr &inputControlChannel) override; ErrCode StopInputService(bool isTerminateIme) override; ErrCode SetSubtype(const SubProperty &property) override; - ErrCode IsEnable(bool &resultValue) override; - ErrCode IsPanelShown(const PanelInfo &panelInfo, bool &isShown) override; + ErrCode IsProxyImeEnable(int64_t id) override; + ErrCode IsPanelShown(int64_t id, const PanelInfo &panelInfo) override; ErrCode OnSecurityChange(int32_t security) override; - ErrCode OnConnectSystemCmd(const sptr &channel, sptr &agent) override; + ErrCode OnConnectSystemCmd(int64_t id, const sptr &channel) override; ErrCode OnClientInactive(const sptr &channel) override; ErrCode OnSetInputType(int32_t inputType) override; ErrCode OnCallingDisplayIdChanged(uint64_t dispalyId) override; diff --git a/frameworks/native/inputmethod_ability/src/input_method_ability.cpp b/frameworks/native/inputmethod_ability/src/input_method_ability.cpp index cac6dfa735e3a90bf116868cb5096333ff584909..b8017c1d25ce8533b34bcf00c697e1b7237ced07 100644 --- a/frameworks/native/inputmethod_ability/src/input_method_ability.cpp +++ b/frameworks/native/inputmethod_ability/src/input_method_ability.cpp @@ -1861,6 +1861,48 @@ int32_t InputMethodAbility::OnResponse(uint64_t msgId, int32_t code, const Respo return ErrorCode::NO_ERROR; } +void InputMethodAbility::ReplyIsEnable(int64_t id, int32_t code, bool &enbale) +{ + IMSA_HILOGD("ReplyIsEnable id: %{public}" PRIu64 " code: %{public}d", id, code); + auto controlChannel = GetInputControlChannel(); + if (controlChannel == nullptr) { + IMSA_HILOGE("controlChannel is nullptr"); + return; + } + int32_t ret = controlChannel->RespondIsEnable(id, code, enbale); + if (ret != ERR_OK) { + IMSA_HILOGE("RespondIsEnable code: %{public}d", ret); + } +} + +void InputMethodAbility::ReplyIsPanelShown(int64_t id, int32_t code, bool &isShown) +{ + IMSA_HILOGD("ReplyIsPanelShown id: %{public}" PRIu64 " code: %{public}d", id, code); + auto controlChannel = GetInputControlChannel(); + if (controlChannel == nullptr) { + IMSA_HILOGE("controlChannel is nullptr"); + return; + } + int32_t ret = controlChannel->RespondIsPanelShown(id, code, isShown); + if (ret != ERR_OK) { + IMSA_HILOGE("RespondIsPanelShown code: %{public}d", ret); + } +} + +void InputMethodAbility::ReplyOnConnectSystemCmd(int64_t id, int32_t code, const sptr &agent) +{ + IMSA_HILOGD("ReplyOnConnectSystemCmd id: %{public}" PRIu64 " code: %{public}d", id, code); + auto controlChannel = GetInputControlChannel(); + if (controlChannel == nullptr) { + IMSA_HILOGE("controlChannel is nullptr"); + return; + } + int32_t ret = controlChannel->RespondOnConnectSystemCmd(id, code, agent); + if (ret != ERR_OK) { + IMSA_HILOGE("RespondOnConnectSystemCmd code: %{public}d", ret); + } +} + int32_t InputMethodAbility::IsCapacitySupport(int32_t capacity, bool &isSupport) { auto proxy = GetImsaProxy(); diff --git a/frameworks/native/inputmethod_ability/src/input_method_core_service_impl.cpp b/frameworks/native/inputmethod_ability/src/input_method_core_service_impl.cpp index 2b563cf6c3c387bc3c20eaa35eba7621a735fc68..a3d210a2f69a5e611da0c85b38258d325ff62fac 100644 --- a/frameworks/native/inputmethod_ability/src/input_method_core_service_impl.cpp +++ b/frameworks/native/inputmethod_ability/src/input_method_core_service_impl.cpp @@ -63,10 +63,13 @@ ErrCode InputMethodCoreServiceImpl::StopInputService(bool isTerminateIme) return ERR_OK; } -ErrCode InputMethodCoreServiceImpl::OnConnectSystemCmd( - const sptr &channel, sptr &agent) +ErrCode InputMethodCoreServiceImpl::OnConnectSystemCmd(int64_t id, const sptr &channel) { - return InputMethodAbility::GetInstance().OnConnectSystemCmd(channel, agent); + sptr agent = nullptr; + int32_t code = InputMethodAbility::GetInstance().OnConnectSystemCmd(channel, agent); + IMSA_HILOGD("OnConnectSystemCmd id: %{public}" PRId64 " code: %{public}d", id, code); + InputMethodAbility::GetInstance().ReplyOnConnectSystemCmd(id, code, agent); + return code; } ErrCode InputMethodCoreServiceImpl::StartInput(const InputClientInfoInner &clientInfoInner, bool isBindFromClient) @@ -103,15 +106,21 @@ ErrCode InputMethodCoreServiceImpl::StopInput(const sptr &channel return ERR_OK; } -ErrCode InputMethodCoreServiceImpl::IsEnable(bool &resultValue) +ErrCode InputMethodCoreServiceImpl::IsProxyImeEnable(int64_t id) { - resultValue = InputMethodAbility::GetInstance().IsEnable(); + bool resultValue = InputMethodAbility::GetInstance().IsEnable(); + IMSA_HILOGD("IsEnable id: %{public}" PRId64 " code: %{public}d", id, resultValue); + InputMethodAbility::GetInstance().ReplyIsEnable(id, ERR_OK, resultValue); return ERR_OK; } -ErrCode InputMethodCoreServiceImpl::IsPanelShown(const PanelInfo &panelInfo, bool &isShown) +ErrCode InputMethodCoreServiceImpl::IsPanelShown(int64_t id, const PanelInfo &panelInfo) { - return InputMethodAbility::GetInstance().IsPanelShown(panelInfo, isShown); + bool isShown = false; + int32_t code = InputMethodAbility::GetInstance().IsPanelShown(panelInfo, isShown); + IMSA_HILOGD("IsPanelShown id: %{public}" PRId64 " code: %{public}d", id, code); + InputMethodAbility::GetInstance().ReplyIsPanelShown(id, code, isShown); + return code; } ErrCode InputMethodCoreServiceImpl::OnClientInactive(const sptr &channel) @@ -132,6 +141,5 @@ ErrCode InputMethodCoreServiceImpl::OnSendPrivateData(const Value &Value) privateCommand = Value.valueMap; return InputMethodAbility::GetInstance().OnSendPrivateData(privateCommand); } - } // namespace MiscServices } // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/inputmethod_controller/include/task_sequencing.h b/frameworks/native/inputmethod_controller/include/task_sequencing.h new file mode 100644 index 0000000000000000000000000000000000000000..fb52c3c553119a538c0e46daa5f040709aafe5cc --- /dev/null +++ b/frameworks/native/inputmethod_controller/include/task_sequencing.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 INPUTMETHOD_IMF_TASK_SEQUENCING_H +#define INPUTMETHOD_IMF_TASK_SEQUENCING_H +#include +#include +#include +#include "event_handler.h" +#include "global.h" + +namespace OHOS { +namespace MiscServices { +using SyncTaskFunc = std::function; +using AsyncTaskFunc = std::function; + +class TaskSequencing { +public: + enum TaskStatus : int32_t { + TS_WAIT = 0, + TS_RUN, + TS_TERMINATE, + }; + + TaskSequencing(){}; + virtual ~TaskSequencing(){}; + + int32_t LineUp(SyncTaskFunc sync); + int32_t LineUp(AsyncTaskFunc async); + void LeaveLine(uint32_t id); +private: + struct TaskInfo { + bool isMainThread = false; + uint32_t id = 0; + SyncTaskFunc sync = nullptr; + AsyncTaskFunc async = nullptr; + std::mutex taskMutex; + std::shared_ptr cv = nullptr; + + TaskInfo(uint32_t id, SyncTaskFunc sync, AsyncTaskFunc async) + { + SetStatus(TaskStatus::TS_WAIT); + this->id = id; + this->sync = sync; + this->async = async; + + if (sync == nullptr) { + return; + } + + auto mainEventRunner = AppExecFwk::EventRunner::GetMainEventRunner(); + if (mainEventRunner == nullptr) { + IMSA_HILOGW("mainEventRunner is nullptr"); + } + auto Current = AppExecFwk::EventHandler::Current(); + if (Current == nullptr) { + IMSA_HILOGW("Current is nullptr"); + } + if (Current != nullptr && Current->GetEventRunner() == nullptr) { + IMSA_HILOGW("GetEventRunner is nullptr"); + } + if (mainEventRunner != nullptr && Current != nullptr && Current->GetEventRunner() != nullptr) { + uint64_t mtid = mainEventRunner->GetThreadId(); + uint64_t ctid = Current->GetEventRunner()->GetThreadId(); + isMainThread = (mtid == ctid); + } + cv = std::make_shared(); + } + void SetStatus(TaskStatus ts) + { + status.store(ts); + } + TaskStatus GetStatus() + { + return status.load(); + } + static uint32_t GetTaskId() + { + static std::atomic taskId = 0; + return ++taskId; + } + private: + std::atomic status; + }; + +private: + bool Clear(uint32_t id); + void ExecuteWaitTasks(std::shared_ptr info); + //-1 failed. 0 success,msg is not the team leader. 1 success,team leader + int32_t PushTask(SyncTaskFunc sync, AsyncTaskFunc aysnc, std::shared_ptr &info); + std::shared_ptr CreateTimeoutRunCv(); + std::shared_ptr GetCv(); + void DeleteCv(); + void HandleRunStateTimeout(std::shared_ptr runCv, uint32_t runId); + +private: + std::mutex taskQueueMutex_; + std::list> taskQueue_; + std::mutex timeoutRunMutex_; + std::shared_ptr timeoutRunCv_ = nullptr; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // INPUTMETHOD_IMF_TASK_SEQUENCING_H \ No newline at end of file diff --git a/frameworks/native/inputmethod_controller/src/input_method_controller.cpp b/frameworks/native/inputmethod_controller/src/input_method_controller.cpp index 3eaff23082eff06e2e8cc047d984cc2f7d5e09a7..5d24d1a66885901a76bfe097b820a518d022dd77 100644 --- a/frameworks/native/inputmethod_controller/src/input_method_controller.cpp +++ b/frameworks/native/inputmethod_controller/src/input_method_controller.cpp @@ -40,6 +40,7 @@ #include "system_ability_definition.h" #include "system_cmd_channel_stub.h" #include "input_method_tools.h" +#include "ffrt.h" #include "notify_service_impl.h" #include "on_input_stop_notify_proxy.h" @@ -301,7 +302,7 @@ int32_t InputMethodController::IsValidTextConfig(const TextConfig &textConfig) return ErrorCode::NO_ERROR; } -int32_t InputMethodController::Attach(sptr listener, const AttachOptions &attachOptions, +int32_t InputMethodController::AttachInner(sptr listener, const AttachOptions &attachOptions, const TextConfig &textConfig, ClientType type) { IMSA_HILOGI("isShowKeyboard %{public}d.", attachOptions.isShowKeyboard); @@ -352,6 +353,54 @@ int32_t InputMethodController::Attach(sptr listener, cons return ErrorCode::NO_ERROR; } +int32_t InputMethodController::Attach(sptr listener, const AttachOptions &attachOptions, + const TextConfig &textConfig, ClientType type) +{ + IMSA_HILOGD("sync."); + auto work = [this, listener, attachOptions, textConfig, type]() -> int32_t { + return AttachInner(listener, attachOptions, textConfig, type); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::AttachAsync(sptr listener, const AttachOptions &attachOptions, + const TextConfig &textConfig, ClientType type, AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this, listener, attachOptions, textConfig, type]() -> int32_t { + return AttachInner(listener, attachOptions, textConfig, type); + }; + return SubmitAsyncTask(work, callback); +} + +int32_t InputMethodController::SubmitSyncTask(std::function work) +{ + SyncTaskFunc task = [this, work](uint32_t id) -> int32_t { + int32_t ret = work(); + taskSeq_.LeaveLine(id); + return ret; + }; + return taskSeq_.LineUp(task); +} + +int32_t InputMethodController::SubmitAsyncTask(std::function work, std::function callback) +{ + AsyncTaskFunc task = [work, callback, this](uint32_t id, bool waitTimeout) -> void { + ffrt::submit([id, waitTimeout, work, callback, this]() { + if (waitTimeout) { + callback(ErrorCode::ERROR_CLIENT_NULL_POINTER); + return; + } + callback(work()); + taskSeq_.LeaveLine(id); + }); + }; + return taskSeq_.LineUp(task); +} + int32_t InputMethodController::ShowTextInputInner(const AttachOptions &attachOptions, ClientType type) { InputMethodSyncTrace tracer("IMC_ShowTextInput"); @@ -375,7 +424,7 @@ int32_t InputMethodController::ShowTextInputInner(const AttachOptions &attachOpt return ret; } -int32_t InputMethodController::HideTextInput() +int32_t InputMethodController::HideTextInputInner() { InputMethodSyncTrace tracer("IMC_HideTextInput"); if (!IsBound()) { @@ -388,7 +437,28 @@ int32_t InputMethodController::HideTextInput() return HideInput(clientInfo_.client); } -int32_t InputMethodController::HideCurrentInput() +int32_t InputMethodController::HideTextInput() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return HideTextInputInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::HideTextInputAsync(AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this]() -> int32_t { + return HideTextInputInner(); + }; + return SubmitAsyncTask(work, callback); +} + +int32_t InputMethodController::HideCurrentInputInner() { InputMethodSyncTrace tracer("IMC_HideCurrentInput"); IMSA_HILOGD("InputMethodController::HideCurrentInput"); @@ -409,7 +479,37 @@ int32_t InputMethodController::HideCurrentInput() return proxy->HideCurrentInputDeprecated(); } +int32_t InputMethodController::HideCurrentInput() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return HideCurrentInputInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::HideCurrentInputAsync(AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this]() -> int32_t { + return HideCurrentInputInner(); + }; + return SubmitAsyncTask(work, callback); +} + int32_t InputMethodController::ShowCurrentInput() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return ShowCurrentInputInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ShowCurrentInputInner() { InputMethodSyncTrace tracer("IMC_ShowCurrentInput"); if (!IsEditable()) { @@ -431,6 +531,27 @@ int32_t InputMethodController::ShowCurrentInput() } int32_t InputMethodController::Close() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return CloseInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::CloseAsync(AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this]() -> int32_t { + return CloseInner(); + }; + return SubmitAsyncTask(work, callback); +} + +int32_t InputMethodController::CloseInner() { if (IsBound()) { IMSA_HILOGI("start."); @@ -454,11 +575,25 @@ int32_t InputMethodController::Close() void InputMethodController::Reset() { - Close(); - RemoveDeathRecipient(); + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + CloseInner(); + RemoveDeathRecipient(); + return ErrorCode::NO_ERROR; + }; + SubmitSyncTask(work); } int32_t InputMethodController::RequestShowInput() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return RequestShowInputInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::RequestShowInputInner() { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -470,6 +605,15 @@ int32_t InputMethodController::RequestShowInput() } int32_t InputMethodController::RequestHideInput(bool isFocusTriggered) +{ + IMSA_HILOGD("sync."); + auto work = [this, isFocusTriggered]() -> int32_t { + return RequestHideInputInner(isFocusTriggered); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::RequestHideInputInner(bool isFocusTriggered) { auto proxy = TryGetSystemAbilityProxy(); if (proxy == nullptr) { @@ -481,6 +625,15 @@ int32_t InputMethodController::RequestHideInput(bool isFocusTriggered) } int32_t InputMethodController::DisplayOptionalInputMethod() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return DisplayOptionalInputMethodInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::DisplayOptionalInputMethodInner() { IMSA_HILOGD("InputMethodController::DisplayOptionalInputMethod start."); auto proxy = GetSystemAbilityProxy(); @@ -520,17 +673,32 @@ int32_t InputMethodController::ListInputMethodCommon(InputMethodStatus status, s int32_t InputMethodController::ListInputMethod(std::vector &props) { - IMSA_HILOGD("InputMethodController::listInputMethod start."); - return ListInputMethodCommon(ALL, props); + IMSA_HILOGD("sync."); + auto work = [this, &props]() -> int32_t { + return ListInputMethodCommon(ALL, props); + }; + return SubmitSyncTask(work); } int32_t InputMethodController::ListInputMethod(bool enable, std::vector &props) { - IMSA_HILOGI("enable: %{public}s.", enable ? "ENABLE" : "DISABLE"); - return ListInputMethodCommon(enable ? ENABLE : DISABLE, props); + IMSA_HILOGD("sync."); + auto work = [this, enable, &props]() -> int32_t { + return ListInputMethodCommon(enable ? ENABLE : DISABLE, props); + }; + return SubmitSyncTask(work); } int32_t InputMethodController::GetDefaultInputMethod(std::shared_ptr &property) +{ + IMSA_HILOGD("sync."); + auto work = [this, &property]() -> int32_t { + return GetDefaultInputMethodInner(property); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::GetDefaultInputMethodInner(std::shared_ptr &property) { InputMethodSyncTrace tracer("IMC_GetDefaultInputMethod"); IMSA_HILOGD("InputMethodController::GetDefaultInputMethod start."); @@ -549,6 +717,15 @@ int32_t InputMethodController::GetDefaultInputMethod(std::shared_ptr & } int32_t InputMethodController::GetInputMethodConfig(OHOS::AppExecFwk::ElementName &inputMethodConfig) +{ + IMSA_HILOGD("sync."); + auto work = [this, &inputMethodConfig]() -> int32_t { + return GetInputMethodConfigInner(inputMethodConfig); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::GetInputMethodConfigInner(OHOS::AppExecFwk::ElementName &inputMethodConfig) { InputMethodSyncTrace tracer("IMC_GetInputMethodConfig"); IMSA_HILOGD("InputMethodController::inputMethodConfig start."); @@ -561,36 +738,72 @@ int32_t InputMethodController::GetInputMethodConfig(OHOS::AppExecFwk::ElementNam } std::shared_ptr InputMethodController::GetCurrentInputMethod() +{ + std::shared_ptr property = nullptr; + auto work = [&property, this]() -> int32_t { + GetCurrentInputMethodInner(property); + return ErrorCode::NO_ERROR; + }; + if (SubmitSyncTask(work) != ErrorCode::NO_ERROR) { + IMSA_HILOGW("SubmitSyncTask failed"); + } + return property; +} + +void InputMethodController::GetCurrentInputMethodInner(std::shared_ptr &property) { InputMethodSyncTrace tracer("IMC_GetCurrentInputMethod"); IMSA_HILOGD("InputMethodController::GetCurrentInputMethod start."); auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { IMSA_HILOGE("proxy is nullptr!"); - return nullptr; + return; } Property propertyData; proxy->GetCurrentInputMethod(propertyData); - auto property = std::make_shared(propertyData); - return property; + property = std::make_shared(propertyData); } std::shared_ptr InputMethodController::GetCurrentInputMethodSubtype() +{ + std::shared_ptr subProperty = nullptr; + auto work = [&subProperty, this]() -> int32_t { + GetCurrentInputMethodSubtypeInner(subProperty); + return ErrorCode::NO_ERROR; + }; + if (SubmitSyncTask(work) != ErrorCode::NO_ERROR) { + IMSA_HILOGW("SubmitSyncTask failed"); + } + return subProperty; +} + +void InputMethodController::GetCurrentInputMethodSubtypeInner(std::shared_ptr &subProperty) { InputMethodSyncTrace tracer("IMC_GetCurrentInputMethodSubtype"); IMSA_HILOGD("InputMethodController::GetCurrentInputMethodSubtype start."); auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { IMSA_HILOGE("proxy is nullptr!"); - return nullptr; + return; } SubProperty subPropertyData; proxy->GetCurrentInputMethodSubtype(subPropertyData); - auto subProperty = std::make_shared(subPropertyData); - return subProperty; + subProperty = std::make_shared(subPropertyData); } bool InputMethodController::IsDefaultImeSet() +{ + IMSA_HILOGD("sync."); + bool ret = false; + auto work = [&ret, this]() -> int32_t { + ret = IsDefaultImeSetInner(); + return ErrorCode::NO_ERROR; + }; + SubmitSyncTask(work); + return ret; +} + +bool InputMethodController::IsDefaultImeSetInner() { IMSA_HILOGI("enter."); auto proxy = GetSystemAbilityProxy(); @@ -605,6 +818,16 @@ bool InputMethodController::IsDefaultImeSet() int32_t InputMethodController::EnableIme( const std::string &bundleName, const std::string &extensionName, EnabledStatus status) +{ + IMSA_HILOGD("sync."); + auto work = [bundleName, extensionName, status, this]() -> int32_t { + return EnableImeInner(bundleName, extensionName, status); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::EnableImeInner( + const std::string &bundleName, const std::string &extensionName, EnabledStatus status) { IMSA_HILOGI("enter."); auto proxy = GetSystemAbilityProxy(); @@ -735,7 +958,10 @@ void InputMethodController::RestoreClientInfoInSaDied() std::lock_guard lock(clientInfoLock_); isShowKeyboard = clientInfo_.isShowKeyboard; } - auto errCode = Attach(listener, isShowKeyboard, tempConfig); + AttachOptions attachOptions; + attachOptions.isShowKeyboard = isShowKeyboard; + attachOptions.requestKeyboardReason = RequestKeyboardReason::NONE; + auto errCode = AttachInner(listener, attachOptions, tempConfig); IMSA_HILOGI("attach end, errCode: %{public}d", errCode); return errCode == ErrorCode::NO_ERROR; }; @@ -1077,7 +1303,7 @@ int32_t InputMethodController::ShowSoftKeyboardInner(ClientType type) return proxy->ShowCurrentInput(type); } -int32_t InputMethodController::HideSoftKeyboard() +int32_t InputMethodController::HideSoftKeyboardInner() { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1093,7 +1319,28 @@ int32_t InputMethodController::HideSoftKeyboard() return proxy->HideCurrentInput(); } -int32_t InputMethodController::StopInputSession() +int32_t InputMethodController::HideSoftKeyboard() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return HideSoftKeyboardInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::HideSoftKeyboardAsync(AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this]() -> int32_t { + return HideSoftKeyboardInner(); + }; + return SubmitAsyncTask(work, callback); +} + +int32_t InputMethodController::StopInputSessionInner() { IMSA_HILOGI("start."); isEditable_.store(false); @@ -1105,7 +1352,37 @@ int32_t InputMethodController::StopInputSession() return proxy->StopInputSession(); } +int32_t InputMethodController::StopInputSession() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return StopInputSessionInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::StopInputSessionAsync(AsyncCallback callback) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this]() -> int32_t { + return StopInputSessionInner(); + }; + return SubmitAsyncTask(work, callback); +} + int32_t InputMethodController::ShowOptionalInputMethod() +{ + IMSA_HILOGD("sync."); + auto work = [this]() -> int32_t { + return ShowOptionalInputMethodInner(); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ShowOptionalInputMethodInner() { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1117,6 +1394,15 @@ int32_t InputMethodController::ShowOptionalInputMethod() } int32_t InputMethodController::ListInputMethodSubtype(const Property &property, std::vector &subProps) +{ + IMSA_HILOGD("sync."); + auto work = [property, &subProps, this]() -> int32_t { + return ListInputMethodSubtypeInner(property, subProps); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ListInputMethodSubtypeInner(const Property &property, std::vector &subProps) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1128,6 +1414,15 @@ int32_t InputMethodController::ListInputMethodSubtype(const Property &property, } int32_t InputMethodController::ListCurrentInputMethodSubtype(std::vector &subProps) +{ + IMSA_HILOGD("sync."); + auto work = [&subProps, this]() -> int32_t { + return ListCurrentInputMethodSubtypeInner(subProps); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ListCurrentInputMethodSubtypeInner(std::vector &subProps) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1140,6 +1435,16 @@ int32_t InputMethodController::ListCurrentInputMethodSubtype(std::vector int32_t { + return SwitchInputMethodInner(trigger, name, subName); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::SwitchInputMethodInner( + SwitchTrigger trigger, const std::string &name, const std::string &subName) { InputMethodSyncTrace tracer("IMC_SwitchInputMethod"); auto proxy = GetSystemAbilityProxy(); @@ -1463,11 +1768,23 @@ int32_t InputMethodController::SendFunctionKey(int32_t functionKey) } bool InputMethodController::IsInputTypeSupported(InputType type) +{ + IMSA_HILOGD("sync."); + bool ret = false; + auto work = [&ret, type, this]() -> int32_t { + ret = IsInputTypeSupportedInner(type); + return ErrorCode::NO_ERROR; + }; + SubmitSyncTask(work); + return ret; +} + +bool InputMethodController::IsInputTypeSupportedInner(InputType type) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { IMSA_HILOGE("proxy is nullptr!"); - return ErrorCode::ERROR_NULL_POINTER; + return false; } IMSA_HILOGI("type: %{public}d.", static_cast(type)); bool ret = false; @@ -1476,6 +1793,18 @@ bool InputMethodController::IsInputTypeSupported(InputType type) } bool InputMethodController::IsCurrentImeByPid(int32_t pid) +{ + IMSA_HILOGD("sync."); + bool ret = false; + auto work = [&ret, pid, this]() -> int32_t { + ret = IsCurrentImeByPidInner(pid); + return ErrorCode::NO_ERROR; + }; + SubmitSyncTask(work); + return ret; +} + +bool InputMethodController::IsCurrentImeByPidInner(int32_t pid) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1488,6 +1817,15 @@ bool InputMethodController::IsCurrentImeByPid(int32_t pid) } int32_t InputMethodController::StartInputType(InputType type) +{ + IMSA_HILOGD("sync."); + auto work = [type, this]() -> int32_t { + return StartInputTypeInner(type); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::StartInputTypeInner(InputType type) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1499,6 +1837,15 @@ int32_t InputMethodController::StartInputType(InputType type) } int32_t InputMethodController::IsPanelShown(const PanelInfo &panelInfo, bool &isShown) +{ + IMSA_HILOGD("sync."); + auto work = [panelInfo, &isShown, this]() -> int32_t { + return IsPanelShownInner(panelInfo, isShown); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::IsPanelShownInner(const PanelInfo &panelInfo, bool &isShown) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1714,6 +2061,15 @@ std::shared_ptr InputMethodController::GetMsgHandle } int32_t InputMethodController::GetInputMethodState(EnabledStatus &state) +{ + IMSA_HILOGD("sync."); + auto work = [&state, this]() -> int32_t { + return GetInputMethodStateinner(state); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::GetInputMethodStateinner(EnabledStatus &state) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { @@ -1741,16 +2097,51 @@ int32_t InputMethodController::ShowTextInput(ClientType type) int32_t InputMethodController::ShowTextInput(const AttachOptions &attachOptions, ClientType type) { - auto ret = ShowTextInputInner(attachOptions, type); - ReportClientShow(static_cast(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_INPUT), ret, type); - return ret; + IMSA_HILOGD("sync."); + auto work = [this, attachOptions, type]() -> int32_t { + auto ret = ShowTextInputInner(attachOptions, type); + ReportClientShow(static_cast(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_INPUT), ret, type); + return ret; + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ShowTextInputAsync(AsyncCallback callback, const AttachOptions &attachOptions, + ClientType type) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this, attachOptions, type]() -> int32_t { + auto ret = ShowTextInputInner(attachOptions, type); + ReportClientShow(static_cast(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_INPUT), ret, type); + return ret; + }; + return SubmitAsyncTask(work, callback); } int32_t InputMethodController::ShowSoftKeyboard(ClientType type) { - auto ret = ShowSoftKeyboardInner(type); - ReportClientShow(static_cast(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT), ret, type); - return ret; + IMSA_HILOGD("sync."); + auto work = [type, this]() -> int32_t { + return ShowSoftKeyboardInner(type); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::ShowSoftKeyboardAsync(AsyncCallback callback, ClientType type) +{ + if (callback == nullptr) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + IMSA_HILOGD("async."); + auto work = [this, type]() -> int32_t { + auto ret = ShowSoftKeyboardInner(type); + ReportClientShow(static_cast(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT), ret, type); + return ret; + }; + return SubmitAsyncTask(work, callback); } void InputMethodController::ReportClientShow(int32_t eventCode, int32_t errCode, ClientType type) @@ -1789,6 +2180,16 @@ void InputMethodController::UpdateTextPreviewState(bool isSupport) } int32_t InputMethodController::SendPrivateData(const std::unordered_map &privateCommand) +{ + IMSA_HILOGD("sync."); + auto work = [privateCommand, this]() -> int32_t { + return SendPrivateDataInner(privateCommand); + }; + return SubmitSyncTask(work); +} + +int32_t InputMethodController::SendPrivateDataInner( + const std::unordered_map &privateCommand) { auto proxy = GetSystemAbilityProxy(); if (proxy == nullptr) { diff --git a/frameworks/native/inputmethod_controller/src/task_sequencing.cpp b/frameworks/native/inputmethod_controller/src/task_sequencing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..992ce0f900b2efbd857b3d36a3b578fcecbe5d1c --- /dev/null +++ b/frameworks/native/inputmethod_controller/src/task_sequencing.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 "task_sequencing.h" +#include +#include "global.h" + +namespace OHOS { +namespace MiscServices { +constexpr uint32_t SYNC_MAIN_THREAD_TIMEOUT = 1000; +constexpr uint32_t SYNC_NON_MAIN_THREAD_TIMEOUT = 2000; +constexpr uint32_t RUN_TIMEOUT = 50; +constexpr size_t MAX_MSG_NUMBER = 1000; + +int32_t TaskSequencing::LineUp(SyncTaskFunc sync) +{ + IMSA_HILOGD("sync run"); + if (sync == nullptr) { + IMSA_HILOGE("parameter is empty"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + std::shared_ptr info = nullptr; + int ret = PushTask(sync, nullptr, info); + if (ret == -1) { + IMSA_HILOGE("sync queue full"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + if (ret == 0) { + uint32_t sync_timeout = SYNC_NON_MAIN_THREAD_TIMEOUT; + std::unique_lock lock(info->taskMutex); + if (info->isMainThread) { + sync_timeout = SYNC_MAIN_THREAD_TIMEOUT; + } + std::cv_status status = info->cv->wait_for(lock, std::chrono::milliseconds(sync_timeout)); + if (status == std::cv_status::timeout) { + if (!Clear(info->id)) { + IMSA_HILOGW("not cleared to message id:%{public}u", info->id); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + } + } + + if (info->GetStatus() != TS_WAIT) { + IMSA_HILOGW("sync current state is not TS_WAIT id:%{public}u", info->id); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + info->SetStatus(TS_RUN); + return info->sync(info->id); +} + +int32_t TaskSequencing::LineUp(AsyncTaskFunc async) +{ + IMSA_HILOGD("async run"); + if (async == nullptr) { + IMSA_HILOGE("parameter is empty"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + std::shared_ptr info = nullptr; + int ret = PushTask(nullptr, async, info); + if (ret == -1) { + IMSA_HILOGW("async queue full"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + if (ret == 0) { + return ErrorCode::NO_ERROR; + } + if (info->GetStatus() != TS_WAIT) { + IMSA_HILOGW("async current state is not TS_WAIT id:%{public}u", info->id); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + info->SetStatus(TS_RUN); + info->async(info->id, false); + return ErrorCode::NO_ERROR; +} + +void TaskSequencing::LeaveLine(uint32_t id) +{ + std::shared_ptr runCv = nullptr; + std::unique_lock lock(taskQueueMutex_); + if (taskQueue_.empty() || taskQueue_.front()->id != id) { + IMSA_HILOGW("queue is empty or id:%{public}u not equal", id); + return; + } + taskQueue_.pop_front(); + if (taskQueue_.empty()) { + return; + } + runCv = GetCv(); + if (runCv != nullptr) { + runCv->notify_one(); + return; + } + ExecuteWaitTasks(taskQueue_.front()); +} + +bool TaskSequencing::Clear(uint32_t id) +{ + uint32_t runId = 0; + std::shared_ptr runCv = nullptr; + IMSA_HILOGW("start clear id:%{public}u", id); + { + std::unique_lock lock(taskQueueMutex_); + if (taskQueue_.empty()) { + return false; + } + auto it = std::find_if(taskQueue_.begin(), taskQueue_.end(), + [id](const std::shared_ptr &info) { return info->id == id; }); + if (it == taskQueue_.end()) { + return false; + } + + std::shared_ptr info = taskQueue_.front(); + while (info->id != id) { + if (info->GetStatus() == TS_WAIT) { + info->SetStatus(TS_TERMINATE); + ExecuteWaitTasks(info); + } else if (info->GetStatus() == TS_RUN) { + runId = info->id; + runCv = CreateTimeoutRunCv(); + break; + } + taskQueue_.pop_front(); + info = taskQueue_.front(); + } + } + if (runCv != nullptr) { + HandleRunStateTimeout(runCv, runId); + Clear(id); + } + return true; +} + +void TaskSequencing::ExecuteWaitTasks(std::shared_ptr info) +{ + if (info->async) { + info->async(info->id, info->GetStatus() == TS_TERMINATE); + } else if (info->cv) { + info->cv->notify_one(); + } +} + +int32_t TaskSequencing::PushTask(SyncTaskFunc sync, AsyncTaskFunc async, std::shared_ptr &info) +{ + bool immediateExec = false; + { + std::unique_lock lock(taskQueueMutex_); + if (taskQueue_.size() >= MAX_MSG_NUMBER) { + IMSA_HILOGW("queue full"); + return -1; + } + immediateExec = taskQueue_.empty(); + info = std::make_shared(TaskInfo::GetTaskId(), sync, async); + taskQueue_.push_back(info); + } + IMSA_HILOGD("push task id:%{public}u", info->id); + return immediateExec ? 1 : 0; +} + +std::shared_ptr TaskSequencing::CreateTimeoutRunCv() +{ + std::unique_lock lock(timeoutRunMutex_); + timeoutRunCv_ = std::make_shared(); + return timeoutRunCv_; +} + +std::shared_ptr TaskSequencing::GetCv() +{ + std::unique_lock lock(timeoutRunMutex_); + return timeoutRunCv_; +} + +void TaskSequencing::DeleteCv() +{ + std::unique_lock lock(timeoutRunMutex_); + timeoutRunCv_ = nullptr; +} + +void TaskSequencing::HandleRunStateTimeout(std::shared_ptr runCv, uint32_t runId) +{ + { + std::unique_lock lock(timeoutRunMutex_); + std::cv_status status = runCv->wait_for(lock, std::chrono::milliseconds(RUN_TIMEOUT)); + if (status == std::cv_status::timeout) { + IMSA_HILOGW("first element wait timeout"); + } + } + { + std::unique_lock lock(taskQueueMutex_); + if (!taskQueue_.empty() && taskQueue_.front()->id == runId) { + taskQueue_.pop_front(); + } + } + DeleteCv(); +} +} // namespace MiscServices +} // namespace OHOS diff --git a/interfaces/inner_api/inputmethod_controller/BUILD.gn b/interfaces/inner_api/inputmethod_controller/BUILD.gn index bdead3f43948a6f795f901cebe50f359988ff0ea..f2ec6bd34283bd9be9dcdd54e483e303d59d6a8c 100644 --- a/interfaces/inner_api/inputmethod_controller/BUILD.gn +++ b/interfaces/inner_api/inputmethod_controller/BUILD.gn @@ -213,6 +213,7 @@ ohos_shared_library("inputmethod_client") { "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_method_utils.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/keyevent_consumer_service_impl.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/system_cmd_channel_service_impl.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/task_sequencing.cpp", ] cflags_cc = [ @@ -269,6 +270,7 @@ ohos_shared_library("inputmethod_client") { "cJSON:cjson", "c_utils:utils", "eventhandler:libeventhandler", + "ffrt:libffrt", "hilog:libhilog", "input:libmmi-client", "ipc:ipc_single", @@ -319,6 +321,7 @@ ohos_static_library("inputmethod_client_static") { "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_method_utils.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/keyevent_consumer_service_impl.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/system_cmd_channel_service_impl.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/task_sequencing.cpp", ] public_configs = [ ":inputmethod_client_native_public_config" ] @@ -342,6 +345,7 @@ ohos_static_library("inputmethod_client_static") { "cJSON:cjson", "c_utils:utils", "eventhandler:libeventhandler", + "ffrt:libffrt", "hilog:libhilog", "input:libmmi-client", "ipc:ipc_single", @@ -395,6 +399,7 @@ ohos_static_library("inputmethod_client_fuzz_static") { "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_method_utils.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/keyevent_consumer_service_impl.cpp", "${inputmethod_path}/frameworks/native/inputmethod_controller/src/system_cmd_channel_service_impl.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/task_sequencing.cpp", ] public_configs = [ ":inputmethod_client_native_public_config" ] @@ -417,6 +422,7 @@ ohos_static_library("inputmethod_client_fuzz_static") { "cJSON:cjson", "c_utils:utils", "eventhandler:libeventhandler", + "ffrt:libffrt", "hilog:libhilog", "input:libmmi-client", "ipc:ipc_single", diff --git a/interfaces/inner_api/inputmethod_controller/include/input_method_controller.h b/interfaces/inner_api/inputmethod_controller/include/input_method_controller.h index c09b21433a7150127e64d2de2b357354ed11e828..f4f20ace26aa36567145a0d78ab7f22744dc6e0a 100644 --- a/interfaces/inner_api/inputmethod_controller/include/input_method_controller.h +++ b/interfaces/inner_api/inputmethod_controller/include/input_method_controller.h @@ -44,6 +44,7 @@ #include "panel_info.h" #include "private_command_interface.h" #include "visibility.h" +#include "task_sequencing.h" namespace OHOS { namespace MiscServices { @@ -146,6 +147,7 @@ private: using PrivateDataValue = std::variant; using KeyEventCallback = std::function &keyEvent, bool isConsumed)>; using WindowScaleCallback = std::function; +using AsyncCallback = std::function; class InputMethodController : public RefBase, public PrivateCommandInterface { public: /** @@ -218,12 +220,10 @@ public: */ IMF_API int32_t Attach(sptr listener, bool isShowKeyboard, const TextConfig &textConfig, ClientType type = ClientType::INNER_KIT); - /** * @brief Set listener and bind IMSA with given states and textConfig. * - * This function is used to set listener and bind IMSA. - * Show soft keyboard when state is true, and customized attribute. + * Implement Attach interface, used for asynchronous calls * * @param listener Indicates the listener in order to manipulate text. * @param attachOptions Indicates the attachOptions, if you want to show soft keyboard, @@ -236,6 +236,23 @@ public: */ IMF_API int32_t Attach(sptr listener, const AttachOptions &attachOptions, const TextConfig &textConfig, ClientType type = ClientType::INNER_KIT); + /** + * @brief Asynchronous set listener and bind IMSA with given states and textConfig. + * + * Implement Attach interface, used for asynchronous calls + * + * @param listener Indicates the listener in order to manipulate text. + * @param attachOptions Indicates the attachOptions, if you want to show soft keyboard, + * please pass in true. + * @param textConfig Indicates the textConfig, such as input attribute, cursorInfo, range of + * text selection,windowId. + * @param type Indicates the type of caller. + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 15 + */ + IMF_API int32_t AttachAsync(sptr listener, const AttachOptions &attachOptions, + const TextConfig &textConfig, ClientType type, AsyncCallback callback); /** * @brief Show soft keyboard. * @@ -256,6 +273,18 @@ public: * @since 15 */ IMF_API int32_t ShowTextInput(const AttachOptions &attachOptions, ClientType type = ClientType::INNER_KIT); + /** + * @brief Asynchronous show soft keyboard. + * + * This function is used to show soft keyboard of current client. + * + * @param attachOptions Indicates the attachOptions, such as requestKeyboardReason + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 15 + */ + IMF_API int32_t ShowTextInputAsync(AsyncCallback callback, const AttachOptions &attachOptions, + ClientType type = ClientType::INNER_KIT); /** * @brief Hide soft keyboard. * @@ -266,6 +295,17 @@ public: */ IMF_API int32_t HideTextInput(); + /** + * @brief Asynchronous hide soft keyboard. + * + * This function is used to hide soft keyboard of current client, and keep binding. + * + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 6 + */ + IMF_API int32_t HideTextInputAsync(AsyncCallback callback); + /** * @brief Hide current input method, clear text listener and unbind IMSA. * @@ -277,6 +317,18 @@ public: */ IMF_API int32_t Close(); + /** + * @brief Asynchronous hide current input method, clear text listener and unbind IMSA. + * + * This function is used to stop input, whick will set listener to nullptr, + * hide current soft keyboard and unbind IMSA. + * + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 15 + */ + IMF_API int32_t CloseAsync(AsyncCallback callback); + /** * @brief A callback function when the cursor changes. * @@ -319,6 +371,7 @@ public: * @since 6 */ IMF_API int32_t OnConfigurationChange(Configuration info); + IMF_API void SetControllerListener(std::shared_ptr controllerListener); /** @@ -506,6 +559,18 @@ public: */ IMF_API int32_t ShowSoftKeyboard(ClientType type = ClientType::INNER_KIT); + /** + * @brief Asynchronous show soft keyboard. + * + * This function is used to show soft keyboard of current client. + * + * @param type Indicates the type of caller. + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 6 + */ + IMF_API int32_t ShowSoftKeyboardAsync(AsyncCallback callback, ClientType type = ClientType::INNER_KIT); + /** * @brief Hide soft keyboard. * @@ -516,6 +581,17 @@ public: */ IMF_API int32_t HideSoftKeyboard(); + /** + * @brief Hide soft keyboard. + * + * This function is used to hide soft keyboard of current client, and keep binding. + * + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 6 + */ + IMF_API int32_t HideSoftKeyboardAsync(AsyncCallback callback); + /** * @brief Stop current input session. * @@ -526,6 +602,17 @@ public: */ IMF_API int32_t StopInputSession(); + /** + * @brief Stop current input session. + * + * This function is used to stop current input session. + * + * @param callback Indicates the callback interface, return value execution code. + * @return Returns 0 for success, others for failure. + * @since 6 + */ + IMF_API int32_t StopInputSessionAsync(AsyncCallback callback); + /** * @brief Show input method setting extension dialog. * @@ -560,6 +647,17 @@ public: */ IMF_API int32_t HideCurrentInput(); + /** + * @brief Hide soft keyboard. + * + * This function is used to hide soft keyboard of current client, and keep binding. + * + * @return Returns 0 for success, others for failure. + * @deprecated since 9 + * @since 6 + */ + IMF_API int32_t HideCurrentInputAsync(AsyncCallback callback); + /** * @brief Request to show input method. * @@ -941,6 +1039,7 @@ public: * @since 16 */ IMF_API int32_t SendMessage(const ArrayBuffer &arrayBuffer); + int32_t RecvMessage(const ArrayBuffer &arrayBuffer); /** @@ -1029,9 +1128,38 @@ private: int32_t SetPreviewTextInner(const std::string &text, const Range &range); int32_t ShowTextInputInner(const AttachOptions &attachOptions, ClientType type); int32_t ShowSoftKeyboardInner(ClientType type); + int32_t AttachInner(sptr listener, const AttachOptions &attachOptions, + const TextConfig &textConfig, ClientType type = ClientType::INNER_KIT); + int32_t CloseInner(); + int32_t HideTextInputInner(); + int32_t HideSoftKeyboardInner(); + int32_t HideCurrentInputInner(); + int32_t StopInputSessionInner(); + void GetCurrentInputMethodInner(std::shared_ptr &property); + int32_t ListInputMethodSubtypeInner(const Property &property, std::vector &subProps); + int32_t ListCurrentInputMethodSubtypeInner(std::vector &subProps); + void GetCurrentInputMethodSubtypeInner(std::shared_ptr &subProperty); + int32_t GetDefaultInputMethodInner(std::shared_ptr &property); + int32_t GetInputMethodConfigInner(OHOS::AppExecFwk::ElementName &inputMethodConfig); + int32_t SwitchInputMethodInner(SwitchTrigger trigger, const std::string &name, const std::string &subName); + int32_t ShowOptionalInputMethodInner(); + int32_t ShowCurrentInputInner(); + int32_t RequestShowInputInner(); + int32_t RequestHideInputInner(bool isFocusTriggered); + int32_t DisplayOptionalInputMethodInner(); + bool IsInputTypeSupportedInner(InputType type); + int32_t StartInputTypeInner(InputType type); + int32_t IsPanelShownInner(const PanelInfo &panelInfo, bool &isShown); + bool IsCurrentImeByPidInner(int32_t pid); + bool IsDefaultImeSetInner(); + int32_t EnableImeInner(const std::string &bundleName, const std::string &extensionName, EnabledStatus status); + int32_t GetInputMethodStateinner(EnabledStatus &state); + int32_t SendPrivateDataInner(const std::unordered_map &privateCommand); void ReportClientShow(int32_t eventCode, int32_t errCode, ClientType type); void GetWindowScaleCoordinate(int32_t& x, int32_t& y, uint32_t windowId); int32_t ResponseDataChannel(uint64_t msgId, int32_t code, const ResponseData &data); + int32_t SubmitSyncTask(std::function work); + int32_t SubmitAsyncTask(std::function work, std::function callback); void CalibrateImmersiveParam(InputAttribute &inputAttribute); friend class InputDataChannelServiceImpl; @@ -1089,6 +1217,7 @@ private: static constexpr int32_t MAX_WAIT_TIME = 5000; BlockQueue keyEventQueue_{ MAX_WAIT_TIME }; + TaskSequencing taskSeq_; std::mutex msgHandlerMutex_; std::shared_ptr msgHandler_ = nullptr; std::mutex bindImeInfoLock_; diff --git a/services/include/imf_event_notification.h b/services/include/imf_event_notification.h new file mode 100644 index 0000000000000000000000000000000000000000..614246eef61445cbd61ab1713a70fe057d3a4e8a --- /dev/null +++ b/services/include/imf_event_notification.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * 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 INPUTMETHOD_IMF_EVENT_NOTIFICATION_H +#define INPUTMETHOD_IMF_EVENT_NOTIFICATION_H +#include +#include +#include +#include "iremote_object.h" +#include "refbase.h" +#include "global.h" + +namespace OHOS { +namespace MiscServices { +const uint32_t DEFAULT_TIMEOUT = 600; +using NotificationValue = std::variant>; + +struct NotificationResult { + int32_t code = ErrorCode::ERROR_CLIENT_NULL_POINTER; + NotificationValue value = std::monostate{}; +}; + +class ImfEventNotification { +public: + bool Wait(NotificationResult &result, uint32_t timeout = DEFAULT_TIMEOUT) + { + std::unique_lock lock(mutex_); + IMSA_HILOGD("wait count: %{public}d", count_); + if (--count_ < 0) { + if (std::cv_status::timeout == cv_.wait_for(lock, std::chrono::milliseconds(timeout))) { + IMSA_HILOGE("wait timeout"); + return false; + } + } + IMSA_HILOGD("wait complete"); + result = result_; + return true; + } + + void Post(const NotificationResult &result) + { + std::unique_lock lock(mutex_); + result_ = result; + IMSA_HILOGD("Post count: %{public}d", count_); + if (++count_ <= 0) { + IMSA_HILOGD("Post notify_one"); + cv_.notify_one(); + } + } + +private: + int count_ = 0; + std::mutex mutex_; + std::condition_variable cv_; + NotificationResult result_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // INPUTMETHOD_IMF_EVENT_NOTIFICATION_H \ No newline at end of file diff --git a/services/include/input_control_channel_service_impl.h b/services/include/input_control_channel_service_impl.h index 8fbc6e6e4af8b0e367bd12e010776e0598c38eb5..9e5e6435390cd9515f8d7fff8597c62617119579 100644 --- a/services/include/input_control_channel_service_impl.h +++ b/services/include/input_control_channel_service_impl.h @@ -22,6 +22,7 @@ #include "input_control_channel_stub.h" #include "iremote_object.h" #include "inputmethod_message_handler.h" +#include "imf_event_notification.h" namespace OHOS { namespace MiscServices { @@ -34,8 +35,12 @@ public: explicit InputControlChannelServiceImpl(int32_t userId); virtual ~InputControlChannelServiceImpl(); ErrCode HideKeyboardSelf() override; + ErrCode RespondOnConnectSystemCmd(int64_t id, int32_t code, const sptr &agent) override; + ErrCode RespondIsEnable(int64_t id, int32_t code, bool resultValue) override; + ErrCode RespondIsPanelShown(int64_t id, int32_t code, bool isShown) override; private: + void ContrlToCoreResponse(int64_t id, int32_t code, NotificationValue &value); int32_t userId_; }; } // namespace MiscServices diff --git a/services/include/input_method_system_ability.h b/services/include/input_method_system_ability.h index d358b514119505af60beccaa0b21213947b43716..565567570ccee427a862387e24e50e97ded4177a 100644 --- a/services/include/input_method_system_ability.h +++ b/services/include/input_method_system_ability.h @@ -25,6 +25,7 @@ #include "input_method_types.h" #include "user_session_manager.h" #include "input_type_manager.h" +#include "variant_util.h" namespace OHOS { namespace MiscServices { diff --git a/services/include/peruser_session.h b/services/include/peruser_session.h index 81b88c50fef3c518b50a464fa23244cf795d1586..bf4529f89d4e00dea130f90ce28f6795b45118e9 100644 --- a/services/include/peruser_session.h +++ b/services/include/peruser_session.h @@ -17,6 +17,7 @@ #define SERVICES_INCLUDE_PERUSER_SESSION_H #include +#include #include "block_queue.h" #include "client_group.h" @@ -28,7 +29,9 @@ #include "inputmethod_message_handler.h" #include "inputmethod_sysevent.h" #include "want.h" +#include "variant_util.h" #include "ime_state_manager.h" +#include "imf_event_notification.h" namespace OHOS { namespace Rosen { @@ -155,6 +158,7 @@ public: int32_t SpecialSendPrivateData(const std::unordered_map &privateCommand); uint64_t GetDisplayGroupId(uint64_t displayId); bool IsDefaultDisplayGroup(uint64_t displayId); + void ContrlToCoreResponse(uint32_t id, int32_t code, NotificationValue &value); bool IsNumkeyAutoInputApp(const std::string &bundleName); std::pair GetCurrentInputPattern(); bool IsPreconfiguredDefaultImeSpecified(const InputClientInfo &inputClientInfo); @@ -298,6 +302,20 @@ private: std::unordered_map> clientGroupMap_; std::mutex isNotifyFinishedLock_{}; BlockData isNotifyFinished_{ false, MAX_NOTIFY_TIME }; + + struct ContrlToCoreMsg { + uint32_t msgId = 0; + ImfEventNotification notify; + NotificationResult result; + static uint32_t GetId() + { + static uint32_t id = 0; + return ++id; + } + }; + int32_t AsyncRequest(std::function work, NotificationValue &value); + std::mutex msgMutex_; + std::unordered_map> msgQueue_; }; } // namespace MiscServices } // namespace OHOS diff --git a/services/src/input_control_channel_service_impl.cpp b/services/src/input_control_channel_service_impl.cpp index 49e65de28bb5490d9ff40faec4c868f4185a8a2a..47978211a669305560ce6b32635f1489c820f2a3 100644 --- a/services/src/input_control_channel_service_impl.cpp +++ b/services/src/input_control_channel_service_impl.cpp @@ -19,6 +19,8 @@ #include "inputmethod_message_handler.h" #include "message_parcel.h" #include "os_account_adapter.h" +#include "user_session_manager.h" +#include namespace OHOS { namespace MiscServices { @@ -48,5 +50,42 @@ ErrCode InputControlChannelServiceImpl::HideKeyboardSelf() MessageHandler::Instance()->SendMessage(msg); return ERR_OK; } + +ErrCode InputControlChannelServiceImpl::RespondOnConnectSystemCmd( + int64_t id, int32_t code, const sptr &agent) +{ + NotificationValue value = agent; + ContrlToCoreResponse(id, code, value); + return ERR_OK; +} + +ErrCode InputControlChannelServiceImpl::RespondIsEnable(int64_t id, int32_t code, bool resultValue) +{ + NotificationValue value = resultValue; + ContrlToCoreResponse(id, code, value); + return ERR_OK; +} + +ErrCode InputControlChannelServiceImpl::RespondIsPanelShown(int64_t id, int32_t code, bool isShown) +{ + NotificationValue value = isShown; + ContrlToCoreResponse(id, code, value); + return ERR_OK; +} + +void InputControlChannelServiceImpl::ContrlToCoreResponse(int64_t id, int32_t code, NotificationValue &value) +{ + const uint32_t OFFSET = 32; + uint32_t userId = static_cast(id >> OFFSET); + uint32_t msgId = static_cast(id & std::numeric_limits::max()); + IMSA_HILOGD("Response id: %{public}" PRIu64 " userId: %{public}u msgId: %{public}u code: %{public}d", + id, userId, msgId, code); + auto session = UserSessionManager::GetInstance().GetUserSession(userId); + if (session == nullptr) { + IMSA_HILOGE("UserId: %{public}u session is nullptr!", userId); + return; + } + session->ContrlToCoreResponse(msgId, code, value); +} } // namespace MiscServices } // namespace OHOS \ No newline at end of file diff --git a/services/src/input_method_system_ability.cpp b/services/src/input_method_system_ability.cpp index a32715ee42724982c63fb57ba9f48cdf559de560..a9b8df0539705914d6afa4cd0266d25b043c2928 100644 --- a/services/src/input_method_system_ability.cpp +++ b/services/src/input_method_system_ability.cpp @@ -16,6 +16,7 @@ #include "input_method_system_ability.h" #include +#include #include "securec.h" #include "unordered_map" diff --git a/services/src/peruser_session.cpp b/services/src/peruser_session.cpp index e0a497ce8e3b7313299cb8d8c7914008dcf7616e..1415d5a1499048bd5017ad282a10c63425bd91c5 100644 --- a/services/src/peruser_session.cpp +++ b/services/src/peruser_session.cpp @@ -46,6 +46,7 @@ #include "input_method_tools.h" #include "ime_state_manager_factory.h" #include "inputmethod_trace.h" +#include "variant_util.h" #include "notify_service_impl.h" namespace OHOS { @@ -511,11 +512,19 @@ void PerUserSession::DeactivateClient(const sptr &client) bool PerUserSession::IsProxyImeEnable() { auto data = GetReadyImeData(ImeType::PROXY_IME); - bool ret = false; if (data == nullptr || data->core == nullptr) { return false; } - data->core->IsEnable(ret); + + bool ret = false; + NotificationValue value; + int32_t code = AsyncRequest([data](int64_t id) { return data->core->IsProxyImeEnable(id);}, value); + if (code != ErrorCode::NO_ERROR) { + return ret; + } + if (!VariantUtil::GetValue(value, ret)) { + IMSA_HILOGE("GetValue failed"); + } return ret; } @@ -1519,8 +1528,21 @@ int32_t PerUserSession::IsPanelShown(const PanelInfo &panelInfo, bool &isShown) IMSA_HILOGE("ime not started!"); return ErrorCode::ERROR_IME_NOT_STARTED; } - return RequestIme(ime, RequestType::NORMAL, - [&ime, &panelInfo, &isShown] { return ime->core->IsPanelShown(panelInfo, isShown); }); + + auto exec = [&ime, &panelInfo, &isShown, this]() ->int32_t { + NotificationValue value; + int32_t ret = AsyncRequest( + [&ime, &panelInfo, &isShown](int64_t id) { return ime->core->IsPanelShown(id, panelInfo); }, value); + if (ret != ErrorCode::NO_ERROR) { + return ret; + } + if (VariantUtil::GetValue(value, isShown)) { + return ret; + } + IMSA_HILOGE("GetValue failed"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + }; + return RequestIme(ime, RequestType::NORMAL, exec); } bool PerUserSession::CheckSecurityMode() @@ -1559,8 +1581,20 @@ int32_t PerUserSession::OnConnectSystemCmd(const sptr &channel, s IMSA_HILOGE("ime: %{public}d is not exist!", ImeType::IME); return ErrorCode::ERROR_IME_NOT_STARTED; } - auto ret = RequestIme(data, RequestType::NORMAL, - [&data, &channel, &agent] { return data->core->OnConnectSystemCmd(channel, agent); }); + auto exec = [&data, &channel, &agent, this]() ->int32_t { + NotificationValue value; + int32_t ret = AsyncRequest( + [&data, &channel, &agent](int64_t id) { return data->core->OnConnectSystemCmd(id, channel); }, value); + if (ret != ErrorCode::NO_ERROR) { + return ret; + } + if (VariantUtil::GetValue(value, agent)) { + return ret; + } + IMSA_HILOGE("GetValue failed"); + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + }; + auto ret = RequestIme(data, RequestType::NORMAL, exec); IMSA_HILOGD("on connect systemCmd, ret: %{public}d.", ret); if (ret != ErrorCode::NO_ERROR) { IMSA_HILOGE("bind failed, ret: %{public}d!", ret); @@ -1569,6 +1603,42 @@ int32_t PerUserSession::OnConnectSystemCmd(const sptr &channel, s return ErrorCode::NO_ERROR; } +int32_t PerUserSession::AsyncRequest(std::function work, NotificationValue &value) +{ + const uint32_t OFFSET = 32; + int32_t ret = 0; + std::shared_ptr msg = std::make_shared(); + { + std::unique_lock insertLock(msgMutex_); + msg->msgId = ContrlToCoreMsg::GetId(); + msgQueue_.insert(std::make_pair(msg->msgId, msg)); + } + int64_t id = static_cast(userId_) << OFFSET; + id |= static_cast(msg->msgId); + IMSA_HILOGD("async request userid: %{public}d msgId: %{public}u id: %{public}" PRId64 "", + userId_, msg->msgId, id); + ret = work(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGW("task exec filed! userid: %{public}d msgId: %{public}u", userId_, msg->msgId); + std::unique_lock lock(msgMutex_); + msgQueue_.erase(msg->msgId); + return ret; + } + if (!msg->notify.Wait(msg->result)) { + ret = ErrorCode::ERROR_CLIENT_NULL_POINTER; + IMSA_HILOGW("async task timeout"); + } else { + IMSA_HILOGD("complete id: %{public}u ret: %{public}d", msg->msgId, ret); + ret = msg->result.code; + if (ret == ErrorCode::NO_ERROR) { + value = msg->result.value; + } + } + std::unique_lock lock(msgMutex_); + msgQueue_.erase(msg->msgId); + return ret; +} + bool PerUserSession::WaitForCurrentImeStop() { IMSA_HILOGI("start."); @@ -2335,6 +2405,22 @@ void PerUserSession::ClearRequestKeyboardReason(std::shared_ptr clientInfo->requestKeyboardReason = RequestKeyboardReason::NONE; } +void PerUserSession::ContrlToCoreResponse(uint32_t id, int32_t code, NotificationValue &value) +{ + IMSA_HILOGD("code: %{public}d id: %{public}u", code, id); + std::unique_lock lock(msgMutex_); + auto it = msgQueue_.find(id); + if (it == msgQueue_.end()) { + IMSA_HILOGW("not found id: %{public}u!", id); + return; + } + it->second->result.code = code; + if (code == ErrorCode::NO_ERROR) { + it->second->result.value = value; + } + it->second->notify.Post(it->second->result); +} + bool PerUserSession::IsNumkeyAutoInputApp(const std::string &bundleName) { return NumkeyAppsManager::GetInstance().NeedAutoNumKeyInput(userId_, bundleName); diff --git a/test/fuzztest/controlchannelstub_fuzzer/BUILD.gn b/test/fuzztest/controlchannelstub_fuzzer/BUILD.gn index 0ee0423fdac6c05c4f5ef67fae3a3fb630895e6e..cc1b2b6fc8f4103175214f603d01cf1699207e23 100644 --- a/test/fuzztest/controlchannelstub_fuzzer/BUILD.gn +++ b/test/fuzztest/controlchannelstub_fuzzer/BUILD.gn @@ -42,7 +42,10 @@ ohos_fuzztest("ControlChannelStubFuzzTest") { deps = [ "${inputmethod_path}/services:inputmethod_service_static" ] external_deps = [ + "ability_base:want", "c_utils:utils", + "data_share:datashare_common", + "data_share:datashare_consumer", "hilog:libhilog", "input:libmmi-client", "ipc:ipc_single", diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 624315eeba38dffdd72659a1cae0e587ab52659d..9305618484d6d3735c990cfbc64bfe5f5ad342b8 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -32,9 +32,11 @@ group("unittest") { "cpp_test:ImfHisysEventReporterTest", "cpp_test:InputMethodAbilityTest", "cpp_test:InputMethodAttachTest", + "cpp_test:ApiSequenceMultiThreadTest", "cpp_test:InputMethodControllerTest", "cpp_test:InputMethodDfxTest", "cpp_test:InputMethodEditorTest", + "cpp_test:InputMethodManagerCommandTest", "cpp_test:InputMethodMessageHandlerTest", "cpp_test:InputMethodPanelAdjustTest", "cpp_test:InputMethodPanelTest", @@ -48,10 +50,10 @@ group("unittest") { "cpp_test:OnDemandStartStopSaTest", "cpp_test:StringUtilsTest", "cpp_test:TaskManagerTest", + "cpp_test:TaskSequencingTest", "cpp_test:TextListenerInnerApiTest", "cpp_test:VirtualListenerTest", "cpp_test:WindowAdapterTest", - "cpp_test:InputMethodManagerCommandTest", "cpp_test/common:inputmethod_tdd_util", "napi_test/src:GetInputMethodJsTest", "resource/bundle_dependencies/editorBox:editorBox", diff --git a/test/unittest/cpp_test/BUILD.gn b/test/unittest/cpp_test/BUILD.gn index 7c2b9b6b0a7a0b59e303a78447e62fe023b37a0f..b14277940ad2bdfd83ad27253dae0fa6ddebe0fe 100644 --- a/test/unittest/cpp_test/BUILD.gn +++ b/test/unittest/cpp_test/BUILD.gn @@ -161,6 +161,67 @@ ohos_unittest("InputMethodAttachTest") { external_deps += [ "window_manager:libwm" ] } } + +ohos_unittest("ApiSequenceMultiThreadTest") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + module_out_path = module_output_path + + sources = [ + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_client_info.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_client_service_impl.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_data_channel_service_impl.cpp", + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/input_method_tools.cpp", + "${inputmethod_path}/services/src/input_control_channel_service_impl.cpp", + "${inputmethod_path}/test/common/src/keyboard_listener_test_impl.cpp", + "src/api_sequence_multi_thread_test_sync.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:input_control_channel_proxy", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:inputmethod_ability", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:input_client_stub", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:input_method_agent_proxy", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client_static", + "${inputmethod_path}/services:inputmethod_service_static", + "${inputmethod_path}/services/json:imf_json_static", + "${inputmethod_path}/test/common:inputmethod_test_common", + "${inputmethod_path}/test/unittest/cpp_test/common:inputmethod_tdd_util", + ] + + external_deps = [ + "ability_base:want", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:runtime", + "access_token:libaccesstoken_sdk", + "bundle_framework:appexecfwk_core", + "cJSON:cjson", + "c_utils:utils", + "data_share:datashare_common", + "data_share:datashare_consumer", + "googletest:gmock", + "googletest:gtest_main", + "hilog:libhilog", + "input:libmmi-client", + "napi:ace_napi", + "safwk:system_ability_fwk", + "window_manager:libdm", + ] + + if (window_manager_use_sceneboard) { + external_deps += [ "window_manager:libwm_lite" ] + } else { + external_deps += [ "window_manager:libwm" ] + } +} + ohos_unittest("InputMethodAbilityTest") { branch_protector_ret = "pac_ret" sanitize = { @@ -1434,8 +1495,9 @@ ohos_unittest("InputMethodManagerCommandTest") { ] deps = [ + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client", "${inputmethod_path}/test/unittest/cpp_test/common:inputmethod_tdd_util", - "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client" ] + ] external_deps = [ "bundle_framework:appexecfwk_core", @@ -1622,3 +1684,34 @@ ohos_unittest("ImaTextEditTest") { cflags = [ "-DWITH_SELINUX" ] } } + +ohos_unittest("TaskSequencingTest") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + module_out_path = module_output_path + + sources = [ + "${inputmethod_path}/frameworks/native/inputmethod_controller/src/task_sequencing.cpp", + "src/task_sequencing_test.cpp", + ] + + configs = [ ":module_private_config" ] + + deps = [ + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client", + "${inputmethod_path}/test/common:inputmethod_test_common", + "${inputmethod_path}/test/unittest/cpp_test/common:inputmethod_tdd_util", + ] + + external_deps = [ + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "eventhandler:libeventhandler", + "googletest:gtest_main", + "hilog:libhilog", + ] +} diff --git a/test/unittest/cpp_test/src/api_sequence_multi_thread_test_sync.cpp b/test/unittest/cpp_test/src/api_sequence_multi_thread_test_sync.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5bf11973f2d641d2e58b7bddb3637dd2fd1f207 --- /dev/null +++ b/test/unittest/cpp_test/src/api_sequence_multi_thread_test_sync.cpp @@ -0,0 +1,867 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#define private public +#define protected public +#include "input_method_ability.h" +#include "input_method_controller.h" +#include "input_method_system_ability.h" +#include "task_manager.h" +#undef private + +#include +#include +#include + +#include "global.h" +#include "identity_checker_mock.h" +#include "input_attribute.h" +#include "input_method_engine_listener_impl.h" +#include "input_method_system_ability_proxy.h" +#include "input_method_system_ability_stub.h" +#include "keyboard_listener_test_impl.h" +#include "tdd_util.h" +#include "text_listener.h" + +using namespace testing::ext; +namespace OHOS { +namespace MiscServices { +using namespace std::chrono; +using namespace testing::ext; +using namespace testing::mt; +namespace { + +constexpr int32_t SLEEP_INTERVAL_MS = 10; + +enum class TaskId : int32_t { + TASK_ID_START, + ATTACH_TASK, + ATTACH_ASYNC_TASK, + SHOW_TEXT_INPUT_TASK, + SHOW_TEXT_INPUT_ASYNC_TASK, + HIDE_TEXT_INPUT_TASK, + HIDE_TEXT_INPUT_ASYNC_TASK, + CLOSE_TASK, + CLOSE_ASYNC_TASK, + SHOW_SOFT_KEYBOARD_TASK, + SHOW_SOFT_KEYBOARD_ASYNC_TASK, + HIDE_SOFT_KEYBOARD_TASK, + HIDE_SOFT_KEYBOARD_ASYNC_TASK, + STOP_INPUT_SESSION_TASK, + STOP_INPUT_SESSION_ASYNC_TASK, + HIDE_CURRENT_INPUT_TASK, + HIDE_CURRENT_INPUT_ASYNC_TASK, + SHOW_CURRENT_INPUT_TASK, + RESET_TASK, + REQUEST_SHOW_INPUT_TASK, + REQUEST_HIDE_INPUT_TASK, + DISPLAY_OPTIONAL_INPUT_METHOD_TASK, + LIST_INPUT_METHOD_TASK, + LIST_INPUT_METHOD_SECOND_TASK, + GET_DEFAULT_INPUT_METHOD_TASK, + GET_INPUT_METHOD_CONFIG_TASK, + GET_CURRENT_INPUT_METHOD_TASK, + GET_CURRENT_INPUT_METHOD_SUNTYPE_TASK, + IS_DEFAULT_IME_SET_TASK, + ENABLE_IME_TASK, + SHOW_OPTIONAL_INPUT_METHOD_TASK, + LIST_INPUT_METHOD_SUBTYPE_TASK, + LIST_CURRENT_INPUT_METHOD_SUBTYPE_TASK, + SWITCH_INPUT_METHOD_TASK, + IS_INPUT_TYPE_SUPPORT_TASK, + IS_CURRENT_IME_BY_PID_TASK, + START_INPUT_TYPE_TASK, + IS_PANEL_SHOW_TASK, + GET_INPUT_METHOD_STATE_TASK, + SEND_PRIVATE_DATA_TASK, + TASK_ID_END, +}; + +// record the task post sequence +struct TaskRecorder { + std::mutex nextTaskIdMtx; + std::condition_variable nextTaskIdCond; + TaskId nextTaskId { 0 }; // post sequence + std::mutex completedTasksMtx; + std::queue completedTasks; // completed sequence +}; + +TaskId& operator++(TaskId& id) +{ + int32_t next = static_cast(id) + 1; + if (next > static_cast(TaskId::TASK_ID_END)) { + IMSA_HILOGE("TaskId overflow"); + next = static_cast(TaskId::TASK_ID_START); + } + id = static_cast(next); + return id; +} + +std::string GetThreadId() +{ + std::thread::id id = std::this_thread::get_id(); + std::ostringstream oss; + oss << id; + return oss.str(); +} + +void ExecuteTask(TaskRecorder &recorder, std::function task, TaskId currentTaskId) +{ + { + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL_MS)); + std::unique_lock lock(recorder.nextTaskIdMtx); + recorder.nextTaskIdCond.wait(lock, [&recorder, currentTaskId]() { + return recorder.nextTaskId == currentTaskId; + }); + } + + IMSA_HILOGI("task[%{public}d] start thread: %{public}s\n", currentTaskId, GetThreadId().c_str()); + task(); // execute task + IMSA_HILOGI("task[%{public}d] end : %{public}s\n", currentTaskId, GetThreadId().c_str()); + + { + ++recorder.nextTaskId; + } + + { + std::lock_guard lock(recorder.completedTasksMtx); + recorder.completedTasks.push(currentTaskId); + recorder.nextTaskIdCond.notify_all(); + } +} + +void ExecuteAsyncTask(TaskRecorder &recorder, std::function task, TaskId currentTaskId, + std::shared_ptr cv, std::mutex &waitLock) +{ + { + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL_MS)); + std::unique_lock lock(recorder.nextTaskIdMtx); + recorder.nextTaskIdCond.wait(lock, [&recorder, currentTaskId]() { + return recorder.nextTaskId == currentTaskId; + }); + } + + IMSA_HILOGI("task[%{public}d] start thread: %{public}s\n", currentTaskId, GetThreadId().c_str()); + auto ret = task(); // execute task + { + ++recorder.nextTaskId; + } + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGI("task[%{public}d] end thread: %{public}s\n", currentTaskId, GetThreadId().c_str()); + recorder.nextTaskIdCond.notify_all(); + return; + } + { + std::unique_lock cvLock(waitLock); + IMSA_HILOGI("task[%{public}d] thread: %{public}s waiting\n", currentTaskId, GetThreadId().c_str()); + cv->wait(cvLock); + } + IMSA_HILOGI("task[%{public}d] end thread: %{public}s\n", currentTaskId, GetThreadId().c_str()); + + { + // record task complete sequence + std::lock_guard lock(recorder.completedTasksMtx); + recorder.completedTasks.push(currentTaskId); + recorder.nextTaskIdCond.notify_all(); + } +} +} + +class ApiSequenceMultiThreadTest : public testing::Test { +public: + static sptr inputMethodController_; + static InputMethodAbility &inputMethodAbility_; + static sptr imsaProxy_; + static sptr imsa_; + static constexpr int32_t EACH_THREAD_CIRCULATION_TIME = 100; + static constexpr int32_t TEST_PID = 100; + static constexpr int32_t WAIT_TASK_EMPTY_TIMES = 100; + static constexpr int32_t WAIT_TASK_EMPTY_INTERVAL = 20; + static bool timeout_; + static std::shared_ptr textConfigHandler_; + static TaskRecorder recorder_; + + static void SetUpTestCase(void) + { + IMSA_HILOGI("ApiSequenceMultiThreadTest::SetUpTestCase"); + IdentityCheckerMock::ResetParam(); + imsa_ = new (std::nothrow) InputMethodSystemAbility(); + if (imsa_ == nullptr) { + return; + } + imsa_->OnStart(); + imsa_->userId_ = TddUtil::GetCurrentUserId(); + imsa_->identityChecker_ = std::make_shared(); + sptr serviceStub = imsa_; + imsaProxy_ = new InputMethodSystemAbilityProxy(serviceStub->AsObject()); + if (imsaProxy_ == nullptr) { + return; + } + IdentityCheckerMock::SetFocused(true); + + inputMethodAbility_.abilityManager_ = imsaProxy_; + TddUtil::InitCurrentImePermissionInfo(); + IdentityCheckerMock::SetBundleName(TddUtil::currentBundleNameMock_); + inputMethodAbility_.SetCoreAndAgent(); + std::shared_ptr runner = AppExecFwk::EventRunner::Create("ApiSequenceMultiThreadTest"); + textConfigHandler_ = std::make_shared(runner); + inputMethodAbility_.SetImeListener(std::make_shared(textConfigHandler_)); + + inputMethodController_ = InputMethodController::GetInstance(); + inputMethodController_->abilityManager_ = imsaProxy_; + } + static void TearDownTestCase(void) + { + IMSA_HILOGI("ApiSequenceMultiThreadTest::TearDownTestCase"); + IdentityCheckerMock::ResetParam(); + imsa_->OnStop(); + } + void SetUp() + { + IMSA_HILOGI("ApiSequenceMultiThreadTest::SetUp"); + TaskManager::GetInstance().SetInited(true); + } + void TearDown() + { + IMSA_HILOGI("ApiSequenceMultiThreadTest::TearDown"); + inputMethodController_->Close(); + BlockRetry(WAIT_TASK_EMPTY_INTERVAL, WAIT_TASK_EMPTY_TIMES, IsTaskEmpty); + TaskManager::GetInstance().Reset(); + } + static bool IsTaskEmpty() + { + return TaskManager::GetInstance().curTask_ == nullptr && TaskManager::GetInstance().amsTasks_.empty() && + TaskManager::GetInstance().imaTasks_.empty() && TaskManager::GetInstance().imsaTasks_.empty() && + TaskManager::GetInstance().innerTasks_.empty(); + } + + static void TestAttach() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + sptr textListener = new TextListener(); + auto ret = inputMethodController_->Attach(textListener, true); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + }, + TaskId::ATTACH_TASK); + } + + static void TestAttachAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + sptr textListener = new TextListener(); + AttachOptions attachOptions; + attachOptions.isShowKeyboard = true; + attachOptions.requestKeyboardReason = RequestKeyboardReason::NONE; + InputAttribute attribute; + attribute.inputPattern = InputAttribute::PATTERN_TEXT; + TextConfig textConfig; + textConfig.inputAttribute = attribute; + ClientType type = ClientType::INNER_KIT; + auto ret = inputMethodController_->AttachAsync(textListener, attachOptions, textConfig, type, + [cv](int32_t code) { + IMSA_HILOGI("notify_all code:%{public}d, currentTaskId:%{public}d\n", + code, TaskId::ATTACH_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::ATTACH_ASYNC_TASK, cv, waitLock); + } + + static void TestShowTextInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->ShowTextInput(ClientType::INNER_KIT); + }, + TaskId::SHOW_TEXT_INPUT_TASK); + } + + static void TestShowTextInputAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + AttachOptions attachOptions; + auto ret = inputMethodController_->ShowTextInputAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all code:%{public}d, currentTaskId:%{public}d\n", + code, TaskId::SHOW_TEXT_INPUT_ASYNC_TASK); + cv->notify_all(); + }, + attachOptions, ClientType::INNER_KIT); + return ret; + }, + TaskId::SHOW_TEXT_INPUT_ASYNC_TASK, cv, waitLock); + } + + static void TestHideTextInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->HideTextInput(); + }, + TaskId::HIDE_TEXT_INPUT_TASK); + } + + static void TestHideTextInputAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->HideTextInputAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", + code, TaskId::HIDE_TEXT_INPUT_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::HIDE_TEXT_INPUT_ASYNC_TASK, cv, waitLock); + } + + static void TestClose() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->Close(); + }, + TaskId::CLOSE_TASK); + } + + static void TestCloseAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->CloseAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", code, + TaskId::CLOSE_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::CLOSE_ASYNC_TASK, cv, waitLock); + } + + static void TestShowSoftKeyboard() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->ShowSoftKeyboard(); + }, + TaskId::SHOW_SOFT_KEYBOARD_TASK); + } + + static void TestShowSoftKeyboardAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->ShowSoftKeyboardAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", code, + TaskId::CLOSE_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::SHOW_SOFT_KEYBOARD_ASYNC_TASK, cv, waitLock); + } + + static void TestHideSoftKeyboard() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->HideSoftKeyboard(); + }, + TaskId::HIDE_SOFT_KEYBOARD_TASK); + } + + static void TestHideSoftKeyboardAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->HideSoftKeyboardAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", code, + TaskId::CLOSE_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::HIDE_SOFT_KEYBOARD_ASYNC_TASK, cv, waitLock); + } + + static void TestStopInputSession() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->StopInputSession(); + }, + TaskId::STOP_INPUT_SESSION_TASK); + } + + static void TestStopInputSessionAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->StopInputSessionAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", code, + TaskId::CLOSE_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::STOP_INPUT_SESSION_ASYNC_TASK, cv, waitLock); + } + + static void TestHideCurrentInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->HideCurrentInput(); + }, + TaskId::HIDE_CURRENT_INPUT_TASK); + } + + static void TestHideCurrentInputAsync() + { + auto cv = std::make_shared(); + std::mutex waitLock; + ExecuteAsyncTask( + recorder_, + [cv]() -> int32_t { + if (!inputMethodController_) { + return ErrorCode::ERROR_CLIENT_NULL_POINTER; + } + auto ret = inputMethodController_->HideCurrentInputAsync([cv](int32_t code) { + IMSA_HILOGI("notify_all, code:%{public}d, currentTaskId:%{public}d\n", code, + TaskId::CLOSE_ASYNC_TASK); + cv->notify_all(); + }); + return ret; + }, + TaskId::HIDE_CURRENT_INPUT_ASYNC_TASK, cv, waitLock); + } + + static void TestShowCurrentInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->ShowCurrentInput(); + }, + TaskId::SHOW_CURRENT_INPUT_TASK); + } + + static void TestReset() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->Reset(); + }, + TaskId::RESET_TASK); + } + + static void TestRequestShowInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->RequestShowInput(); + }, + TaskId::REQUEST_SHOW_INPUT_TASK); + } + + static void TestRequestHideInput() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->RequestHideInput(); + }, + TaskId::REQUEST_HIDE_INPUT_TASK); + } + + static void TestDisplayOptionalInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->DisplayOptionalInputMethod(); + }, + TaskId::DISPLAY_OPTIONAL_INPUT_METHOD_TASK); + } + + static void TestListInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + std::vector props; + inputMethodController_->ListInputMethod(props); + }, + TaskId::LIST_INPUT_METHOD_TASK); + } + + static void TestListInputMethodSecond() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + std::vector props; + inputMethodController_->ListInputMethod(false, props); + }, + TaskId::LIST_INPUT_METHOD_SECOND_TASK); + } + + static void TestGetDefaultInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + std::shared_ptr prop = nullptr; + inputMethodController_->GetDefaultInputMethod(prop); + }, + TaskId::GET_DEFAULT_INPUT_METHOD_TASK); + } + + static void TestGetInputMethodConfig() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + OHOS::AppExecFwk::ElementName inputMethodConfig; + inputMethodController_->GetInputMethodConfig(inputMethodConfig); + }, + TaskId::GET_INPUT_METHOD_CONFIG_TASK); + } + + static void TestGetCurrentInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->GetCurrentInputMethod(); + }, + TaskId::GET_CURRENT_INPUT_METHOD_TASK); + } + + static void TestGetCurrentInputMethodSubtype() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->GetCurrentInputMethodSubtype(); + }, + TaskId::GET_CURRENT_INPUT_METHOD_SUNTYPE_TASK); + } + + static void TestIsDefaultImeSet() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->IsDefaultImeSet(); + }, + TaskId::IS_DEFAULT_IME_SET_TASK); + } + + static void TestEnableIme() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + const std::string bundleName; + inputMethodController_->EnableIme(bundleName); + }, + TaskId::ENABLE_IME_TASK); + } + + static void TestShowOptionalInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->ShowOptionalInputMethod(); + }, + TaskId::SHOW_OPTIONAL_INPUT_METHOD_TASK); + } + + static void TestListInputMethodSubtype() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + Property property; + std::vector subProps; + inputMethodController_->ListInputMethodSubtype(property, subProps); + }, + TaskId::LIST_INPUT_METHOD_SUBTYPE_TASK); + } + + static void TestListCurrentInputMethodSubtype() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + std::vector subProps; + inputMethodController_->ListCurrentInputMethodSubtype(subProps); + }, + TaskId::LIST_CURRENT_INPUT_METHOD_SUBTYPE_TASK); + } + + static void TestSwitchInputMethod() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + SubProperty subProp; + subProp.name = "com.example.testIme"; + subProp.id = "InputMethodExtAbility"; + inputMethodController_->SwitchInputMethod(SwitchTrigger::CURRENT_IME, subProp.name, subProp.id); + }, + TaskId::SWITCH_INPUT_METHOD_TASK); + } + + static void TestIsInputTypeSupported() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->IsInputTypeSupported(InputType::NONE); + }, + TaskId::IS_INPUT_TYPE_SUPPORT_TASK); + } + + static void TestIsCurrentImeByPid() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->IsCurrentImeByPid(TEST_PID); + }, + TaskId::IS_CURRENT_IME_BY_PID_TASK); + } + + static void TestStartInputType() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + inputMethodController_->StartInputType(InputType::NONE); + }, + TaskId::START_INPUT_TYPE_TASK); + } + + static void TestIsPanelShown() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + const PanelInfo panelInfo; + bool isShown = false; + inputMethodController_->IsPanelShown(panelInfo, isShown); + }, + TaskId::IS_PANEL_SHOW_TASK); + } + + static void TestGetInputMethodState() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + EnabledStatus status = EnabledStatus::DISABLED; + inputMethodController_->GetInputMethodState(status); + }, + TaskId::GET_INPUT_METHOD_STATE_TASK); + } + + static void TestSendPrivateData() + { + ExecuteTask( + recorder_, + []() { + ASSERT_NE(inputMethodController_, nullptr); + std::unordered_map privateCommand; + inputMethodController_->SendPrivateData(privateCommand); + }, + TaskId::SEND_PRIVATE_DATA_TASK); + } +}; +sptr ApiSequenceMultiThreadTest::inputMethodController_; +InputMethodAbility &ApiSequenceMultiThreadTest::inputMethodAbility_ = InputMethodAbility::GetInstance(); +sptr ApiSequenceMultiThreadTest::imsaProxy_; +sptr ApiSequenceMultiThreadTest::imsa_; +bool ApiSequenceMultiThreadTest::timeout_ { false }; +std::shared_ptr ApiSequenceMultiThreadTest::textConfigHandler_ { nullptr }; +TaskRecorder ApiSequenceMultiThreadTest::recorder_; + +/** + * @tc.name: multiThreadAttachTest_001 + * @tc.desc: test ime Attach in multi-thread + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(ApiSequenceMultiThreadTest, multiThreadAttachTest_001, TestSize.Level0) +{ + ApiSequenceMultiThreadTest::recorder_.nextTaskId = TaskId::ATTACH_TASK; + SET_THREAD_NUM(1); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestAttach); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestAttachAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowTextInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowTextInputAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideTextInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideTextInputAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestClose); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestCloseAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowSoftKeyboard); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowSoftKeyboardAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideSoftKeyboard); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideSoftKeyboardAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestStopInputSession); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestStopInputSessionAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideCurrentInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestHideCurrentInputAsync); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowCurrentInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestReset); +} + +/** + * @tc.name: multiThreadAttachTest_002 + * @tc.desc: test ime Attach in multi-thread + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(ApiSequenceMultiThreadTest, multiThreadAttachTest_002, TestSize.Level0) +{ + ApiSequenceMultiThreadTest::recorder_.nextTaskId = TaskId::ATTACH_TASK; + SET_THREAD_NUM(1); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestRequestShowInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestRequestHideInput); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestDisplayOptionalInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestListInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestListInputMethodSecond); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestGetDefaultInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestGetInputMethodConfig); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestGetCurrentInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestGetCurrentInputMethodSubtype); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestIsDefaultImeSet); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestEnableIme); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestShowOptionalInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestListInputMethodSubtype); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestListCurrentInputMethodSubtype); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestSwitchInputMethod); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestIsInputTypeSupported); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestIsCurrentImeByPid); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestStartInputType); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestIsPanelShown); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestGetInputMethodState); + MTEST_ADD_TASK(RANDOM_THREAD_ID, ApiSequenceMultiThreadTest::TestSendPrivateData); +} + +/** + * @tc.name: multiThreadAttachTest_003 + * @tc.desc: test ime Attach in multi-thread + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(ApiSequenceMultiThreadTest, multiThreadAttachTest_003, TestSize.Level0) +{ + MTEST_POST_RUN(); + std::queue expectQueue; + for (TaskId i = TaskId::ATTACH_TASK; i < TaskId::TASK_ID_END; ++i) { + expectQueue.push(i); + } + EXPECT_EQ(expectQueue, ApiSequenceMultiThreadTest::recorder_.completedTasks); +} +} // namespace MiscServices +} // namespace OHOS diff --git a/test/unittest/cpp_test/src/ime_system_channel_test.cpp b/test/unittest/cpp_test/src/ime_system_channel_test.cpp index 5f0522e97f99236382ea0fc6707f9e4db27f6b61..feecdf44abffe29fb0557cbeaadfe0fc683d312e 100644 --- a/test/unittest/cpp_test/src/ime_system_channel_test.cpp +++ b/test/unittest/cpp_test/src/ime_system_channel_test.cpp @@ -93,6 +93,7 @@ HWTEST_F(ImeSystemChannelTest, testConnectSystemCmd002, TestSize.Level1) { IMSA_HILOGI("ImeSystemChannelTest testConnectSystemCmd002 Test START"); TokenScope scope(ImeSystemChannelTest::permissionTokenId_); + imeSystemChannel_->isSystemCmdConnect_.store(true); auto ret = imeSystemChannel_->ConnectSystemCmd(sysCmdListener_); EXPECT_EQ(ret, ErrorCode::NO_ERROR); } diff --git a/test/unittest/cpp_test/src/input_method_ability_test.cpp b/test/unittest/cpp_test/src/input_method_ability_test.cpp index cd15f0fce2d71944aa6b6aafa94a6a7ac55e8558..5856005a4cbd4be30ec630c1b2a2e580155d37a2 100644 --- a/test/unittest/cpp_test/src/input_method_ability_test.cpp +++ b/test/unittest/cpp_test/src/input_method_ability_test.cpp @@ -36,6 +36,7 @@ #include "iinput_data_channel.h" #include "identity_checker_mock.h" #include "input_attribute.h" +#include "input_control_channel_service_impl.h" #include "input_control_channel_stub.h" #include "input_data_channel_proxy.h" #include "input_data_channel_service_impl.h" @@ -55,6 +56,8 @@ using namespace testing::ext; namespace OHOS { namespace MiscServices { constexpr uint32_t DEALY_TIME = 1; +constexpr int32_t MAIN_USER_ID = 100; +constexpr uint32_t OFFSET = 32; class InputMethodAbilityTest : public testing::Test { public: static std::mutex imeListenerCallbackLock_; @@ -2139,5 +2142,93 @@ HWTEST_F(InputMethodAbilityTest, testClearBindInfo, TestSize.Level0) EXPECT_TRUE(inputMethodAbility_.dataChannelObject_ == nullptr); InputMethodAbilityTest::GetIMCDetachIMA(); } + +/** + * @tc.name: testReplyIsEnable + * @tc.desc: testReplyIsEnable + * @tc.type: FUNC + */ +HWTEST_F(InputMethodAbilityTest, testReplyIsEnable_001, TestSize.Level0) +{ + inputMethodAbility_.ClearInputControlChannel(); + int64_t id = 1; + int32_t code = 1; + bool enable = false; + inputMethodAbility_.ReplyIsEnable(id, code, enable); + EXPECT_FALSE(enable); + + sptr inputControlChannel = new (std::nothrow) InputControlChannelServiceImpl(MAIN_USER_ID); + sptr obj = inputControlChannel->AsObject(); + inputMethodAbility_.SetInputControlChannel(obj); + + inputMethodAbility_.ReplyIsEnable(id, code, enable); + EXPECT_FALSE(enable); +} + +/** + * @tc.name: testReplyIsPanelShown + * @tc.desc: testReplyIsPanelShown + * @tc.type: FUNC + */ +HWTEST_F(InputMethodAbilityTest, testReplyIsPanelShown_001, TestSize.Level0) +{ + inputMethodAbility_.ClearInputControlChannel(); + int64_t id = 1; + int32_t code = 1; + bool isShown = false; + inputMethodAbility_.ReplyIsPanelShown(id, code, isShown); + EXPECT_FALSE(isShown); + + sptr inputControlChannel = new (std::nothrow) InputControlChannelServiceImpl(MAIN_USER_ID); + sptr obj = inputControlChannel->AsObject(); + inputMethodAbility_.SetInputControlChannel(obj); + + inputMethodAbility_.ReplyIsPanelShown(id, code, isShown); + EXPECT_FALSE(isShown); +} + +/** + * @tc.name: testReplyOnConnectSystemCmd + * @tc.desc: testReplyOnConnectSystemCmd + * @tc.type: FUNC + */ +HWTEST_F(InputMethodAbilityTest, testReplyOnConnectSystemCmd_001, TestSize.Level0) +{ + inputMethodAbility_.ClearInputControlChannel(); + int64_t id = 1; + int32_t code = 1; + sptr agent = nullptr; + inputMethodAbility_.ReplyOnConnectSystemCmd(id, code, agent); + auto channel = inputMethodAbility_.GetInputControlChannel(); + EXPECT_TRUE(channel == nullptr); + + sptr inputControlChannel = new (std::nothrow) InputControlChannelServiceImpl(MAIN_USER_ID); + sptr obj = inputControlChannel->AsObject(); + inputMethodAbility_.SetInputControlChannel(obj); + + inputMethodAbility_.ReplyOnConnectSystemCmd(id, code, agent); + channel = inputMethodAbility_.GetInputControlChannel(); + EXPECT_TRUE(channel != nullptr); +} + +/** + * @tc.name: testContrlToCoreResponse + * @tc.desc: testContrlToCoreResponse + * @tc.type: FUNC + */ +HWTEST_F(InputMethodAbilityTest, testContrlToCoreResponse_001, TestSize.Level0) +{ + InputControlChannelServiceImpl impl(0); + int64_t id = 0; + int32_t code = 0; + bool isShow = false; + auto sessionMap = UserSessionManager::GetInstance().GetUserSessions(); + sessionMap.clear(); + auto ret = impl.RespondIsPanelShown(id, code, isShow); + EXPECT_EQ(ret, ERR_OK); + uint32_t userId = static_cast(id >> OFFSET); + auto session = UserSessionManager::GetInstance().GetUserSession(userId); + EXPECT_TRUE(session == nullptr); +} } // namespace MiscServices } // namespace OHOS diff --git a/test/unittest/cpp_test/src/input_method_controller_test.cpp b/test/unittest/cpp_test/src/input_method_controller_test.cpp index be683ab8d71f56d8e8500ff133f034f0b9009bcf..dae3820870634cfd3b561be1482822d7f22e06b1 100644 --- a/test/unittest/cpp_test/src/input_method_controller_test.cpp +++ b/test/unittest/cpp_test/src/input_method_controller_test.cpp @@ -2111,5 +2111,108 @@ HWTEST_F(InputMethodControllerTest, TestNotifyOnInputStopFinished001, TestSize.L EXPECT_EQ(ret, ErrorCode::NO_ERROR); UserSessionManager::GetInstance().userSessions_.clear(); } + +/** + * @tc.name: TestAttachAsync + * @tc.desc: Test AttachAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestAttachAsync, TestSize.Level0) +{ + sptr listener = nullptr; + AttachOptions opts; + TextConfig textConfig; + ClientType type = ClientType::INNER_KIT; + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->AttachAsync(listener, opts, textConfig, type, fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestHideTextInputAsync + * @tc.desc: Test HideTextInputAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestHideTextInputAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->HideTextInputAsync(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestHideCurrentInputAsync + * @tc.desc: Test HideCurrentInputAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestHideCurrentInputAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->HideCurrentInputAsync(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestCloseAsync + * @tc.desc: Test CloseAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestCloseAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->CloseAsync(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestHideSoftKeyboardAsync + * @tc.desc: Test HideSoftKeyboardAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestHideSoftKeyboardAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->HideSoftKeyboardAsync(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestStopInputSessionAsync + * @tc.desc: Test StopInputSessionAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestStopInputSessionAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + auto ret = inputMethodController_->StopInputSessionAsync(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestShowTextInputAsync + * @tc.desc: Test ShowTextInputAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestShowTextInputAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + AttachOptions opts; + ClientType type = ClientType::INNER_KIT; + auto ret = inputMethodController_->ShowTextInputAsync(fun, opts, type); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} + +/** + * @tc.name: TestShowSoftKeyboardAsync + * @tc.desc: Test ShowSoftKeyboardAsync + * @tc.type: FUNC + */ +HWTEST_F(InputMethodControllerTest, TestShowSoftKeyboardAsync, TestSize.Level0) +{ + AsyncCallback fun = nullptr; + ClientType type = ClientType::INNER_KIT; + auto ret = inputMethodController_->ShowSoftKeyboardAsync(fun, type); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); +} } // namespace MiscServices } // namespace OHOS diff --git a/test/unittest/cpp_test/src/input_method_private_member_test.cpp b/test/unittest/cpp_test/src/input_method_private_member_test.cpp index 65414c6e9b20c49412fa7f4019cbb98351011254..571a92bc7616c4ad662552d45b785bd8df76a7b0 100644 --- a/test/unittest/cpp_test/src/input_method_private_member_test.cpp +++ b/test/unittest/cpp_test/src/input_method_private_member_test.cpp @@ -20,6 +20,7 @@ #include "ime_info_inquirer.h" #include "input_method_agent_service_impl.h" #include "input_method_core_service_impl.h" +#include "input_data_channel_service_impl.h" #include "input_method_controller.h" #include "input_method_system_ability.h" #include "peruser_session.h" @@ -70,6 +71,7 @@ public: constexpr std::int32_t MAIN_USER_ID = 100; constexpr std::int32_t INVALID_USER_ID = 10001; constexpr std::int32_t INVALID_PROCESS_ID = -1; +constexpr std::int32_t PID = 100; std::atomic InputMethodPrivateMemberTest::tryLockFailCount_ = 0; std::shared_ptr InputMethodPrivateMemberTest::session_ = nullptr; void InputMethodPrivateMemberTest::TestImfStartIme() @@ -1944,5 +1946,107 @@ HWTEST_F(InputMethodPrivateMemberTest, TestIsLargeMemoryStateNeed_002, TestSize. userSession->UpdateLargeMemorySceneState(3); EXPECT_EQ(ret, ErrorCode::NO_ERROR); } + +/** + * @tc.name: IsProxyImeEnable + * @tc.desc: IsProxyImeEnable + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(InputMethodPrivateMemberTest, IsProxyImeEnable001, TestSize.Level0) +{ + IMSA_HILOGI("InputMethodPrivateMemberTest::StartImeTryLock001 start."); + session_ = std::make_shared(MAIN_USER_ID); + std::string bundleName = "bundleName"; + std::string extName = "extName"; + sptr coreStub = new InputMethodCoreServiceImpl(); + ASSERT_NE(coreStub, nullptr); + sptr core = coreStub; + auto imeData1 = std::make_shared(core, nullptr, nullptr, PID); + imeData1->imeStatus = ImeStatus::READY; + imeData1->ime = std::make_pair(bundleName, extName); + session_->imeData_.insert_or_assign(ImeType::IME, imeData1); + session_->IsProxyImeEnable(); +} + +/** + * @tc.name: OnConnectSystemCmd + * @tc.desc: OnConnectSystemCmd + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(InputMethodPrivateMemberTest, OnConnectSystemCmd001, TestSize.Level0) +{ + IMSA_HILOGI("InputMethodPrivateMemberTest::StartImeTryLock001 start."); + session_ = std::make_shared(MAIN_USER_ID); + std::string bundleName = "bundleName"; + std::string extName = "extName"; + sptr coreStub = new InputMethodCoreServiceImpl(); + ASSERT_NE(coreStub, nullptr); + sptr core = coreStub; + auto imeData1 = std::make_shared(core, nullptr, nullptr, PID); + imeData1->imeStatus = ImeStatus::READY; + imeData1->ime = std::make_pair(bundleName, extName); + session_->imeData_.insert_or_assign(ImeType::IME, imeData1); + + sptr channel = new (std::nothrow) InputDataChannelServiceImpl(); + sptr agent = new (std::nothrow) InputMethodAgentServiceImpl(); + session_->OnConnectSystemCmd(channel, agent); +} + +/** + * @tc.name: ContrlToCoreResponse + * @tc.desc: ContrlToCoreResponse + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(InputMethodPrivateMemberTest, ContrlToCoreResponse001, TestSize.Level0) +{ + IMSA_HILOGI("InputMethodPrivateMemberTest::StartImeTryLock001 start."); + session_ = std::make_shared(MAIN_USER_ID); + ASSERT_TRUE(session_ != nullptr); + session_->msgQueue_.clear(); + uint32_t id = 1; + int32_t code = 1; + NotificationValue value; + session_->ContrlToCoreResponse(id, code, value); +} + +/** + * @tc.name: AsyncRequest + * @tc.desc: AsyncRequest + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(InputMethodPrivateMemberTest, AsyncRequest001, TestSize.Level0) +{ + IMSA_HILOGI("InputMethodPrivateMemberTest::StartImeTryLock001 start."); + session_ = std::make_shared(MAIN_USER_ID); + ASSERT_TRUE(session_ != nullptr); + auto task = [](int64_t) -> int32_t { return ErrorCode::ERROR_NULL_POINTER; }; + NotificationValue value; + session_->AsyncRequest(task, value); +} + +/** + * @tc.name: AsyncRequest + * @tc.desc: AsyncRequest + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(InputMethodPrivateMemberTest, AsyncRequest002, TestSize.Level0) +{ + IMSA_HILOGI("InputMethodPrivateMemberTest::StartImeTryLock001 start."); + session_ = std::make_shared(MAIN_USER_ID); + ASSERT_TRUE(session_ != nullptr); + auto task = [](int64_t id) -> int32_t { return ErrorCode::ERROR_NULL_POINTER; }; + NotificationValue value; + std::shared_ptr msg = std::make_shared(); + msg->msgId = PerUserSession::ContrlToCoreMsg::GetId(); + msg->notify.count_ = -1; + session_->msgQueue_.clear(); + session_->msgQueue_.insert(std::make_pair(msg->msgId, msg)); + session_->AsyncRequest(task, value); +} } // namespace MiscServices } // namespace OHOS \ No newline at end of file diff --git a/test/unittest/cpp_test/src/task_sequencing_test.cpp b/test/unittest/cpp_test/src/task_sequencing_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ae482c4616351b74809230924685850cd1e2755 --- /dev/null +++ b/test/unittest/cpp_test/src/task_sequencing_test.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2021-2023 Huawei Device Co., Ltd. + * 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 "global.h" + #define private public +#include "task_sequencing.h" +#undef private +#include +#include +#include +#include +#include +#include + +using namespace testing::ext; +namespace OHOS { +namespace MiscServices { +class TaskSequencingTest : public testing::Test { +public: + static int32_t SyncCallBack(int32_t id) + { + return ErrorCode::NO_ERROR; + } + + static void AsyncCallBack(int32_t id, bool timeout) + { + } + + static void TriggerSignal(std::shared_ptr cv) + { + static constexpr int32_t sleepMilliseconds = 10; + std::this_thread::sleep_for(std::chrono::milliseconds(sleepMilliseconds)); + cv->notify_all(); + } +}; + +/** + * @tc.name: syncTask + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(TaskSequencingTest, syncTask, TestSize.Level1) +{ + const int32_t maxMsg = 1000; + int32_t ret = 0; + TaskSequencing task; + SyncTaskFunc fun = nullptr; + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); + + fun = SyncCallBack; + for (int i = 0; i < maxMsg; ++i) { + std::shared_ptr info = + std::make_shared(TaskSequencing::TaskInfo::GetTaskId(), fun, nullptr); + task.taskQueue_.push_back(info); + } + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); + task.taskQueue_.clear(); + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); +} + +/** + * @tc.name: asyncTask + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(TaskSequencingTest, asyncTask, TestSize.Level1) +{ + const int32_t maxMsg = 1000; + int32_t ret = 0; + TaskSequencing task; + AsyncTaskFunc fun = nullptr; + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); + + fun = AsyncCallBack; + for (int i = 0; i < maxMsg; ++i) { + std::shared_ptr info = + std::make_shared(TaskSequencing::TaskInfo::GetTaskId(), nullptr, fun); + task.taskQueue_.push_back(info); + } + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::ERROR_CLIENT_NULL_POINTER); + { + task.taskQueue_.clear(); + } + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); +} + +/** + * @tc.name: leaveLine + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(TaskSequencingTest, leaveLine, TestSize.Level1) +{ + uint32_t id = 2; + int32_t ret = 0; + TaskSequencing task; + AsyncTaskFunc fun = AsyncCallBack; + + task.LeaveLine(id); + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + task.LeaveLine(id); + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + task.LeaveLine(--id); + task.LeaveLine(++id); + + ret = task.LineUp(fun); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + ret = task.LineUp(SyncCallBack); + EXPECT_EQ(ret, ErrorCode::NO_ERROR); + task.LeaveLine(++id); + task.LeaveLine(++id); +} + +/** + * @tc.name: clear + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(TaskSequencingTest, clear, TestSize.Level1) +{ + uint32_t id = 2; + bool ret = 0; + TaskSequencing task; + AsyncTaskFunc fun = AsyncCallBack; + ret = task.Clear(id); + EXPECT_EQ(ret, false); + + EXPECT_EQ(task.LineUp(fun), ErrorCode::NO_ERROR); + ret = task.Clear(id); + EXPECT_EQ(ret, false); + + task.taskQueue_.front()->SetStatus(TaskSequencing::TaskStatus::TS_RUN); + EXPECT_EQ(task.LineUp(fun), ErrorCode::NO_ERROR); + EXPECT_EQ(task.LineUp(fun), ErrorCode::NO_ERROR); + id = task.taskQueue_.front()->id + 2; + ret = task.Clear(id); + EXPECT_EQ(ret, true); +} + +/** + * @tc.name: handleRunStateTimeout + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F(TaskSequencingTest, handleRunStateTimeout, TestSize.Level1) +{ + uint32_t id = 2; + TaskSequencing task; + AsyncTaskFunc fun = AsyncCallBack; + std::shared_ptr cv = task.CreateTimeoutRunCv(); + EXPECT_NE(cv, nullptr); + + task.HandleRunStateTimeout(cv, id); + EXPECT_EQ(task.LineUp(AsyncCallBack), ErrorCode::NO_ERROR); + + id = task.taskQueue_.front()->id; + cv = task.CreateTimeoutRunCv(); + EXPECT_NE(cv, nullptr); + task.HandleRunStateTimeout(cv, id - 1); + cv = task.CreateTimeoutRunCv(); + EXPECT_NE(cv, nullptr); + std::thread t(std::bind(TriggerSignal, cv)); + task.HandleRunStateTimeout(cv, id); + t.join(); +} +} // namespace MiscServices +} // namespace OHOS