From eaf11f9c083884c4af2a5871401ab9103e1d5965 Mon Sep 17 00:00:00 2001 From: zhaolinglan Date: Wed, 23 Jul 2025 20:06:48 +0800 Subject: [PATCH] add test Signed-off-by: zhaolinglan --- common/include/global.h | 9 + common/include/variant_util.h | 15 +- .../common/include/response_data_util.h | 74 ++ .../common/include/service_response_data.h | 165 +++++ .../common/src/service_response_data.cpp | 223 ++++++ .../IImaResponseChannel.idl | 20 + .../include/ima_response_channel_impl.h | 37 + .../include/ima_service_proxy.h | 81 ++ .../src/ima_response_channel_impl.cpp | 39 + .../src/ima_service_proxy.cpp | 259 +++++++ .../IImcResponseChannel.idl | 20 + .../include/imc_response_channel_impl.h | 37 + .../include/imc_service_proxy.h | 87 +++ .../src/imc_response_channel_impl.cpp | 39 + .../src/imc_service_proxy.cpp | 286 +++++++ .../inner_api/inputmethod_ability/BUILD.gn | 72 ++ .../inner_api/inputmethod_controller/BUILD.gn | 70 ++ .../task_manager/include/actions/sa_action.h | 109 +++ .../include/actions/sa_action_report.h | 52 ++ .../include/actions/sa_action_wait.h | 66 ++ services/task_manager/include/caller_info.h | 36 + .../task_manager/include/requester_manager.h | 78 ++ .../task_manager/include/sa_task_manager.h | 105 +++ services/task_manager/include/tasks/sa_task.h | 256 +++++++ .../task_manager/src/actions/sa_action.cpp | 208 ++++++ .../src/actions/sa_action_wait.cpp | 84 +++ .../task_manager/src/requester_manager.cpp | 162 ++++ services/task_manager/src/sa_task_manager.cpp | 484 ++++++++++++ services/task_manager/src/tasks/sa_task.cpp | 250 +++++++ test/unittest/BUILD.gn | 1 + test/unittest/cpp_test/BUILD.gn | 60 ++ .../cpp_test/src/sa_task_manager_test.cpp | 701 ++++++++++++++++++ 32 files changed, 4172 insertions(+), 13 deletions(-) create mode 100644 frameworks/native/common/include/response_data_util.h create mode 100644 frameworks/native/common/include/service_response_data.h create mode 100644 frameworks/native/common/src/service_response_data.cpp create mode 100644 frameworks/native/inputmethod_ability/IImaResponseChannel.idl create mode 100644 frameworks/native/inputmethod_ability/include/ima_response_channel_impl.h create mode 100644 frameworks/native/inputmethod_ability/include/ima_service_proxy.h create mode 100644 frameworks/native/inputmethod_ability/src/ima_response_channel_impl.cpp create mode 100644 frameworks/native/inputmethod_ability/src/ima_service_proxy.cpp create mode 100644 frameworks/native/inputmethod_controller/IImcResponseChannel.idl create mode 100644 frameworks/native/inputmethod_controller/include/imc_response_channel_impl.h create mode 100644 frameworks/native/inputmethod_controller/include/imc_service_proxy.h create mode 100644 frameworks/native/inputmethod_controller/src/imc_response_channel_impl.cpp create mode 100644 frameworks/native/inputmethod_controller/src/imc_service_proxy.cpp create mode 100644 services/task_manager/include/actions/sa_action.h create mode 100644 services/task_manager/include/actions/sa_action_report.h create mode 100644 services/task_manager/include/actions/sa_action_wait.h create mode 100644 services/task_manager/include/caller_info.h create mode 100644 services/task_manager/include/requester_manager.h create mode 100644 services/task_manager/include/sa_task_manager.h create mode 100644 services/task_manager/include/tasks/sa_task.h create mode 100644 services/task_manager/src/actions/sa_action.cpp create mode 100644 services/task_manager/src/actions/sa_action_wait.cpp create mode 100644 services/task_manager/src/requester_manager.cpp create mode 100644 services/task_manager/src/sa_task_manager.cpp create mode 100644 services/task_manager/src/tasks/sa_task.cpp create mode 100644 test/unittest/cpp_test/src/sa_task_manager_test.cpp diff --git a/common/include/global.h b/common/include/global.h index e7d955b5f..677fde232 100644 --- a/common/include/global.h +++ b/common/include/global.h @@ -80,6 +80,7 @@ enum { ERROR_JS_CB_NOT_REGISTER, // only for hiSysEvent ERROR_DEAL_TIMEOUT, // only for hiSysEvent ERROR_IPC_REMOTE_NULLPTR, + ERROR_INVALID_VARIANT_TYPE, ERROR_IMA_BEGIN, ERROR_IME, @@ -98,6 +99,8 @@ enum { ERROR_INVALID_RANGE, ERROR_CLIENT_NOT_BOUND, ERROR_IMC_NULLPTR, + ERROR_IMC_SERVICE_RESPONSE_TIMEOUT, + ERROR_REQUEST_INTERRUPTED, ERROR_IMC_END, ERROR_IMSA_BEGIN, @@ -131,6 +134,12 @@ enum { ERROR_IME_NOT_FOUND, ERROR_OPERATE_SYSTEM_IME, ERROR_SWITCH_IME, + + ERROR_IMSA_REQUESTER_NOT_FOUND, + ERROR_IMSA_TASK_TIMEOUT, + ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED, + ERROR_IMSA_REQUEST_COUNT_OVER_MAXIMUM, + ERROR_SA_POST_TASK_FAILED, ERROR_IMSA_END, }; }; // namespace ErrorCode diff --git a/common/include/variant_util.h b/common/include/variant_util.h index 347ba8da8..86fd58e3f 100644 --- a/common/include/variant_util.h +++ b/common/include/variant_util.h @@ -25,19 +25,8 @@ namespace OHOS { namespace MiscServices { class VariantUtil { public: - template - static bool GetValue( - const std::variant &input, T &output) - { - if (!std::holds_alternative(input)) { - return false; - } - output = std::get(input); - return true; - } - - template - static bool GetValue(const ResponseData &input, T &output) + template + static bool GetValue(const std::variant &input, T &output) { if (!std::holds_alternative(input)) { return false; diff --git a/frameworks/native/common/include/response_data_util.h b/frameworks/native/common/include/response_data_util.h new file mode 100644 index 000000000..f25ab7cbd --- /dev/null +++ b/frameworks/native/common/include/response_data_util.h @@ -0,0 +1,74 @@ +/* + * 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 IMF_FRAMEWORKS_RESPONSE_DATA_UTIL_H +#define IMF_FRAMEWORKS_RESPONSE_DATA_UTIL_H + +#include +#include + +#include "input_method_property.h" +#include "input_method_utils.h" + +namespace OHOS { +namespace MiscServices { +inline constexpr size_t MAX_SIZE = 102400; +class ResponseDataUtil { +public: + template static bool Unmarshall(Parcel &in, std::vector &out) + { + int32_t len = 0; + if (!in.ReadInt32(len)) { + return false; + } + if (len < 0) { + return false; + } + auto size = static_cast(len); + if (size > MAX_SIZE) { + return false; + } + out.clear(); + for (size_t i = 0; i < size; ++i) { + T value; + if (!value.ReadFromParcel(in)) { + return false; + } + out.push_back(value); + } + return true; + } + + template static bool Marshall(const std::vector &in, Parcel &out) + { + auto size = in.size(); + if (size > INT32_MAX) { + return false; + } + if (!out.WriteInt32(static_cast(size))) { + return false; + } + for (const auto &it : in) { + if (!it.Marshalling(out)) { + return false; + } + } + return true; + } +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_FRAMEWORKS_RESPONSE_DATA_UTIL_H diff --git a/frameworks/native/common/include/service_response_data.h b/frameworks/native/common/include/service_response_data.h new file mode 100644 index 000000000..31c564f12 --- /dev/null +++ b/frameworks/native/common/include/service_response_data.h @@ -0,0 +1,165 @@ +/* + * 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 IMF_FRAMEWORKS_SERVICE_RESPONSE_DATA_H +#define IMF_FRAMEWORKS_SERVICE_RESPONSE_DATA_H + +#include +#include +#include +#include + +#include "input_method_property.h" +#include "input_method_utils.h" +#include "response_data_util.h" + +namespace OHOS { +namespace MiscServices { +using RequestId = uint32_t; +using RequestFunc = std::function; + +struct StartInputResponse : public Parcelable { + sptr agent{ nullptr }; + int64_t pid{ 0 }; + std::string bundleName; + void Set(sptr imeAgent, int64_t imePid, const std::string &imeBundleName); + bool ReadFromParcel(Parcel &in); + bool Marshalling(Parcel &out) const override; + static StartInputResponse *Unmarshalling(Parcel &in); +}; + +struct InputStartInfo : public Parcelable { + bool isInputStart{ false }; + uint32_t callingWindowId{ 0 }; + int32_t requestKeyboardReason{ 0 }; + void Set(bool inputStart, uint32_t id, int32_t reason); + bool ReadFromParcel(Parcel &in); + bool Marshalling(Parcel &out) const override; + static InputStartInfo *Unmarshalling(Parcel &in); +}; + +enum ServiceDataType : int32_t { + TYPE_MONOSTATE = 0, + + // basic types + TYPE_BOOL = 1, + TYPE_INT32 = 2, + TYPE_UINT32 = 3, + TYPE_INT64 = 4, + TYPE_UINT64 = 5, + TYPE_REMOTE_OBJECT = 6, + + // inner types + TYPE_PROPERTY = 7, + TYPE_PROPERTIES = 8, + TYPE_SUB_PROPERTY = 9, + TYPE_SUB_PROPERTIES = 10, + TYPE_START_INPUT_RESPONSE = 11, + TYPE_INPUT_START_INFO = 12, + + TYPE_END, +}; + +using ServiceResponseData = + std::variant, Property, + std::vector, SubProperty, std::vector, StartInputResponse, InputStartInfo>; + +struct ServiceResponse { + int32_t result{ 0 }; + ServiceResponseData responseData{ std::monostate{} }; +}; + +struct PendingRequest { + std::promise promise; +}; + +using UnmarshalFunc = std::function; +struct ServiceResponseDataInner : public Parcelable { +public: + bool ReadFromParcel(Parcel &in); + bool Marshalling(Parcel &out) const override; + static ServiceResponseDataInner *Unmarshalling(Parcel &in); + ServiceResponseData data; + +private: + static const std::unordered_map UNMARSHAL_FUNCTION_MAP; +}; + +struct ServiceResponseWriter { + Parcel &out; + bool result = true; + + explicit ServiceResponseWriter(Parcel &parcel) : out(parcel) + { + } + + void operator()(std::monostate val) + { + IMSA_HILOGD("no need to marshal"); + result = true; + } + void operator()(bool val) + { + result = out.WriteBool(val); + } + void operator()(int32_t val) + { + result = out.WriteInt32(val); + } + void operator()(uint32_t val) + { + result = out.WriteUint32(val); + } + void operator()(int64_t val) + { + result = out.WriteInt64(val); + } + void operator()(uint64_t val) + { + result = out.WriteUint64(val); + } + void operator()(sptr val) + { + result = static_cast(&out)->WriteRemoteObject(val); + } + void operator()(const Property &val) + { + result = val.Marshalling(out); + } + void operator()(const std::vector &val) + { + result = ResponseDataUtil::Marshall(val, out); + } + void operator()(const SubProperty &val) + { + result = val.Marshalling(out); + } + void operator()(const std::vector &val) + { + result = ResponseDataUtil::Marshall(val, out); + } + void operator()(const StartInputResponse &val) + { + result = val.Marshalling(out); + } + void operator()(const InputStartInfo &val) + { + result = val.Marshalling(out); + } +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_FRAMEWORKS_SERVICE_RESPONSE_DATA_H diff --git a/frameworks/native/common/src/service_response_data.cpp b/frameworks/native/common/src/service_response_data.cpp new file mode 100644 index 000000000..fd2f73718 --- /dev/null +++ b/frameworks/native/common/src/service_response_data.cpp @@ -0,0 +1,223 @@ +/* + * 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 "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +const std::unordered_map ServiceResponseDataInner::UNMARSHAL_FUNCTION_MAP = { + { static_cast(TYPE_MONOSTATE), [](Parcel &in, ServiceResponseData &out) { out = std::monostate{}; } }, + { static_cast(TYPE_BOOL), [](Parcel &in, ServiceResponseData &out) { out = in.ReadBool(); } }, + { static_cast(TYPE_INT32), [](Parcel &in, ServiceResponseData &out) { out = in.ReadInt32(); } }, + { static_cast(TYPE_UINT32), [](Parcel &in, ServiceResponseData &out) { out = in.ReadUint32(); } }, + { static_cast(TYPE_INT64), [](Parcel &in, ServiceResponseData &out) { out = in.ReadInt64(); } }, + { static_cast(TYPE_UINT64), [](Parcel &in, ServiceResponseData &out) { out = in.ReadUint64(); } }, + { static_cast(TYPE_REMOTE_OBJECT), + [](Parcel &in, ServiceResponseData &out) { out = static_cast(&in)->ReadRemoteObject(); } }, + { static_cast(TYPE_PROPERTY), + [](Parcel &in, ServiceResponseData &out) { + Property value; + value.ReadFromParcel(in); + out = value; + } }, + { static_cast(TYPE_PROPERTIES), + [](Parcel &in, ServiceResponseData &out) { + std::vector value; + ResponseDataUtil::Unmarshall(in, value); + out = value; + } }, + { static_cast(TYPE_SUB_PROPERTY), + [](Parcel &in, ServiceResponseData &out) { + SubProperty value; + value.ReadFromParcel(in); + out = value; + } }, + { static_cast(TYPE_SUB_PROPERTIES), + [](Parcel &in, ServiceResponseData &out) { + std::vector value; + ResponseDataUtil::Unmarshall(in, value); + out = value; + } }, + { static_cast(TYPE_START_INPUT_RESPONSE), + [](Parcel &in, ServiceResponseData &out) { + StartInputResponse value; + value.ReadFromParcel(in); + out = value; + } }, + { static_cast(TYPE_INPUT_START_INFO), + [](Parcel &in, ServiceResponseData &out) { + InputStartInfo value; + value.ReadFromParcel(in); + out = value; + } }, +}; +bool ServiceResponseDataInner::ReadFromParcel(Parcel &in) +{ + int32_t valueType = 0; + if (!in.ReadInt32(valueType)) { + return false; + } + if (valueType < static_cast(ServiceDataType::TYPE_MONOSTATE) || + valueType >= static_cast(ServiceDataType::TYPE_END)) { + IMSA_HILOGE("invalid value type"); + return false; + } + auto iter = UNMARSHAL_FUNCTION_MAP.find(valueType); + if (iter == UNMARSHAL_FUNCTION_MAP.end()) { + return false; + } + auto handler = iter->second; + if (handler == nullptr) { + return false; + } + handler(in, data); + return true; +} + +bool ServiceResponseDataInner::Marshalling(Parcel &out) const +{ + int32_t valueType = static_cast(data.index()); + if (valueType < static_cast(ServiceDataType::TYPE_MONOSTATE) || + valueType >= static_cast(ServiceDataType::TYPE_END)) { + IMSA_HILOGE("invalid value type"); + return false; + } + if (!out.WriteInt32(valueType)) { + IMSA_HILOGE("failed to write valueType"); + return false; + } + if (valueType == static_cast(ServiceDataType::TYPE_MONOSTATE)) { + return true; + } + ServiceResponseWriter writer{ out }; + std::visit(writer, data); + if (!writer.result) { + IMSA_HILOGE("failed to write response data"); + return false; + } + return true; +} + +ServiceResponseDataInner *ServiceResponseDataInner::Unmarshalling(Parcel &in) +{ + auto data = new (std::nothrow) ServiceResponseDataInner(); + if (data == nullptr) { + return nullptr; + } + if (!data->ReadFromParcel(in)) { + delete data; + data = nullptr; + } + return data; +} + +void StartInputResponse::Set(sptr imeAgent, int64_t imePid, const std::string &imeBundleName) +{ + agent = imeAgent; + pid = imePid; + bundleName = imeBundleName; +} + +bool StartInputResponse::ReadFromParcel(Parcel &in) +{ + agent = (static_cast(&in))->ReadRemoteObject(); + if (agent == nullptr) { + return false; + } + if (!in.ReadInt64(pid)) { + return false; + } + if (!in.ReadString(bundleName)) { + return false; + } + return true; +} + +bool StartInputResponse::Marshalling(Parcel &out) const +{ + if (!(static_cast(&out))->WriteRemoteObject(agent)) { + return false; + } + if (!out.WriteInt64(pid)) { + return false; + } + if (!out.WriteString(bundleName)) { + return false; + } + return true; +} + +StartInputResponse *StartInputResponse::Unmarshalling(Parcel &in) +{ + auto data = new (std::nothrow) StartInputResponse(); + if (data == nullptr) { + return nullptr; + } + if (!data->ReadFromParcel(in)) { + delete data; + return nullptr; + } + return data; +} + +void InputStartInfo::Set(bool inputStart, uint32_t id, int32_t reason) +{ + isInputStart = inputStart; + callingWindowId = id; + requestKeyboardReason = reason; +} + +bool InputStartInfo::ReadFromParcel(Parcel &in) +{ + if (!in.ReadBool(isInputStart)) { + return false; + } + if (!in.ReadUint32(callingWindowId)) { + return false; + } + if (!in.ReadInt32(requestKeyboardReason)) { + return false; + } + return true; +} + +bool InputStartInfo::Marshalling(Parcel &out) const +{ + if (!out.WriteBool(isInputStart)) { + return false; + } + if (!out.WriteUint32(callingWindowId)) { + return false; + } + if (!out.WriteInt32(requestKeyboardReason)) { + return false; + } + return true; +} + +InputStartInfo *InputStartInfo::Unmarshalling(Parcel &in) +{ + auto data = new (std::nothrow) InputStartInfo(); + if (data == nullptr) { + return nullptr; + } + if (!data->ReadFromParcel(in)) { + delete data; + return nullptr; + } + return data; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/inputmethod_ability/IImaResponseChannel.idl b/frameworks/native/inputmethod_ability/IImaResponseChannel.idl new file mode 100644 index 000000000..475fcd212 --- /dev/null +++ b/frameworks/native/inputmethod_ability/IImaResponseChannel.idl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +sequenceable service_response_data..OHOS.MiscServices.ServiceResponseDataInner; +interface OHOS.MiscServices.IImaResponseChannel { + [oneway] void OnResponse( + [in] unsigned int requestId, [in] int resultErrCode, [in] ServiceResponseDataInner response); +} diff --git a/frameworks/native/inputmethod_ability/include/ima_response_channel_impl.h b/frameworks/native/inputmethod_ability/include/ima_response_channel_impl.h new file mode 100644 index 000000000..371eb9264 --- /dev/null +++ b/frameworks/native/inputmethod_ability/include/ima_response_channel_impl.h @@ -0,0 +1,37 @@ +/* + * 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 IMF_IMA_RESPONSE_CHANNEL_IMPL_H +#define IMF_IMA_RESPONSE_CHANNEL_IMPL_H + +#include "iima_response_channel.h" +#include "ima_response_channel_stub.h" +#include "iremote_object.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +class ImaResponseChannelImpl final + : public ImaResponseChannelStub, public std::enable_shared_from_this { + DISALLOW_COPY_AND_MOVE(ImaResponseChannelImpl); + +public: + ImaResponseChannelImpl(); + ~ImaResponseChannelImpl(); + ErrCode OnResponse(uint32_t requestId, int32_t resultErrCode, const ServiceResponseDataInner &response) override; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMF_IMA_RESPONSE_CHANNEL_IMPL_H diff --git a/frameworks/native/inputmethod_ability/include/ima_service_proxy.h b/frameworks/native/inputmethod_ability/include/ima_service_proxy.h new file mode 100644 index 000000000..f0bd018bb --- /dev/null +++ b/frameworks/native/inputmethod_ability/include/ima_service_proxy.h @@ -0,0 +1,81 @@ +/* + * 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 IMF_IMA_SERVICE_PROXY_H +#define IMF_IMA_SERVICE_PROXY_H + +#include +#include +#include +#include + +#include "iima_response_channel.h" +#include "iinput_method_system_ability.h" +#include "input_death_recipient.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +class ImaServiceProxy { +private: + ImaServiceProxy(); + +public: + static ImaServiceProxy &GetInstance(); + + ~ImaServiceProxy() = default; + ImaServiceProxy(const ImaServiceProxy &) = delete; + ImaServiceProxy(ImaServiceProxy &&) = delete; + ImaServiceProxy &operator=(const ImaServiceProxy &) = delete; + ImaServiceProxy &operator=(ImaServiceProxy &&) = delete; + + int32_t SendRequest(const RequestFunc &request, int64_t timeout); + template + int32_t SendRequest(const RequestFunc &request, ResultType &resultValue, int64_t timeout); + + void OnResponse(RequestId id, const ServiceResponse &responseData); + + void RemoveDeathRecipient(); + +private: + sptr GetSystemAbilityProxy(bool ifRetry = true); + void OnRemoteSaDied(const wptr &remote); + std::atomic hasRegistered_{ false }; + std::mutex abilityLock_{}; + sptr abilityManager_ = nullptr; + sptr deathRecipient_; + +private: + void Init(); + RequestId GetNextRequestId(); + int32_t SendRequestInner(const RequestFunc &request, ServiceResponseData &responseData, int64_t timeout); + + void AddRequest(RequestId id, PendingRequest pendingRequest); + void RemoveRequest(RequestId requestId); + void RemoveUnresponsiveRequest(RequestId requestId); + void ClearRequest(); + + std::atomic currentRequestId_{ 0 }; + std::atomic isInterrupted_{ false }; + + std::mutex requestsMutex_{}; + std::unordered_map pendingRequests_; + + sptr responseChannelStub_{ nullptr }; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_IMA_SERVICE_PROXY_H diff --git a/frameworks/native/inputmethod_ability/src/ima_response_channel_impl.cpp b/frameworks/native/inputmethod_ability/src/ima_response_channel_impl.cpp new file mode 100644 index 000000000..87ac7e439 --- /dev/null +++ b/frameworks/native/inputmethod_ability/src/ima_response_channel_impl.cpp @@ -0,0 +1,39 @@ +/* + * 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 "ima_response_channel_impl.h" + +#include "ima_service_proxy.h" + +namespace OHOS { +namespace MiscServices { + +ImaResponseChannelImpl::ImaResponseChannelImpl() +{ +} + +ImaResponseChannelImpl::~ImaResponseChannelImpl() +{ +} + +ErrCode ImaResponseChannelImpl::OnResponse( + uint32_t requestId, int32_t resultErrCode, const ServiceResponseDataInner &response) +{ + ServiceResponse serviceResponse = { .result = resultErrCode, .responseData = response.data }; + ImaServiceProxy::GetInstance().OnResponse(requestId, serviceResponse); + return ERR_OK; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/inputmethod_ability/src/ima_service_proxy.cpp b/frameworks/native/inputmethod_ability/src/ima_service_proxy.cpp new file mode 100644 index 000000000..1e9e35f24 --- /dev/null +++ b/frameworks/native/inputmethod_ability/src/ima_service_proxy.cpp @@ -0,0 +1,259 @@ +/* + * 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 "ima_service_proxy.h" + +#include "ima_response_channel_impl.h" +#include "input_method_ability.h" +#include "on_demand_start_stop_sa.h" +#include "variant_util.h" + +namespace OHOS { +namespace MiscServices { +ImaServiceProxy &ImaServiceProxy::GetInstance() +{ + static ImaServiceProxy ImaServiceProxy; + return ImaServiceProxy; +} + +ImaServiceProxy::ImaServiceProxy() +{ + Init(); +} + +void ImaServiceProxy::Init() +{ + IMSA_HILOGD("start"); + sptr channel = new (std::nothrow) ImaResponseChannelImpl(); + if (channel == nullptr) { + IMSA_HILOGE("failed to create channel!"); + return; + } + responseChannelStub_ = channel; +} + +RequestId ImaServiceProxy::GetNextRequestId() +{ + static std::atomic seqId{ 1 }; + return seqId.fetch_add(1, std::memory_order_seq_cst); +} + +void ImaServiceProxy::OnResponse(RequestId id, const ServiceResponse &responseData) +{ + std::lock_guard lock(requestsMutex_); + auto iter = pendingRequests_.find(id); + if (iter == pendingRequests_.end()) { + IMSA_HILOGE("failed to find pending request, id: %{public}d", id); + return; + } + IMSA_HILOGD("id: %{public}d", id); + iter->second.promise.set_value(responseData); +} + +sptr ImaServiceProxy::GetSystemAbilityProxy(bool ifRetry) +{ + std::lock_guard lock(abilityLock_); + if (abilityManager_ != nullptr) { + return abilityManager_; + } + IMSA_HILOGD("get input method service proxy."); + auto systemAbility = OnDemandStartStopSa::GetInputMethodSystemAbility(ifRetry); + if (systemAbility == nullptr) { + IMSA_HILOGE("systemAbility is nullptr!"); + return nullptr; + } + + if (deathRecipient_ == nullptr) { + deathRecipient_ = new (std::nothrow) InputDeathRecipient(); + if (deathRecipient_ == nullptr) { + IMSA_HILOGE("create death recipient failed!"); + return nullptr; + } + } + deathRecipient_->SetDeathRecipient([this](const wptr &remote) { OnRemoteSaDied(remote); }); + if ((systemAbility->IsProxyObject()) && (!systemAbility->AddDeathRecipient(deathRecipient_))) { + IMSA_HILOGE("failed to add death recipient!"); + return nullptr; + } + abilityManager_ = iface_cast(systemAbility); + return abilityManager_; +} + +void ImaServiceProxy::RemoveDeathRecipient() +{ + std::lock_guard lock(abilityLock_); + if (abilityManager_ != nullptr && abilityManager_->AsObject() != nullptr && deathRecipient_ != nullptr) { + abilityManager_->AsObject()->RemoveDeathRecipient(deathRecipient_); + } + deathRecipient_ = nullptr; + abilityManager_ = nullptr; +} + +int32_t ImaServiceProxy::SendRequest(const RequestFunc &request, int64_t timeout) +{ + if (request == nullptr) { + IMSA_HILOGE("request is nullptr"); + return ErrorCode::ERROR_IMC_NULLPTR; + } + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + PendingRequest pendingRequest; + RequestId id = GetNextRequestId(); + auto future = pendingRequest.promise.get_future(); + AddRequest(id, std::move(pendingRequest)); + + // send request + int32_t ret = request(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("request failed, ret: %{public}d", ret); + RemoveUnresponsiveRequest(id); + return ret; + } + + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + // wait till timeout + if (future.wait_for(std::chrono::milliseconds(timeout)) != std::future_status::ready) { + IMSA_HILOGE("service handle timeout"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT; + } + + // handle response + ServiceResponse response = future.get(); + ret = response.result; + RemoveRequest(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("task failed"); + } + return ret; +} + +int32_t ImaServiceProxy::SendRequestInner( + const RequestFunc &request, ServiceResponseData &responseData, int64_t timeout) +{ + if (request == nullptr) { + IMSA_HILOGE("request is nullptr"); + return ErrorCode::ERROR_IMC_NULLPTR; + } + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + + PendingRequest pendingRequest; + RequestId id = GetNextRequestId(); + auto future = pendingRequest.promise.get_future(); + AddRequest(id, std::move(pendingRequest)); + + // send request + int32_t ret = request(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("request failed, ret: %{public}d", ret); + RemoveUnresponsiveRequest(id); + return ret; + } + + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + // wait till timeout + if (future.wait_for(std::chrono::milliseconds(timeout)) != std::future_status::ready) { + IMSA_HILOGE("service handle timeout"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT; + } + + // handle response + ServiceResponse response = future.get(); + ret = response.result; + RemoveRequest(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("task failed"); + return ret; + } + responseData = response.responseData; + return ErrorCode::NO_ERROR; +} + +template +int32_t ImaServiceProxy::SendRequest(const RequestFunc &request, ResultType &resultValue, int64_t timeout) +{ + ServiceResponseData responseData; + int32_t ret = SendRequestInner(request, responseData, timeout); + if (ret != ErrorCode::NO_ERROR) { + return ret; + } + if (!VariantUtil::GetValue(responseData, resultValue)) { + IMSA_HILOGE("failed to get result value"); + return ErrorCode::ERROR_INVALID_VARIANT_TYPE; + } + return ErrorCode::NO_ERROR; +} + +void ImaServiceProxy::OnRemoteSaDied(const wptr &remote) +{ + hasRegistered_.store(false); + ClearRequest(); + { + std::lock_guard lock(abilityLock_); + abilityManager_ = nullptr; + } +} + +void ImaServiceProxy::AddRequest(RequestId id, PendingRequest pendingRequest) +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_[id] = std::move(pendingRequest); + currentRequestId_.store(id); + IMSA_HILOGD("ima request[%{public}u] added", static_cast(id)); +} + +void ImaServiceProxy::RemoveRequest(RequestId requestId) +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_.erase(requestId); + currentRequestId_.store(0); + IMSA_HILOGD("ima request[%{public}u] removed", static_cast(requestId)); +} + +void ImaServiceProxy::RemoveUnresponsiveRequest(RequestId requestId) +{ + std::lock_guard lock(requestsMutex_); + auto iter = pendingRequests_.find(requestId); + if (iter == pendingRequests_.end()) { + IMSA_HILOGD("ima request[%{public}u] already removed", static_cast(requestId)); + return; + } + ServiceResponse response = { .result = ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT }; + iter->second.promise.set_value(response); + pendingRequests_.erase(iter); + IMSA_HILOGD("ima request[%{public}u] removed", static_cast(requestId)); +} + +void ImaServiceProxy::ClearRequest() +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_.clear(); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/inputmethod_controller/IImcResponseChannel.idl b/frameworks/native/inputmethod_controller/IImcResponseChannel.idl new file mode 100644 index 000000000..497fb83cb --- /dev/null +++ b/frameworks/native/inputmethod_controller/IImcResponseChannel.idl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +sequenceable service_response_data..OHOS.MiscServices.ServiceResponseDataInner; +interface OHOS.MiscServices.IImcResponseChannel { + [oneway] void OnResponse( + [in] unsigned int requestId, [in] int resultErrCode, [in] ServiceResponseDataInner response); +} diff --git a/frameworks/native/inputmethod_controller/include/imc_response_channel_impl.h b/frameworks/native/inputmethod_controller/include/imc_response_channel_impl.h new file mode 100644 index 000000000..cbbff526c --- /dev/null +++ b/frameworks/native/inputmethod_controller/include/imc_response_channel_impl.h @@ -0,0 +1,37 @@ +/* + * 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 IMF_IMC_RESPONSE_CHANNEL_IMPL_H +#define IMF_IMC_RESPONSE_CHANNEL_IMPL_H + +#include "iimc_response_channel.h" +#include "imc_response_channel_stub.h" +#include "iremote_object.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +class ImcResponseChannelImpl final + : public ImcResponseChannelStub, public std::enable_shared_from_this { + DISALLOW_COPY_AND_MOVE(ImcResponseChannelImpl); + +public: + ImcResponseChannelImpl(); + ~ImcResponseChannelImpl(); + ErrCode OnResponse(uint32_t requestId, int32_t resultErrCode, const ServiceResponseDataInner &response) override; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMF_IMC_RESPONSE_CHANNEL_IMPL_H diff --git a/frameworks/native/inputmethod_controller/include/imc_service_proxy.h b/frameworks/native/inputmethod_controller/include/imc_service_proxy.h new file mode 100644 index 000000000..5a4634dfe --- /dev/null +++ b/frameworks/native/inputmethod_controller/include/imc_service_proxy.h @@ -0,0 +1,87 @@ +/* + * 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 IMF_IMC_SERVICE_PROXY_H +#define IMF_IMC_SERVICE_PROXY_H + +#include +#include +#include +#include + +#include "iimc_response_channel.h" +#include "iinput_method_system_ability.h" +#include "input_client_info.h" +#include "input_death_recipient.h" +#include "input_method_utils.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +class ImcServiceProxy { +private: + ImcServiceProxy(); + +public: + static ImcServiceProxy &GetInstance(); + + ~ImcServiceProxy() = default; + ImcServiceProxy(const ImcServiceProxy &) = delete; + ImcServiceProxy(ImcServiceProxy &&) = delete; + ImcServiceProxy &operator=(const ImcServiceProxy &) = delete; + ImcServiceProxy &operator=(ImcServiceProxy &&) = delete; + + int32_t SendRequest(const RequestFunc &request, int64_t timeout); + template + int32_t SendRequest(const RequestFunc &request, ResultType &resultValue, int64_t timeout); + + void OnResponse(RequestId id, const ServiceResponse &responseData); + + void RemoveDeathRecipient(); + + void Interrupt(); + void Resume(); + +private: + sptr TryGetSystemAbilityProxy(); + sptr GetSystemAbilityProxy(bool ifRetry = true); + void OnRemoteSaDied(const wptr &remote); + std::atomic hasRegistered_{ false }; + std::mutex abilityLock_{}; + sptr abilityManager_ = nullptr; + sptr deathRecipient_; + +private: + void Init(); + RequestId GetNextRequestId(); + int32_t SendRequestInner(const RequestFunc &request, ServiceResponseData &responseData, int64_t timeout); + + void AddRequest(RequestId id, PendingRequest pendingRequest); + void RemoveRequest(RequestId requestId); + void RemoveUnresponsiveRequest(RequestId requestId); + void ClearRequest(); + + std::atomic currentRequestId_{ 0 }; + std::atomic isInterrupted_{ false }; + + std::mutex requestsMutex_{}; + std::unordered_map pendingRequests_; + + sptr responseChannelStub_{ nullptr }; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_IMC_SERVICE_PROXY_H diff --git a/frameworks/native/inputmethod_controller/src/imc_response_channel_impl.cpp b/frameworks/native/inputmethod_controller/src/imc_response_channel_impl.cpp new file mode 100644 index 000000000..e9c385b24 --- /dev/null +++ b/frameworks/native/inputmethod_controller/src/imc_response_channel_impl.cpp @@ -0,0 +1,39 @@ +/* + * 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 "imc_response_channel_impl.h" + +#include "imc_service_proxy.h" + +namespace OHOS { +namespace MiscServices { + +ImcResponseChannelImpl::ImcResponseChannelImpl() +{ +} + +ImcResponseChannelImpl::~ImcResponseChannelImpl() +{ +} + +ErrCode ImcResponseChannelImpl::OnResponse( + uint32_t requestId, int32_t resultErrCode, const ServiceResponseDataInner &response) +{ + ServiceResponse serviceResponse = { .result = resultErrCode, .responseData = response.data }; + ImcServiceProxy::GetInstance().OnResponse(requestId, serviceResponse); + return ERR_OK; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/inputmethod_controller/src/imc_service_proxy.cpp b/frameworks/native/inputmethod_controller/src/imc_service_proxy.cpp new file mode 100644 index 000000000..035f00bb1 --- /dev/null +++ b/frameworks/native/inputmethod_controller/src/imc_service_proxy.cpp @@ -0,0 +1,286 @@ +/* + * 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 "imc_service_proxy.h" + +#include "imc_response_channel_impl.h" +#include "ime_system_channel.h" +#include "input_client_info.h" +#include "input_method_controller.h" +#include "input_method_tools.h" +#include "input_method_utils.h" +#include "on_demand_start_stop_sa.h" +#include "variant_util.h" + +namespace OHOS { +namespace MiscServices { +ImcServiceProxy &ImcServiceProxy::GetInstance() +{ + static ImcServiceProxy imcServiceProxy; + return imcServiceProxy; +} + +ImcServiceProxy::ImcServiceProxy() +{ + Init(); +} + +void ImcServiceProxy::Init() +{ + IMSA_HILOGD("start"); + sptr channel = new (std::nothrow) ImcResponseChannelImpl(); + if (channel == nullptr) { + IMSA_HILOGE("failed to create channel!"); + return; + } + responseChannelStub_ = channel; +} + +RequestId ImcServiceProxy::GetNextRequestId() +{ + static std::atomic seqId{ 1 }; + return seqId.fetch_add(1, std::memory_order_seq_cst); +} + +void ImcServiceProxy::OnResponse(RequestId id, const ServiceResponse &responseData) +{ + std::lock_guard lock(requestsMutex_); + auto iter = pendingRequests_.find(id); + if (iter == pendingRequests_.end()) { + IMSA_HILOGE("failed to find pending request, id: %{public}d", id); + return; + } + IMSA_HILOGD("id: %{public}d", id); + iter->second.promise.set_value(responseData); +} + +int32_t ImcServiceProxy::SendRequest(const RequestFunc &request, int64_t timeout) +{ + if (request == nullptr) { + IMSA_HILOGE("request is nullptr"); + return ErrorCode::ERROR_IMC_NULLPTR; + } + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + PendingRequest pendingRequest; + RequestId id = GetNextRequestId(); + auto future = pendingRequest.promise.get_future(); + AddRequest(id, std::move(pendingRequest)); + + // send request + int32_t ret = request(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("request failed, ret: %{public}d", ret); + RemoveUnresponsiveRequest(id); + return ret; + } + + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + // wait till timeout + if (future.wait_for(std::chrono::milliseconds(timeout)) != std::future_status::ready) { + IMSA_HILOGE("service handle timeout"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT; + } + + // handle response + ServiceResponse response = future.get(); + ret = response.result; + RemoveRequest(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("task failed"); + } + return ret; +} + +int32_t ImcServiceProxy::SendRequestInner( + const RequestFunc &request, ServiceResponseData &responseData, int64_t timeout) +{ + if (request == nullptr) { + IMSA_HILOGE("request is nullptr"); + return ErrorCode::ERROR_IMC_NULLPTR; + } + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + + PendingRequest pendingRequest; + RequestId id = GetNextRequestId(); + auto future = pendingRequest.promise.get_future(); + AddRequest(id, std::move(pendingRequest)); + + // send request + int32_t ret = request(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("request failed, ret: %{public}d", ret); + RemoveUnresponsiveRequest(id); + return ret; + } + + if (isInterrupted_.load()) { + IMSA_HILOGW("request is interrupted"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_REQUEST_INTERRUPTED; + } + // wait till timeout + if (future.wait_for(std::chrono::milliseconds(timeout)) != std::future_status::ready) { + IMSA_HILOGE("service handle timeout"); + RemoveUnresponsiveRequest(id); + return ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT; + } + + // handle response + ServiceResponse response = future.get(); + ret = response.result; + RemoveRequest(id); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("task failed"); + return ret; + } + responseData = response.responseData; + return ErrorCode::NO_ERROR; +} + +template +int32_t ImcServiceProxy::SendRequest(const RequestFunc &request, ResultType &resultValue, int64_t timeout) +{ + ServiceResponseData responseData; + int32_t ret = SendRequestInner(request, responseData, timeout); + if (ret != ErrorCode::NO_ERROR) { + return ret; + } + if (!VariantUtil::GetValue(responseData, resultValue)) { + IMSA_HILOGE("failed to get result value"); + return ErrorCode::ERROR_INVALID_VARIANT_TYPE; + } + return ErrorCode::NO_ERROR; +} + + +void ImcServiceProxy::OnRemoteSaDied(const wptr &remote) +{ + hasRegistered_.store(false); + ClearRequest(); + { + std::lock_guard lock(abilityLock_); + abilityManager_ = nullptr; + } +} + +void ImcServiceProxy::Interrupt() +{ + if (!isInterrupted_.exchange(true)) { + auto id = currentRequestId_.load(); + if (id == 0) { + return; + } + ServiceResponse response = { .result = ErrorCode::ERROR_REQUEST_INTERRUPTED }; + OnResponse(id, response); + } +} + +void ImcServiceProxy::Resume() +{ + isInterrupted_.store(false); +} + +sptr ImcServiceProxy::TryGetSystemAbilityProxy() +{ + return GetSystemAbilityProxy(false); +} + +sptr ImcServiceProxy::GetSystemAbilityProxy(bool ifRetry) +{ + std::lock_guard lock(abilityLock_); + if (abilityManager_ != nullptr) { + return abilityManager_; + } + IMSA_HILOGD("get input method service proxy."); + auto systemAbility = OnDemandStartStopSa::GetInputMethodSystemAbility(ifRetry); + if (systemAbility == nullptr) { + IMSA_HILOGE("systemAbility is nullptr!"); + return nullptr; + } + + if (deathRecipient_ == nullptr) { + deathRecipient_ = new (std::nothrow) InputDeathRecipient(); + if (deathRecipient_ == nullptr) { + IMSA_HILOGE("create death recipient failed!"); + return nullptr; + } + } + deathRecipient_->SetDeathRecipient([this](const wptr &remote) { OnRemoteSaDied(remote); }); + if ((systemAbility->IsProxyObject()) && (!systemAbility->AddDeathRecipient(deathRecipient_))) { + IMSA_HILOGE("failed to add death recipient!"); + return nullptr; + } + abilityManager_ = iface_cast(systemAbility); + return abilityManager_; +} + +void ImcServiceProxy::RemoveDeathRecipient() +{ + std::lock_guard lock(abilityLock_); + if (abilityManager_ != nullptr && abilityManager_->AsObject() != nullptr && deathRecipient_ != nullptr) { + abilityManager_->AsObject()->RemoveDeathRecipient(deathRecipient_); + } + deathRecipient_ = nullptr; + abilityManager_ = nullptr; +} + +void ImcServiceProxy::AddRequest(RequestId id, PendingRequest pendingRequest) +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_[id] = std::move(pendingRequest); + currentRequestId_.store(id); + IMSA_HILOGD("imc request[%{public}u] added", static_cast(id)); +} + +void ImcServiceProxy::RemoveRequest(RequestId requestId) +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_.erase(requestId); + currentRequestId_.store(0); + IMSA_HILOGD("imc request[%{public}u] removed", static_cast(requestId)); +} + +void ImcServiceProxy::RemoveUnresponsiveRequest(RequestId requestId) +{ + std::lock_guard lock(requestsMutex_); + auto iter = pendingRequests_.find(requestId); + if (iter == pendingRequests_.end()) { + IMSA_HILOGD("imc request[%{public}u] already removed", static_cast(requestId)); + return; + } + ServiceResponse response = { .result = ErrorCode::ERROR_IMC_SERVICE_RESPONSE_TIMEOUT }; + iter->second.promise.set_value(response); + pendingRequests_.erase(iter); + IMSA_HILOGD("imc request[%{public}u] removed", static_cast(requestId)); +} + +void ImcServiceProxy::ClearRequest() +{ + std::lock_guard lock(requestsMutex_); + pendingRequests_.clear(); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/inner_api/inputmethod_ability/BUILD.gn b/interfaces/inner_api/inputmethod_ability/BUILD.gn index c92a7bb25..ea9b111fb 100644 --- a/interfaces/inner_api/inputmethod_ability/BUILD.gn +++ b/interfaces/inner_api/inputmethod_ability/BUILD.gn @@ -50,6 +50,11 @@ idl_gen_interface("keyevent_consumer_interface") { "${inputmethod_path}/frameworks/native/inputmethod_controller/IKeyEventConsumer.idl") } +idl_gen_interface("ima_response_channel_interface") { + src_idl = rebase_path( + "${inputmethod_path}/frameworks/native/inputmethod_ability/IImaResponseChannel.idl") +} + config("inputmethod_ability_native_config") { visibility = [ ":*" ] include_dirs = [ @@ -86,6 +91,31 @@ config("inputmethod_ability_native_public_config") { ] } +config("ima_response_channel_native_public_config") { + visibility = [ + "${inputmethod_path}/common/*", + "${inputmethod_path}/frameworks/kits/extension/*", + "${inputmethod_path}/frameworks/ndk/*", + "${inputmethod_path}/test/fuzztest/*", + "${inputmethod_path}/test/unittest/*", + "../inputmethod_controller/*", + "./*", + "${inputmethod_path}/frameworks/kits/extension_cj/*", + ] + include_dirs = [ + "include", + "${inputmethod_path}/common/include", + "${inputmethod_path}/frameworks/native/common/include", + "${inputmethod_path}/frameworks/native/inputmethod_ability/include", + "${inputmethod_path}/frameworks/native/inputmethod_controller/include", + "${inputmethod_path}/frameworks/common", + "${inputmethod_path}/frameworks/services/json/include", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller/include", + "${inputmethod_path}/services/include", + "${target_gen_dir}", + ] +} + ohos_source_set("input_control_channel_proxy") { sanitize = { cfi = true @@ -302,6 +332,48 @@ ohos_source_set("keyevent_consumer_stub") { part_name = "imf" } +ohos_source_set("ima_response_channel_proxy") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + public_configs = [ ":ima_response_channel_native_public_config" ] + output_values = get_target_outputs(":ima_response_channel_interface") + sources = filter_include(output_values, [ "*_proxy.cpp" ]) + deps = [ ":ima_response_channel_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "samgr:samgr_proxy", + ] + subsystem_name = "inputmethod" + part_name = "imf" +} + +ohos_source_set("ima_response_channel_stub") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + public_configs = [ ":ima_response_channel_native_public_config" ] + output_values = get_target_outputs(":ima_response_channel_interface") + sources = filter_include(output_values, [ "*_stub.cpp" ]) + deps = [ ":ima_response_channel_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "samgr:samgr_proxy", + ] + subsystem_name = "inputmethod" + part_name = "imf" +} + ohos_shared_library("inputmethod_ability") { branch_protector_ret = "pac_ret" sanitize = { diff --git a/interfaces/inner_api/inputmethod_controller/BUILD.gn b/interfaces/inner_api/inputmethod_controller/BUILD.gn index bdead3f43..81231a7c3 100644 --- a/interfaces/inner_api/inputmethod_controller/BUILD.gn +++ b/interfaces/inner_api/inputmethod_controller/BUILD.gn @@ -30,6 +30,11 @@ idl_gen_interface("oninputstop_notify_interface") { "${inputmethod_path}/frameworks/native/inputmethod_controller/IOnInputStopNotify.idl") } +idl_gen_interface("imc_response_channel_interface") { + src_idl = rebase_path( + "${inputmethod_path}/frameworks/native/inputmethod_controller/IImcResponseChannel.idl") +} + config("inputmethod_client_native_config") { visibility = [ ":*" ] include_dirs = [ @@ -64,6 +69,29 @@ config("inputmethod_client_native_public_config") { ] } +config("imc_response_channel_native_public_config") { + visibility = [ + "${inputmethod_path}/common/*", + "${inputmethod_path}/frameworks/ndk/*", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability/*", + "${inputmethod_path}/test/fuzztest/*", + "${inputmethod_path}/test/unittest/*", + "./*", + ] + include_dirs = [ + "include", + "${inputmethod_path}/common/include", + "${inputmethod_path}/frameworks/common", + "${inputmethod_path}/frameworks/native/common/include", + "${inputmethod_path}/frameworks/native/inputmethod_controller/include", + "${inputmethod_path}/frameworks/native/inputmethod_ability/include", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability/include", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller/include", + "${inputmethod_path}/services/include", + "${target_gen_dir}", + ] +} + ohos_source_set("input_client_proxy") { sanitize = { cfi = true @@ -190,6 +218,48 @@ ohos_source_set("oninputstop_notify_proxy") { part_name = "imf" } +ohos_source_set("imc_response_channel_proxy") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + public_configs = [ ":imc_response_channel_native_public_config" ] + output_values = get_target_outputs(":imc_response_channel_interface") + sources = filter_include(output_values, [ "*_proxy.cpp" ]) + deps = [ ":imc_response_channel_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "samgr:samgr_proxy", + ] + subsystem_name = "inputmethod" + part_name = "imf" +} + +ohos_source_set("imc_response_channel_stub") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + public_configs = [ ":imc_response_channel_native_public_config" ] + output_values = get_target_outputs(":imc_response_channel_interface") + sources = filter_include(output_values, [ "*_stub.cpp" ]) + deps = [ ":imc_response_channel_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "samgr:samgr_proxy", + ] + subsystem_name = "inputmethod" + part_name = "imf" +} + ohos_shared_library("inputmethod_client") { branch_protector_ret = "pac_ret" diff --git a/services/task_manager/include/actions/sa_action.h b/services/task_manager/include/actions/sa_action.h new file mode 100644 index 000000000..ecd88af2d --- /dev/null +++ b/services/task_manager/include/actions/sa_action.h @@ -0,0 +1,109 @@ +/* + * 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 IMF_SERVICES_SA_ACTION_NEW_H +#define IMF_SERVICES_SA_ACTION_NEW_H + +#include +#include + +#include "action.h" +#include "input_client_info.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +static constexpr int32_t INVALID_RET_CODE = -1; +enum class PauseType : int32_t { + PAUSED_TYPE_INVALID = -1, + PAUSE_TYPE_START_IME = 0, + PAUSE_TYPE_STOP_IME = 1, +}; + +struct PauseInfo { + PauseType type{ PauseType::PAUSED_TYPE_INVALID }; // pause type + std::string target; // bundleName of waiting target + uint64_t resumeId{ 0 }; + inline std::string ToString() const + { + std::stringstream ss; + ss << "PausedInfo:[" + << "type: " << static_cast(type) << " target: " << target << " resumeId: " << resumeId + << "]"; + return ss.str(); + } +}; + +using ActionInnerData = std::variant; +using ResultHandler = std::function; +using SaActionFunc = std::function; +class SaAction { +public: + SaAction() = default; + explicit SaAction(SaActionFunc func) : func_(std::move(func)), isResultAffectParent_(true) + { + } + SaAction(SaActionFunc func, ResultHandler onSuccess, ResultHandler onFailure) + : func_(std::move(func)), onSuccess_(std::move(onSuccess)), onFailure_(std::move(onFailure)), + isResultAffectParent_(true), failureCode_(INVALID_RET_CODE) + { + } + SaAction(SaActionFunc func, ResultHandler onSuccess, ResultHandler onFailure, bool isAffect, + int32_t failureCode = INVALID_RET_CODE) + : func_(std::move(func)), onSuccess_(std::move(onSuccess)), onFailure_(std::move(onFailure)), + isResultAffectParent_(isAffect), failureCode_(failureCode) + { + } + virtual ~SaAction() = default; + + virtual RunningState Execute(int32_t &ret, ServiceResponseData &responseData); + virtual RunningState Resume(uint64_t resumedId, int32_t &ret, ServiceResponseData &data); + virtual int32_t Pend(std::unique_ptr action); + virtual PauseInfo GetPausedInfo(); + + virtual RunningState Execute(int32_t &ret, ServiceResponseData &responseData, ActionInnerData &innerData); + virtual RunningState Resume( + uint64_t resumedId, int32_t &ret, ServiceResponseData &data, ActionInnerData &innerData); + + virtual RunningState GetState(); + + virtual bool IsWaitAction(); + +protected: + RunningState state_{ RUNNING_STATE_IDLE }; + +private: + RunningState ExecuteInner(int32_t &ret, ServiceResponseData &responseData); + + RunningState ExecuteImpl(ServiceResponseData &responseData); + + bool hasFuncExecuted_{ false }; + SaActionFunc func_; + ResultHandler onSuccess_{ nullptr }; + ResultHandler onFailure_{ nullptr }; + + bool isResultAffectParent_{ true }; + + std::unique_ptr curSubAction_{ nullptr }; + std::list> subActions_; + + int32_t retCode_{ 0 }; + int32_t failureCode_{ INVALID_RET_CODE }; + ActionInnerData innerData_{ std::monostate{} }; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_SERVICES_SA_ACTION_NEW_H diff --git a/services/task_manager/include/actions/sa_action_report.h b/services/task_manager/include/actions/sa_action_report.h new file mode 100644 index 000000000..247dec817 --- /dev/null +++ b/services/task_manager/include/actions/sa_action_report.h @@ -0,0 +1,52 @@ +/* + * 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 IMF_SERVICES_SA_ACTION_REPORT_H +#define IMF_SERVICES_SA_ACTION_REPORT_H + +#include "sa_action.h" + +namespace OHOS { +namespace MiscServices { +using ReportFunc = std::function; +class SaActionReport : public SaAction { +public: + SaActionReport() = default; + explicit SaActionReport(ReportFunc func) : func_(std::move(func)) + { + } + ~SaActionReport() = default; + + RunningState Execute(int32_t &ret, ServiceResponseData &responseData) override + { + if (state_ != RUNNING_STATE_IDLE) { + return RUNNING_STATE_ERROR; + } + + state_ = RUNNING_STATE_RUNNING; + if (func_ != nullptr) { + func_(ret, responseData); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + +private: + ReportFunc func_; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_SERVICES_SA_ACTION_REPORT_H diff --git a/services/task_manager/include/actions/sa_action_wait.h b/services/task_manager/include/actions/sa_action_wait.h new file mode 100644 index 000000000..80f1c7d7a --- /dev/null +++ b/services/task_manager/include/actions/sa_action_wait.h @@ -0,0 +1,66 @@ +/* + * 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 IMF_SERVICES_SA_ACTION_WAIT_H +#define IMF_SERVICES_SA_ACTION_WAIT_H + +#include + +#include "sa_action.h" +#include "sa_task.h" + +namespace OHOS { +namespace MiscServices { +class SaActionWait : public SaAction { +public: + SaActionWait(uint32_t timeoutMs, PauseInfo info) + : completeId_(SaTask::GetNextSeqId()), timeoutId_(SaTask::GetNextSeqId()), timeoutMs_(timeoutMs), + pauseInfo_(std::move(info)), onComplete_(nullptr), onTimeout_(nullptr) + { + pauseInfo_.resumeId = completeId_; + } + SaActionWait(uint32_t timeoutMs, PauseInfo info, SaActionFunc onComplete, SaActionFunc onTimeout) + : completeId_(SaTask::GetNextSeqId()), timeoutId_(SaTask::GetNextSeqId()), timeoutMs_(timeoutMs), + pauseInfo_(std::move(info)), onComplete_(std::move(onComplete)), onTimeout_(std::move(onTimeout)) + { + pauseInfo_.resumeId = completeId_; + } + + ~SaActionWait() = default; + + RunningState Execute(int32_t &ret, ServiceResponseData &responseData) override; + RunningState Execute(int32_t &ret, ServiceResponseData &responseData, ActionInnerData &innerData) override; + + RunningState Resume(uint64_t resumedId, int32_t &ret, ServiceResponseData &data) override; + RunningState Resume( + uint64_t resumedId, int32_t &ret, ServiceResponseData &data, ActionInnerData &innerData) override; + + PauseInfo GetPausedInfo() override; + + RunningState GetState() override; + + bool IsWaitAction() override; + +private: + const uint64_t completeId_; + const uint64_t timeoutId_; + const uint32_t timeoutMs_; + PauseInfo pauseInfo_{}; + SaActionFunc onComplete_; + SaActionFunc onTimeout_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMF_SERVICES_SA_ACTION_WAIT_H diff --git a/services/task_manager/include/caller_info.h b/services/task_manager/include/caller_info.h new file mode 100644 index 000000000..f281ea8f9 --- /dev/null +++ b/services/task_manager/include/caller_info.h @@ -0,0 +1,36 @@ +/* + * 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 IMF_CALLER_INFO_H +#define IMF_CALLER_INFO_H + +#include +#include + +namespace OHOS { +namespace MiscServices { +static constexpr int32_t MAIN_USER_ID = 100; +struct CallerInfo { + uint32_t requestId = 0; + int32_t pid = 0; + int32_t uid = 0; + int32_t userId{ MAIN_USER_ID }; + uint32_t tokenId = 0; + uint64_t fullTokenId = 0; + std::string bundleName; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMF_CALLER_INFO_H diff --git a/services/task_manager/include/requester_manager.h b/services/task_manager/include/requester_manager.h new file mode 100644 index 000000000..2ea686b86 --- /dev/null +++ b/services/task_manager/include/requester_manager.h @@ -0,0 +1,78 @@ +/* + * 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 IMF_SERVICES_REQUESTER_MANAGER_H +#define IMF_SERVICES_REQUESTER_MANAGER_H + +#include + +#include +#include +#include + +#include "input_death_recipient.h" +#include "iremote_object.h" +#include "iima_response_channel.h" +#include "iimc_response_channel.h" + +namespace OHOS { +namespace MiscServices { +struct RequesterInfo { + uint32_t requestCount{ 0 }; + sptr imaResponseChannel{ nullptr }; + sptr imcResponseChannel{ nullptr }; + sptr deathRecipient{ nullptr }; + void AddChannel(sptr imaChannel, sptr imcChannel) + { + if (imaChannel != nullptr) { + imaResponseChannel = imaChannel; + } + if (imcChannel != nullptr) { + imcResponseChannel = imcChannel; + } + } +}; + +class RequesterManager : public std::enable_shared_from_this { +private: + RequesterManager() = default; + +public: + ~RequesterManager() = default; + + RequesterManager(const RequesterManager &) = delete; + RequesterManager(RequesterManager &&) = delete; + RequesterManager &operator=(const RequesterManager &) = delete; + RequesterManager &operator=(RequesterManager &&) = delete; + + static RequesterManager &GetInstance(); + + std::shared_ptr GetRequester(int32_t pid); + int32_t AddImaChannel(int32_t pid, sptr channel); + int32_t AddImcChannel(int32_t pid, sptr channel); + + void TaskIn(int32_t pid); + void TaskOut(int32_t pid); + +private: + int32_t AddChannel(int32_t pid, sptr imaChannel, sptr imcChannel); + void OnClientDied(int32_t pid); + std::mutex mutex_{}; + std::unordered_map> requesterMap_; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_SERVICES_REQUESTER_MANAGER_H diff --git a/services/task_manager/include/sa_task_manager.h b/services/task_manager/include/sa_task_manager.h new file mode 100644 index 000000000..a8654460b --- /dev/null +++ b/services/task_manager/include/sa_task_manager.h @@ -0,0 +1,105 @@ +/* + * 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 IMF_SERVICES_SA_TASK_MANAGER_H +#define IMF_SERVICES_SA_TASK_MANAGER_H + +#include "event_handler.h" +#include "identity_checker_impl.h" +#include "input_method_utils.h" +#include "sa_action.h" +#include "sa_action_wait.h" +#include "sa_task.h" + +namespace OHOS { +namespace MiscServices { +using CallBack = std::function; + +using SaTaskPtr = std::shared_ptr; +using SaActionPtr = std::unique_ptr; +class SaTaskManager final { +private: + SaTaskManager(); + +public: + ~SaTaskManager() = default; + + SaTaskManager(const SaTaskManager &) = delete; + SaTaskManager(SaTaskManager &&) = delete; + SaTaskManager &operator=(const SaTaskManager &) = delete; + SaTaskManager &operator=(SaTaskManager &&) = delete; + + static SaTaskManager &GetInstance(); + + void Init(); + + // Post a task to work thread + int32_t PostTask(SaTaskPtr task, uint32_t delayMs = 0); + + // Trigger task process async + void ProcessAsync(); + + // Resume paused task with seqId + void Complete(uint64_t resumeId); + + // Wait for task and execute + int32_t WaitExec(std::unique_ptr waitAction, const SaActionFunc &execFunc = nullptr); + // Try to resume the current paused task + void TryResume(const PauseType &pauseType, const CallerInfo &callerInfo); + + // Pend an action to current task during executing + int32_t Pend(SaActionPtr action); + int32_t Pend(const SaActionFunc &func); + +private: + friend class InputMethodSystemAbility; + friend class SaActionWait; + void SetInited(bool flag); + void Reset(); + int32_t PendWaitResult(const SaActionFunc &func); + +private: + void OnNewTask(SaTaskPtr task); // Accept a new task + void Process(); // Process next task + + void ProcessNextCriticalTask(); + void ProcessNextSwitchImeTask(); + void ProcessNextHigherRequestTask(); + void ProcessNextNormalRequestTask(); + void ProcessNextQueryTask(); + void ProcessNextResumeTask(); + void ProcessNextInnerTask(); + + void ExecuteCurrentTask(); // Execute current task + +private: + bool IsWhiteListRequest(SaTaskCode taskCode); + bool inited_{ false }; + std::shared_ptr eventHandler_{ nullptr }; + std::shared_ptr identityChecker_{ nullptr }; + + SaTaskPtr curTask_ = { nullptr }; + SaTaskPtr pausedTask_ = { nullptr }; + std::list criticalTasks_; + std::list switchImeTasks_; + std::list higherRequestTasks_; + std::list normalRequestTasks_; + std::list queryTasks_; + std::list innerTasks_; + std::list resumeTasks_; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // IMF_SERVICES_SA_TASK_MANAGER_H \ No newline at end of file diff --git a/services/task_manager/include/tasks/sa_task.h b/services/task_manager/include/tasks/sa_task.h new file mode 100644 index 000000000..90f11a409 --- /dev/null +++ b/services/task_manager/include/tasks/sa_task.h @@ -0,0 +1,256 @@ +/* + * 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 IMF_SERVICES_SA_TASK_H +#define IMF_SERVICES_SA_TASK_H + +#include "caller_info.h" +#include "iima_response_channel.h" +#include "iimc_response_channel.h" +#include "sa_action.h" +#include "sa_action_report.h" +#include "service_response_data.h" + +namespace OHOS { +namespace MiscServices { +constexpr uint32_t INVALID_SEQ_ID = 0; +enum class SaTaskType : int32_t { + TYPE_CRITICAL_CHANGE = 0, // tasks bringing critical changes or updates + TYPE_SWITCH_IME = 1, // tasks causing ime switch + TYPE_HIGHER_REQUEST = 2, // tasks with a higher priority request + TYPE_NORMAL_REQUEST = 3, // tasks with a normal priority request + TYPE_RESUME = 4, // tasks creating resume inner tasks + TYPE_QUERY = 5, // tasks used for querying + TYPE_INNER = 6, // tasks acting on the current paused task + + TYPE_TOTAL_COUNT, + TYPE_INVALID = -1, +}; + +#define TASK_TYPE_OFFSET(src) ((src)*10000) + +enum class SaTaskCode : uint32_t { + TASK_CRITICAL_CHANGE_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_CRITICAL_CHANGE)), + // user change + ON_USER_STARTED, + ON_USER_REMOVED, + ON_USER_STOPPED, + // scb status change + ON_WMS_CONNECTED, + ON_WMS_DISCONNECTED, + // changes update ime info + ON_BUNDLE_SCAN_FINISHED, + ON_DATA_SHARE_READY, + // change current ime state + ON_EXTENSION, + ON_IME_ENABLED_STATE_CHANGED, + ON_IME_LIFE_CYCLE_STOP, + // on critical sa started + ON_ACCOUNT_SA_STARTED, + ON_MEM_SA_STARTED, + ON_WMS_SA_STARTED, + TASK_CRITICAL_CHANGE_END, + + TASK_SWITCH_IME_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_SWITCH_IME)), + // proxy ime register from IPC + REGISTER_PROXY_IME, + UNREGISTER_PROXY_IME, + UNREGISTERED_PROXY_IME, + // switch ime requests from IPC + SWITCH_INPUT_METHOD, + START_INPUT_TYPE, + EXIT_CURRENT_INPUT_TYPE, + // events which may switch ime + ON_PACKAGE_REMOVED, + ON_SCREEN_UNLOCKED, + TASK_SWITCH_IME_END, + + TASK_HIGHER_REQUEST_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_HIGHER_REQUEST)), + // events which influence user typing or panel showing + ON_FOCUSED, + ON_UNFOCUSED, + ON_COMMON_EVENT_SA_STARTED, + ON_DISPLAY_ID_CHANGED, + ON_MMI_SA_STARTED, + TASK_HIGHER_REQUEST_END, + + TASK_NORMAL_REQUEST_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_NORMAL_REQUEST)), + // operate keyboard from IPC + START_INPUT, + SHOW_CURRENT_INPUT, + HIDE_CURRENT_INPUT, + STOP_INPUT_SESSION, + SHOW_INPUT, + HIDE_INPUT, + RELEASE_INPUT, + REQUEST_SHOW_INPUT, + REQUEST_HIDE_INPUT, + CONNECT_SYSTEM_CMD, + SHOW_CURRENT_INPUT_DEPRECATED, + HIDE_CURRENT_INPUT_DEPRECATED, + HIDE_KEYBOARD_SELF, + ON_PASTEBOARD_SA_STARTED, + // data transaction or info setting requests from IPC + SET_CALLING_WINDOW, + SEND_PRIVATE_DATA, + PANEL_STATUS_CHANGE, + UPDATE_LISTEN_EVENT_FLAG, + DISPLAY_OPTIONAL_INPUT_METHOD, + TASK_NORMAL_REQUEST_END, + + TASK_RESUME_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_RESUME)), + // resume pause for ime start + INIT_CONNECT, + SET_CORE_AND_AGENT, + // resume pause for ime stop + ON_IME_DIED, + TASK_RESUME_END, + + TASK_QUERY_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_QUERY)), + // Get imf info + GET_CURRENT_INPUT_METHOD, + GET_CURRENT_INPUT_METHOD_SUBTYPE, + GET_DEFAULT_INPUT_METHOD, + GET_INPUT_METHOD_CONFIG, + GET_INPUT_METHOD_STATE, + GET_INPUT_START_INFO, + GET_SECURITY_MODE, + // List ime info + LIST_INPUT_METHOD, + LIST_INPUT_METHOD_SUBTYPE, + LIST_CURRENT_INPUT_METHOD_SUBTYPE, + // Judge ime info + IS_CURRENT_IME, + IS_CURRENT_IME_BY_PID, + IS_DEFAULT_IME, + IS_DEFAULT_IME_SCREEN, + IS_DEFAULT_IME_SET, + IS_INPUT_TYPE_SUPPORTED, + IS_PANEL_SHOWN, + IS_SYSTEM_APP, + IS_SYSTEM_IME_APP, + // Operate or update ime info + ENABLE_IME, + ON_BOOT_COMPLETED, + ON_DATA_SHARE_CALLBACK, + ON_PACKAGE_ADDED, + ON_PACKAGE_CHANGED, + ON_UPDATE_GLOBAL_ENABLED_TABLE, + ON_UPDATE_IME_INFO, + ON_SYSTEM_LANGUAGE_CHANGED, + TASK_QUERY_END, + + TASK_INNER_BEGIN = TASK_TYPE_OFFSET(static_cast(SaTaskType::TYPE_INNER)), + RESUME_WAIT, + RESUME_TIMEOUT, + TASK_INNER_END, +}; + +class SaTask { +public: + explicit SaTask(SaTaskCode code) + : code_(code), seqId_(GetNextSeqId()), imaResponseChannel_(nullptr), imcResponseChannel_(nullptr) + { + } + SaTask(SaTaskCode code, uint64_t seqId) + : code_(code), seqId_(seqId), imaResponseChannel_(nullptr), imcResponseChannel_(nullptr) + { + } + SaTask(SaTaskCode code, SaActionFunc func) + : code_(code), seqId_(GetNextSeqId()), imaResponseChannel_(nullptr), imcResponseChannel_(nullptr) + { + action_ = std::make_unique(func); + } + SaTask(SaTaskCode code, std::unique_ptr action) + : code_(code), seqId_(GetNextSeqId()), imaResponseChannel_(nullptr), imcResponseChannel_(nullptr) + { + action_ = std::move(action); + } + SaTask(SaTaskCode code, SaActionFunc func, CallerInfo info) + : code_(code), seqId_(GetNextSeqId()), callerInfo_(info), imaResponseChannel_(nullptr), + imcResponseChannel_(nullptr) + { + action_ = std::make_unique(func); + } + SaTask(SaTaskCode code, SaActionFunc func, CallerInfo info, sptr channel) + : code_(code), seqId_(GetNextSeqId()), callerInfo_(info), imaResponseChannel_(channel), + imcResponseChannel_(nullptr) + { + action_ = std::make_unique(func); + } + SaTask(SaTaskCode code, SaActionFunc func, CallerInfo info, sptr channel) + : code_(code), seqId_(GetNextSeqId()), callerInfo_(info), imaResponseChannel_(nullptr), + imcResponseChannel_(channel) + { + action_ = std::make_unique(func); + } + ~SaTask(); + + static uint64_t GetNextSeqId(); + + void SetHiSysReporter(const ReportFunc &func); + + RunningState Execute(); + RunningState Resume(uint64_t resumeId); + RunningState OnTask(const std::shared_ptr &task); + + int32_t PendWaitResult(std::unique_ptr action); + // Pend an action to the task directly. + int32_t Pend(std::unique_ptr action); + // Pend an action to the task, of which properties will inherit curAction_. + int32_t Pend(SaActionFunc func); + template int32_t Pend(Args &&... args) + { + return (Pend(std::forward(args)) && ...); + } + + // get task info + SaTaskType GetType() const; + SaTaskCode GetCode() const; + uint64_t GetSeqId() const; + CallerInfo GetCallerInfo() const; + bool IsRunning() const; + bool IsPaused() const; + PauseInfo GetPauseInfo(); + + void OnResponse(int32_t retCode); + +private: + RunningState ExecuteInner(); + void InvokeResponse(); + +protected: + static constexpr int32_t INVALID_FAIL_CODE = -1; + RunningState state_{ RUNNING_STATE_IDLE }; + + const SaTaskCode code_; + const uint64_t seqId_; + + CallerInfo callerInfo_; + sptr imaResponseChannel_; + sptr imcResponseChannel_; + ServiceResponseData responseData_{ std::monostate{} }; + + bool hasResponded_{ false }; + int32_t failRet_{ INVALID_FAIL_CODE }; + int32_t retCode_{ ErrorCode::NO_ERROR }; + + std::unique_ptr action_{ nullptr }; + std::unique_ptr hiSysReporter_{ nullptr }; +}; +} // namespace MiscServices +} // namespace OHOS + +#endif // IMF_SERVICES_SA_TASK_H diff --git a/services/task_manager/src/actions/sa_action.cpp b/services/task_manager/src/actions/sa_action.cpp new file mode 100644 index 000000000..c883c033d --- /dev/null +++ b/services/task_manager/src/actions/sa_action.cpp @@ -0,0 +1,208 @@ +/* + * 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 "sa_action.h" + +#include "variant_util.h" + +namespace OHOS { +namespace MiscServices { +RunningState SaAction::Execute(int32_t &ret, ServiceResponseData &responseData) +{ + if (state_ != RUNNING_STATE_IDLE) { + return RUNNING_STATE_ERROR; + } + return ExecuteInner(ret, responseData); +} + +RunningState SaAction::Execute(int32_t &ret, ServiceResponseData &responseData, ActionInnerData &innerData) +{ + if (state_ != RUNNING_STATE_IDLE) { + return RUNNING_STATE_ERROR; + } + innerData_ = innerData; + auto state = ExecuteInner(ret, responseData); + innerData = innerData_; + return state; +} + +RunningState SaAction::Resume(uint64_t resumedId, int32_t &ret, ServiceResponseData &data) +{ + if (curSubAction_ == nullptr) { + IMSA_HILOGE("curSubAction_ is nullptr"); + return RUNNING_STATE_ERROR; + } + + if (state_ != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("state_ is %{public}u, do not need to resume", state_); + return RUNNING_STATE_ERROR; + } + + auto state = curSubAction_->Resume(resumedId, ret, data); + if (state == RUNNING_STATE_PAUSED) { + return state_; + } + if (state == RUNNING_STATE_COMPLETED) { + curSubAction_.reset(); + return ExecuteInner(ret, data); + } + IMSA_HILOGE("curAction_ resume return %{public}u, error!", state); + return RUNNING_STATE_ERROR; +} + +RunningState SaAction::Resume(uint64_t resumedId, int32_t &ret, ServiceResponseData &data, ActionInnerData &innerData) +{ + if (curSubAction_ == nullptr) { + IMSA_HILOGE("curSubAction_ is nullptr"); + return RUNNING_STATE_ERROR; + } + + if (state_ != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("state_ is %{public}u, do not need to resume", state_); + return RUNNING_STATE_ERROR; + } + + auto state = curSubAction_->Resume(resumedId, ret, data, innerData); + if (state == RUNNING_STATE_PAUSED) { + return state_; + } + if (state == RUNNING_STATE_COMPLETED) { + curSubAction_.reset(); + return ExecuteInner(ret, data); + } + IMSA_HILOGE("curAction_ resume return %{public}u, error!", state); + return RUNNING_STATE_ERROR; +} + +int32_t SaAction::Pend(std::unique_ptr action) +{ + if (action == nullptr) { + IMSA_HILOGE("action is nullptr"); + return ErrorCode::ERROR_NULL_POINTER; + } + + if (state_ != RUNNING_STATE_RUNNING && state_ != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("curTask_ is not running or paused, pend failed!"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + + if (curSubAction_ == nullptr) { + subActions_.push_back(std::move(action)); + return ErrorCode::NO_ERROR; + } + + if (curSubAction_->IsWaitAction()) { + subActions_.push_front(std::move(action)); + return ErrorCode::NO_ERROR; + } + + return curSubAction_->Pend(std::move(action)); +} + +PauseInfo SaAction::GetPausedInfo() +{ + if (state_ != RUNNING_STATE_PAUSED) { + return {}; + } + if (curSubAction_ == nullptr) { + return {}; + } + return curSubAction_->GetPausedInfo(); +} + +RunningState SaAction::GetState() +{ + if (curSubAction_ == nullptr) { + return state_; + } + return curSubAction_->GetState(); +} + +RunningState SaAction::ExecuteInner(int32_t &ret, ServiceResponseData &responseData) +{ + state_ = RUNNING_STATE_RUNNING; + + state_ = ExecuteImpl(responseData); + + // state is paused, return. + if (state_ == RUNNING_STATE_PAUSED) { + return state_; + } + // state error + if (state_ == RUNNING_STATE_ERROR) { + return state_; + } + // state is RUNNING_STATE_COMPLETED + ResultHandler handler = nullptr; + if (retCode_ != ErrorCode::NO_ERROR) { + // return failureCode_ if valid + retCode_ = failureCode_ != INVALID_RET_CODE ? failureCode_ : retCode_; + handler = onFailure_; + } else { + handler = onSuccess_; + } + if (handler != nullptr) { + handler(retCode_); + } + ret = retCode_; + return state_; +} + +RunningState SaAction::ExecuteImpl(ServiceResponseData &responseData) +{ + // execute func_ first + if (func_ != nullptr && !hasFuncExecuted_) { + retCode_ = func_(responseData, innerData_); + hasFuncExecuted_ = true; + if (retCode_ != ERR_OK) { + return RUNNING_STATE_COMPLETED; + } + } + + // check sub actions + while (!subActions_.empty()) { + curSubAction_ = std::move(subActions_.front()); + subActions_.pop_front(); + + int32_t ret = 0; + auto state = curSubAction_->Execute(ret, responseData, innerData_); + + // current sub action is paused, return. + if (state == RUNNING_STATE_PAUSED) { + return RUNNING_STATE_PAUSED; + } + // no need to handle current sub action's result. + if (!curSubAction_->isResultAffectParent_) { + curSubAction_.reset(); + continue; + } + // handle current sub action's result. + retCode_ = ret; + curSubAction_.reset(); + if (retCode_ != ErrorCode::NO_ERROR) { + IMSA_HILOGD("current sub action failed, drop the actions left unexecuted"); + subActions_.clear(); + } + } + + return RUNNING_STATE_COMPLETED; +} + +bool SaAction::IsWaitAction() +{ + return false; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/services/task_manager/src/actions/sa_action_wait.cpp b/services/task_manager/src/actions/sa_action_wait.cpp new file mode 100644 index 000000000..fc0015930 --- /dev/null +++ b/services/task_manager/src/actions/sa_action_wait.cpp @@ -0,0 +1,84 @@ +/* + * 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 "sa_action_wait.h" + +#include "sa_task_manager.h" + +namespace OHOS { +namespace MiscServices { +RunningState SaActionWait::Execute(int32_t &ret, ServiceResponseData &responseData) +{ + state_ = RUNNING_STATE_PAUSED; + + auto task = std::make_shared(SaTaskCode::RESUME_TIMEOUT, timeoutId_); + ret = SaTaskManager::GetInstance().PostTask(task, timeoutMs_); + return state_; +} + +RunningState SaActionWait::Execute(int32_t &ret, ServiceResponseData &responseData, ActionInnerData &innerData) +{ + return Execute(ret, responseData); +} + +RunningState SaActionWait::Resume(uint64_t resumedId, int32_t &ret, ServiceResponseData &data) +{ + ActionInnerData innerData; + return Resume(resumedId, ret, data, innerData); +} + +RunningState SaActionWait::Resume( + uint64_t resumedId, int32_t &ret, ServiceResponseData &data, ActionInnerData &innerData) +{ + if (state_ != RUNNING_STATE_PAUSED) { + return RUNNING_STATE_ERROR; + } + + if (resumedId == completeId_) { + if (onComplete_ != nullptr) { + state_ = RUNNING_STATE_RUNNING; + onComplete_(data, innerData); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + if (resumedId == timeoutId_) { + if (onTimeout_ != nullptr) { + state_ = RUNNING_STATE_RUNNING; + onTimeout_(data, innerData); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + + return state_; +} + +PauseInfo SaActionWait::GetPausedInfo() +{ + return pauseInfo_; +} + +RunningState SaActionWait::GetState() +{ + return state_; +} + +bool SaActionWait::IsWaitAction() +{ + return true; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/services/task_manager/src/requester_manager.cpp b/services/task_manager/src/requester_manager.cpp new file mode 100644 index 000000000..e218055da --- /dev/null +++ b/services/task_manager/src/requester_manager.cpp @@ -0,0 +1,162 @@ +/* + * 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 "requester_manager.h" + +#include "global.h" + +namespace OHOS { +namespace MiscServices { +constexpr int32_t MAX_REQUEST_COUNT = 6; +RequesterManager &RequesterManager::GetInstance() +{ + static RequesterManager requesterManager; + return requesterManager; +} + +std::shared_ptr RequesterManager::GetRequester(int32_t pid) +{ + std::lock_guard lock(mutex_); + auto iter = requesterMap_.find(pid); + if (iter == requesterMap_.end() || iter->second == nullptr) { + IMSA_HILOGE("client: %{public}d not registered or nullptr", pid); + return nullptr; + } + if (iter->second->imaResponseChannel == nullptr && iter->second->imcResponseChannel == nullptr) { + IMSA_HILOGE("client: %{public}d channel is nullptr", pid); + return nullptr; + } + auto requesterInfo = iter->second; + if (requesterInfo->requestCount >= MAX_REQUEST_COUNT) { + IMSA_HILOGE("requests from client: %{public}d, count: %{public}d, too much", pid, requesterInfo->requestCount); + return nullptr; + } + return requesterInfo; +} + +int32_t RequesterManager::AddImaChannel(int32_t pid, sptr channel) +{ + int32_t ret = AddChannel(pid, channel, nullptr); + IMSA_HILOGI("pid[%{public}d], register result: %{public}d", pid, ret); + return ret; +} + +int32_t RequesterManager::AddImcChannel(int32_t pid, sptr channel) +{ + int32_t ret = AddChannel(pid, nullptr, channel); + IMSA_HILOGI("pid[%{public}d], register result: %{public}d", pid, ret); + return ret; +} + +void RequesterManager::TaskIn(int32_t pid) +{ + std::lock_guard lock(mutex_); + auto iter = requesterMap_.find(pid); + if (iter == requesterMap_.end()) { + IMSA_HILOGE("client: %{public}d not found", pid); + return; + } + if (iter->second == nullptr) { + IMSA_HILOGE("pid %{public}d info nullptr", pid); + return; + } + ++iter->second->requestCount; +} + +void RequesterManager::TaskOut(int32_t pid) +{ + std::lock_guard lock(mutex_); + auto iter = requesterMap_.find(pid); + if (iter == requesterMap_.end()) { + IMSA_HILOGE("client: %{public}d not found", pid); + return; + } + if (iter->second == nullptr) { + IMSA_HILOGE("pid %{public}d info nullptr", pid); + return; + } + if (iter->second->requestCount > 0) { + --iter->second->requestCount; + } +} + +int32_t RequesterManager::AddChannel( + int32_t pid, sptr imaChannel, sptr imcChannel) +{ + const bool isAddingIma = imaChannel != nullptr; + const bool isAddingImc = imcChannel != nullptr; + if ((imaChannel && imcChannel) || (!imaChannel && !imcChannel)) { + IMSA_HILOGE("both nullptr or both not nullptr"); + return ErrorCode::ERROR_IMSA_NULLPTR; + } + std::lock_guard lock(mutex_); + std::shared_ptr info; + auto iter = requesterMap_.find(pid); + if (iter != requesterMap_.end() && iter->second != nullptr) { + info = iter->second; + if ((isAddingIma && info->imaResponseChannel != nullptr) || + (isAddingImc && info->imcResponseChannel != nullptr)) { + IMSA_HILOGD("client: %{public}d already registered", pid); + return ErrorCode::NO_ERROR; + } + if (info->imaResponseChannel != nullptr || info->imcResponseChannel != nullptr) { + info->AddChannel(imaChannel, imcChannel); + return ErrorCode::NO_ERROR; + } + } else { + info = std::make_shared(); + } + + sptr deathRecipient = new (std::nothrow) InputDeathRecipient(); + if (deathRecipient == nullptr) { + IMSA_HILOGE("failed to new deathRecipient!"); + return ErrorCode::ERROR_NULL_POINTER; + } + deathRecipient->SetDeathRecipient([this, pid](const wptr &remote) { OnClientDied(pid); }); + auto object = isAddingIma ? imaChannel->AsObject() : imcChannel->AsObject(); + if (object == nullptr || !object->IsProxyObject() || !object->AddDeathRecipient(deathRecipient)) { + IMSA_HILOGE("failed to add death recipient"); + return ErrorCode::ERROR_ADD_DEATH_RECIPIENT_FAILED; + } + info->deathRecipient = deathRecipient; + info->AddChannel(imaChannel, imcChannel); + requesterMap_.insert_or_assign(pid, info); + IMSA_HILOGI("register success, pid: %{public}d", pid); + return ErrorCode::NO_ERROR; +} + +void RequesterManager::OnClientDied(int32_t pid) +{ + std::lock_guard lock(mutex_); + IMSA_HILOGI("requester: %{public}d died", pid); + auto iter = requesterMap_.find(pid); + if (iter == requesterMap_.end()) { + IMSA_HILOGD("already removed"); + return; + } + auto info = iter->second; + if (info != nullptr) { + if (info->imaResponseChannel != nullptr && info->imaResponseChannel->AsObject() != nullptr) { + info->imaResponseChannel->AsObject()->RemoveDeathRecipient(info->deathRecipient); + } + if (info->imcResponseChannel != nullptr && info->imcResponseChannel->AsObject() != nullptr) { + info->imcResponseChannel->AsObject()->RemoveDeathRecipient(info->deathRecipient); + } + } + requesterMap_.erase(pid); + IMSA_HILOGI("requester: %{public}d removed", pid); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/services/task_manager/src/sa_task_manager.cpp b/services/task_manager/src/sa_task_manager.cpp new file mode 100644 index 000000000..abe42eb98 --- /dev/null +++ b/services/task_manager/src/sa_task_manager.cpp @@ -0,0 +1,484 @@ +/* + * 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 "sa_task_manager.h" + +#include +#include + +#include "global.h" +#include "ime_info_inquirer.h" +#include "requester_manager.h" +#include "sa_action_wait.h" +#include "xcollie/watchdog.h" + +namespace OHOS { +namespace MiscServices { +using namespace AppExecFwk; +constexpr const char *THREAD_NAME = "OS_ImsaTaskHandler"; +constexpr const uint64_t WATCHDOG_TIMEOUT = 10000; // 10S +const std::unordered_set WHITE_LIST_REQUESTS = { SaTaskCode::UPDATE_LISTEN_EVENT_FLAG }; +SaTaskManager::SaTaskManager() +{ + // initialized the event handler with 10s timeout watchdog + auto runner = EventRunner::Create(THREAD_NAME); + eventHandler_ = std::make_shared(runner); + auto ret = HiviewDFX::Watchdog::GetInstance().AddThread(THREAD_NAME, eventHandler_, nullptr, WATCHDOG_TIMEOUT); + if (ret != 0) { + IMSA_HILOGW("failed to add watch dog ret: %{public}d", ret); + } +} + +void SaTaskManager::Init() +{ + identityChecker_ = std::make_shared(); + inited_ = true; +} + +SaTaskManager &SaTaskManager::GetInstance() +{ + static SaTaskManager instance; + return instance; +} + +int32_t SaTaskManager::PostTask(SaTaskPtr task, uint32_t delayMs) +{ + if (task == nullptr) { + IMSA_HILOGE("task is nullptr"); + return ErrorCode::ERROR_NULL_POINTER; + } + auto func = [this, task]() { OnNewTask(task); }; + bool ret = eventHandler_->PostTask(func, __FUNCTION__, delayMs); + if (!ret) { + IMSA_HILOGE("failed to post task: %{public}u", static_cast(task->GetCode())); + return ErrorCode::ERROR_SA_POST_TASK_FAILED; + } + RequesterManager::GetInstance().TaskIn(task->GetCallerInfo().pid); + return ErrorCode::NO_ERROR; +} + +void SaTaskManager::ProcessAsync() +{ + auto func = [=] { Process(); }; + eventHandler_->PostTask(func, __FUNCTION__); +} + +void SaTaskManager::Complete(uint64_t resumeId) +{ + PostTask(std::make_shared(SaTaskCode::RESUME_WAIT, resumeId)); +} + +int32_t SaTaskManager::Pend(SaActionPtr action) +{ + if (action == nullptr) { + IMSA_HILOGE("action is nullptr"); + return ErrorCode::ERROR_NULL_POINTER; + } + if (curTask_ == nullptr || !curTask_->IsRunning()) { + IMSA_HILOGE("curTask_ is NULL or not running, pend failed!"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + return curTask_->Pend(std::move(action)); +} + +int32_t SaTaskManager::Pend(const SaActionFunc &func) +{ + return Pend(std::make_unique(func)); +} + +int32_t SaTaskManager::PendWaitResult(const SaActionFunc &func) +{ + if (curTask_ == nullptr || !curTask_->IsPaused()) { + IMSA_HILOGE("curTask_ is NULL or not paused, pend failed!"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + auto action = std::make_unique(func); + return curTask_->PendWaitResult(std::move(action)); +} + +int32_t SaTaskManager::WaitExec(std::unique_ptr waitAction, const SaActionFunc &execFunc) +{ + if (waitAction == nullptr) { + IMSA_HILOGE("wait action nullptr"); + return ErrorCode::ERROR_IMSA_NULLPTR; + } + int32_t ret = Pend(std::move(waitAction)); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("Pend ActionWait failed, ret: %{public}d", ret); + return ret; + } + + auto exec = std::make_unique(execFunc); + ret = Pend(execFunc); + if (ret != ErrorCode::NO_ERROR) { + IMSA_HILOGE("Pend Action failed, ret: %{public}d", ret); + return ret; + } + return ErrorCode::NO_ERROR; +} + +void SaTaskManager::SetInited(bool flag) +{ + inited_ = flag; +} + +void SaTaskManager::OnNewTask(SaTaskPtr task) +{ + if (task == nullptr) { + IMSA_HILOGE("task is nullptr"); + return; + } + auto taskType = task->GetType(); + if (taskType <= SaTaskType::TYPE_INVALID || taskType >= SaTaskType::TYPE_TOTAL_COUNT) { + IMSA_HILOGE("task type %{public}d unknown!", taskType); + return; + } + switch (taskType) { + case SaTaskType::TYPE_CRITICAL_CHANGE: { + criticalTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_SWITCH_IME: { + switchImeTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_HIGHER_REQUEST: { + higherRequestTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_NORMAL_REQUEST: { + normalRequestTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_QUERY: { + queryTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_RESUME: { + resumeTasks_.push_back(task); + break; + } + case SaTaskType::TYPE_INNER: { + innerTasks_.push_back(task); + break; + } + default: { + IMSA_HILOGE("task type %{public}d unknown!", taskType); + return; + } + } + + Process(); +} + +void SaTaskManager::Process() +{ + // tasks creating resume inner tasks + ProcessNextResumeTask(); + // tasks acting on the current paused task + ProcessNextInnerTask(); + + // tasks bringing critical changes or updates + ProcessNextCriticalTask(); + // tasks causing ime switch + ProcessNextSwitchImeTask(); + + // tasks with a higher priority request + ProcessNextHigherRequestTask(); + // tasks with a normal priority request + ProcessNextNormalRequestTask(); + + // tasks used for querying + ProcessNextQueryTask(); +} + +void SaTaskManager::ProcessNextCriticalTask() +{ + if (criticalTasks_.empty()) { + return; + } + // CRITICAL_CHANGE task has the highest priority. If curTask_ exists and is not CRITICAL_CHANGE task, drop it. + if (curTask_ != nullptr) { + if (curTask_->GetType() == SaTaskType::TYPE_CRITICAL_CHANGE && curTask_->IsPaused()) { + return; + } + curTask_.reset(); + } + + while (curTask_ == nullptr) { + if (criticalTasks_.empty()) { + return; + } + curTask_ = criticalTasks_.front(); + criticalTasks_.pop_front(); + ExecuteCurrentTask(); + } +} + +void SaTaskManager::ProcessNextSwitchImeTask() +{ + if (switchImeTasks_.empty()) { + return; + } + // SWITCH_IME task has a priority second to type CRITICAL_CHANGE. + if (curTask_ != nullptr) { + if (curTask_->IsPaused()) { + return; + } + IMSA_HILOGW("curTask_ state abnormal! Reset"); + curTask_.reset(); + } + + while (curTask_ == nullptr) { + if (switchImeTasks_.empty()) { + return; + } + curTask_ = switchImeTasks_.front(); + switchImeTasks_.pop_front(); + ExecuteCurrentTask(); + } +} + +void SaTaskManager::ProcessNextHigherRequestTask() +{ + if (!inited_) { + IMSA_HILOGW("not initialized yet"); + return; + } + if (higherRequestTasks_.empty()) { + IMSA_HILOGD("immeRequestTasks_ empty"); + return; + } + + // If curTask_ is NULL or state abnormal, execute higherRequestTasks_ directly. + if (curTask_ == nullptr || !curTask_->IsPaused()) { + curTask_.reset(); + while (!higherRequestTasks_.empty() && curTask_ == nullptr) { + curTask_ = higherRequestTasks_.front(); + higherRequestTasks_.pop_front(); + ExecuteCurrentTask(); + } + return; + } + + // If curTask_ not NULL, task which comes from target app and is in white list can be executed. + pausedTask_ = std::move(curTask_); + std::list remainingTasks; + while (!higherRequestTasks_.empty()) { + auto task = higherRequestTasks_.front(); + higherRequestTasks_.pop_front(); + // Task not from target app, keep waiting. + auto callerBundleName = identityChecker_->GetBundleNameByToken(task->GetCallerInfo().tokenId); + if (pausedTask_->GetPauseInfo().target != callerBundleName) { + remainingTasks.push_back(task); + IMSA_HILOGW( + "task %{public}u not from target app, push back to tasks", static_cast(task->GetCode())); + continue; + } + // Task from target app but not in whitelist, reject directly. + if (!IsWhiteListRequest(task->GetCode())) { + task->OnResponse(ErrorCode::ERROR_IMSA_TASK_TIMEOUT); + IMSA_HILOGW("task %{public}u from target app is dropped", static_cast(task->GetCode())); + continue; + } + // Task from target app and in white list, execute it. + curTask_ = task; + ExecuteCurrentTask(); + } + // Restore curTask_ with pausedTask, restore immeRequestTasks_ with tasks still waiting. + curTask_ = std::move(pausedTask_); + higherRequestTasks_ = std::move(remainingTasks); +} + +void SaTaskManager::ProcessNextNormalRequestTask() +{ + if (!inited_) { + IMSA_HILOGW("not initialized yet"); + return; + } + if (normalRequestTasks_.empty()) { + IMSA_HILOGD("requestTasks_ empty"); + return; + } + + // If curTask_ is NULL or state abnormal, execute normalRequestTasks_ directly. + if (curTask_ == nullptr || !curTask_->IsPaused()) { + while (!normalRequestTasks_.empty() && curTask_ == nullptr) { + curTask_ = normalRequestTasks_.front(); + normalRequestTasks_.pop_front(); + ExecuteCurrentTask(); + } + return; + } + + // If curTask_ not NULL, task which is from target app and in white list can be executed. + pausedTask_ = std::move(curTask_); + std::list remainingTask; + while (!normalRequestTasks_.empty()) { + auto task = normalRequestTasks_.front(); + normalRequestTasks_.pop_front(); + // Task not from target app, keep waiting. + auto callerBundleName = identityChecker_->GetBundleNameByToken(task->GetCallerInfo().tokenId); + if (pausedTask_->GetPauseInfo().target != callerBundleName) { + remainingTask.push_back(task); + IMSA_HILOGW( + "task %{public}u not from target app, push back to tasks", static_cast(task->GetCode())); + continue; + } + // Task from target app but not in whitelist, reject it. + if (!IsWhiteListRequest(task->GetCode())) { + task->OnResponse(ErrorCode::ERROR_IMSA_TASK_TIMEOUT); + IMSA_HILOGW("task %{public}u from target app is dropped", static_cast(task->GetCode())); + continue; + } + // Task from target app and in white list, execute it. + curTask_ = task; + ExecuteCurrentTask(); + } + // Restore curTask_ with pausedTask, restore normalRequestTasks_ with tasks still waiting. + curTask_ = std::move(pausedTask_); + normalRequestTasks_ = std::move(remainingTask); +} + +void SaTaskManager::ProcessNextQueryTask() +{ + if (!inited_) { + IMSA_HILOGW("not initialized yet"); + return; + } + if (queryTasks_.empty()) { + IMSA_HILOGD("queryTasks_ empty"); + return; + } + // QUERY tasks can be executed when curTask_ exists. + auto pausedTask = std::move(curTask_); + while (curTask_ == nullptr && !queryTasks_.empty()) { + curTask_ = queryTasks_.front(); + queryTasks_.pop_front(); + ExecuteCurrentTask(); + } + curTask_ = std::move(pausedTask); +} + +void SaTaskManager::ProcessNextResumeTask() +{ + if (resumeTasks_.empty()) { + IMSA_HILOGD("resumeTasks_ empty, return"); + return; + } + // RESUME tasks can be executed when curTask_ exists. + pausedTask_ = std::move(curTask_); + while (curTask_ == nullptr && !resumeTasks_.empty()) { + curTask_ = resumeTasks_.front(); + resumeTasks_.pop_front(); + ExecuteCurrentTask(); + } + curTask_ = std::move(pausedTask_); +} + +void SaTaskManager::ProcessNextInnerTask() +{ + while (curTask_ != nullptr) { + // curTask_ is not NULL, it must be paused + // Loop through innerTasks_, try resume + if (innerTasks_.empty()) { + IMSA_HILOGD("innerTasks_ empty, return"); + return; + } + + auto task = innerTasks_.front(); + innerTasks_.pop_front(); + auto state = curTask_->OnTask(task); + if (state == RUNNING_STATE_COMPLETED) { + // current task completed + curTask_.reset(); + innerTasks_.clear(); + return; + } + + if (state == RUNNING_STATE_PAUSED) { + // current task still paused, try next inner task + continue; + } + + // unreachable + IMSA_HILOGE("Unexpected OnTask result %{public}d", state); + curTask_.reset(); + innerTasks_.clear(); + } +} + +void SaTaskManager::ExecuteCurrentTask() +{ + if (curTask_ == nullptr) { + return; + } + auto state = curTask_->Execute(); + if (state == RUNNING_STATE_COMPLETED) { + IMSA_HILOGI("curTask_ %{public}u completed", static_cast(curTask_->GetCode())); + curTask_.reset(); + ProcessAsync(); + return; + } + if (state == RUNNING_STATE_PAUSED) { + IMSA_HILOGI("curTask_ %{public}u paused", static_cast(curTask_->GetCode())); + return; + } + IMSA_HILOGE("Unexpected execute result state: %{public}u", state); + curTask_.reset(); +} + +void SaTaskManager::TryResume(const PauseType &pauseType, const CallerInfo &callerInfo) +{ + if (pausedTask_ == nullptr || !pausedTask_->IsPaused()) { + IMSA_HILOGD("curTask_ nullptr or not paused state"); + return; + } + PauseInfo pausedInfo = pausedTask_->GetPauseInfo(); + if (pausedInfo.type == PauseType::PAUSED_TYPE_INVALID) { + IMSA_HILOGE("invalid pause type"); + return; + } + if (pauseType != pausedInfo.type) { + IMSA_HILOGD("type not match, type: %{public}d, target: %{public}d", static_cast(pauseType), + static_cast(pausedInfo.type)); + return; + } + if (callerInfo.bundleName != pausedInfo.target) { + IMSA_HILOGD("bundleName not match, caller: %{public}s, target: %{public}s", callerInfo.bundleName.c_str(), + pausedInfo.target.c_str()); + return; + } + IMSA_HILOGI("start resume, %{public}s", pausedInfo.ToString().c_str()); + Complete(pausedInfo.resumeId); +} + +void SaTaskManager::Reset() +{ + inited_ = false; + curTask_ = nullptr; + eventHandler_ = nullptr; + innerTasks_.clear(); + criticalTasks_.clear(); + normalRequestTasks_.clear(); + queryTasks_.clear(); +} + +bool SaTaskManager::IsWhiteListRequest(SaTaskCode taskCode) +{ + return WHITE_LIST_REQUESTS.find(taskCode) != WHITE_LIST_REQUESTS.end(); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/services/task_manager/src/tasks/sa_task.cpp b/services/task_manager/src/tasks/sa_task.cpp new file mode 100644 index 000000000..dbb03904f --- /dev/null +++ b/services/task_manager/src/tasks/sa_task.cpp @@ -0,0 +1,250 @@ +/* + * 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 "sa_task.h" + +#include + +#include "requester_manager.h" + +namespace OHOS { +namespace MiscServices { +SaTask::~SaTask() +{ + if (!hasResponded_) { + failRet_ = ErrorCode::ERROR_IMSA_TASK_TIMEOUT; + InvokeResponse(); + } + RequesterManager::GetInstance().TaskOut(callerInfo_.pid); +} + +RunningState SaTask::Execute() +{ + if (state_ != RUNNING_STATE_IDLE) { + IMSA_HILOGE("Task not runnable, state=%{public}u", state_); + return RUNNING_STATE_ERROR; + } + return ExecuteInner(); +} + +RunningState SaTask::Resume(uint64_t resumeId) +{ + if (action_ == nullptr) { + IMSA_HILOGE("curAction_ is nullptr"); + return RUNNING_STATE_ERROR; + } + + if (state_ != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("state_ is %{public}u, do not need to resume", state_); + return RUNNING_STATE_ERROR; + } + + int32_t ret = 0; + state_ = action_->Resume(resumeId, ret, responseData_); + if (state_ == RUNNING_STATE_PAUSED) { + return state_; + } + if (state_ == RUNNING_STATE_COMPLETED) { + IMSA_HILOGE("curAction_ complete!"); + return RUNNING_STATE_COMPLETED; + } + IMSA_HILOGE("curAction_ resume return %{public}u, error!", state_); + return RUNNING_STATE_ERROR; +} + +RunningState SaTask::OnTask(const std::shared_ptr &task) +{ + if (task == nullptr) { + IMSA_HILOGE("task is nullptr"); + return state_; + } + auto type = task->GetType(); + if (type == SaTaskType::TYPE_INNER) { + return Resume(task->GetSeqId()); + } + return state_; +} + +int32_t SaTask::PendWaitResult(std::unique_ptr action) +{ + if (action == nullptr || action->GetState() != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("action is nullptr or not paused, pend failed"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + return action_->Pend(std::move(action)); +} + +int32_t SaTask::Pend(std::unique_ptr action) +{ + if (action_ == nullptr || action_->GetState() != RUNNING_STATE_RUNNING) { + IMSA_HILOGE("curAction_ is NULL or not running, pend failed!"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + return action_->Pend(std::move(action)); +} + +int32_t SaTask::Pend(SaActionFunc func) +{ + if (action_ == nullptr || action_->GetState() != RUNNING_STATE_RUNNING) { + IMSA_HILOGE("curAction_ is NULL or not running, pend failed!"); + return ErrorCode::ERROR_SA_TASK_MANAGER_PEND_ACTION_FAILED; + } + return action_->Pend(std::make_unique(func)); +} + +SaTaskType SaTask::GetType() const +{ + auto type = static_cast(code_); + if (type >= static_cast(SaTaskCode::TASK_CRITICAL_CHANGE_BEGIN) + && type <= static_cast(SaTaskCode::TASK_CRITICAL_CHANGE_END)) { + return SaTaskType::TYPE_CRITICAL_CHANGE; + } + if (type >= static_cast(SaTaskCode::TASK_SWITCH_IME_BEGIN) + && type <= static_cast(SaTaskCode::TASK_SWITCH_IME_END)) { + return SaTaskType::TYPE_SWITCH_IME; + } + if (type >= static_cast(SaTaskCode::TASK_HIGHER_REQUEST_BEGIN) + && type <= static_cast(SaTaskCode::TASK_HIGHER_REQUEST_END)) { + return SaTaskType::TYPE_HIGHER_REQUEST; + } + if (type >= static_cast(SaTaskCode::TASK_NORMAL_REQUEST_BEGIN) + && type <= static_cast(SaTaskCode::TASK_NORMAL_REQUEST_END)) { + return SaTaskType::TYPE_NORMAL_REQUEST; + } + if (type >= static_cast(SaTaskCode::TASK_QUERY_BEGIN) + && type <= static_cast(SaTaskCode::TASK_QUERY_END)) { + return SaTaskType::TYPE_QUERY; + } + if (type >= static_cast(SaTaskCode::TASK_RESUME_BEGIN) + && type <= static_cast(SaTaskCode::TASK_RESUME_END)) { + return SaTaskType::TYPE_RESUME; + } + if (type >= static_cast(SaTaskCode::TASK_INNER_BEGIN) + && type <= static_cast(SaTaskCode::TASK_INNER_END)) { + return SaTaskType::TYPE_INNER; + } + return SaTaskType::TYPE_INVALID; +} + +SaTaskCode SaTask::GetCode() const +{ + return code_; +} + +uint64_t SaTask::GetSeqId() const +{ + return seqId_; +} + +CallerInfo SaTask::GetCallerInfo() const +{ + return callerInfo_; +} + +bool SaTask::IsRunning() const +{ + if (action_ == nullptr) { + return state_ == RUNNING_STATE_RUNNING; + } + return action_->GetState() == RUNNING_STATE_RUNNING; +} + +bool SaTask::IsPaused() const +{ + return state_ == RUNNING_STATE_PAUSED; +} + +uint64_t SaTask::GetNextSeqId() +{ + static std::atomic seqId{ 1 }; + return seqId.fetch_add(1, std::memory_order_seq_cst); +} + +void SaTask::SetHiSysReporter(const ReportFunc &func) +{ + hiSysReporter_ = std::make_unique(func); +} + +RunningState SaTask::ExecuteInner() +{ + state_ = RUNNING_STATE_RUNNING; + if (action_ == nullptr) { + IMSA_HILOGW("action_ is nullptr"); + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + + RunningState state = action_->Execute(retCode_, responseData_); + if (state == RUNNING_STATE_COMPLETED) { + InvokeResponse(); + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + + state_ = RUNNING_STATE_PAUSED; + return state_; +} + +void SaTask::OnResponse(int32_t retCode) +{ + retCode_ = retCode; + InvokeResponse(); +} + +void SaTask::InvokeResponse() +{ + hasResponded_ = true; + + if (retCode_ != ErrorCode::NO_ERROR) { + // When failRect_ is set validly, return failRet_. + if (failRet_ != INVALID_FAIL_CODE) { + retCode_ = failRet_; + } + } + if (hiSysReporter_ != nullptr) { + hiSysReporter_->Execute(retCode_, responseData_); + } + + action_ = nullptr; + + if (imaResponseChannel_ == nullptr && imcResponseChannel_ == nullptr) { + IMSA_HILOGD("no need response"); + return; + } + + ServiceResponseDataInner inner; + if (responseData_.valueless_by_exception()) { + inner.data = std::monostate(); + } else { + inner.data = responseData_; + } + if (imaResponseChannel_ != nullptr) { + imaResponseChannel_->OnResponse(callerInfo_.requestId, retCode_, inner); + } + if (imcResponseChannel_ != nullptr) { + imcResponseChannel_->OnResponse(callerInfo_.requestId, retCode_, inner); + } +} + +PauseInfo SaTask::GetPauseInfo() +{ + if (action_ == nullptr || action_->GetState() != RUNNING_STATE_PAUSED) { + IMSA_HILOGE("curAction_ nullptr or not paused"); + return {}; + } + return action_->GetPausedInfo(); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 624315eeb..062d8c78a 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -45,6 +45,7 @@ group("unittest") { "cpp_test:JsonOperateTest", "cpp_test:NewImeSwitchTest", "cpp_test:NumKeyAppsManagerTest", + "cpp_test:SaTaskManagerTest", "cpp_test:OnDemandStartStopSaTest", "cpp_test:StringUtilsTest", "cpp_test:TaskManagerTest", diff --git a/test/unittest/cpp_test/BUILD.gn b/test/unittest/cpp_test/BUILD.gn index 5d18c1b0e..7ab89b7e7 100644 --- a/test/unittest/cpp_test/BUILD.gn +++ b/test/unittest/cpp_test/BUILD.gn @@ -1580,6 +1580,66 @@ ohos_unittest("NumKeyAppsManagerTest") { ] } +ohos_unittest("SaTaskManagerTest") { + branch_protector_ret = "pac_ret" + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + module_out_path = module_output_path + include_dirs = [ + "${inputmethod_path}/common/include", + "${inputmethod_path}/frameworks/native/inputmethod_ability/include/actions", + "${inputmethod_path}/services/task_manager/include", + "${inputmethod_path}/services/task_manager/include/actions", + "${inputmethod_path}/services/task_manager/include/tasks", + ] + sources = [ + "src/sa_task_manager_test.cpp", + "${inputmethod_path}/frameworks/native/common/src/service_response_data.cpp", + "${inputmethod_path}/services/task_manager/src/requester_manager.cpp", + "${inputmethod_path}/services/task_manager/src/sa_task_manager.cpp", + "${inputmethod_path}/services/task_manager/src/actions/sa_action.cpp", + "${inputmethod_path}/services/task_manager/src/actions/sa_action_wait.cpp", + "${inputmethod_path}/services/task_manager/src/tasks/sa_task.cpp", + ] + + configs = [ ":module_private_config" ] + deps = [ + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:ima_response_channel_proxy", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:ima_response_channel_stub", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:imc_response_channel_proxy", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:imc_response_channel_stub", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:inputmethod_client_static", + "${inputmethod_path}/services:inputmethod_service_static", + "${inputmethod_path}/services/adapter/settings_data_provider:settings_data_static", + "${inputmethod_path}/services/file:imf_file_static", + "${inputmethod_path}/services/json:imf_json_static", + "${inputmethod_path}/test/unittest/cpp_test/common:inputmethod_tdd_util", + "${inputmethod_path}/interfaces/inner_api/inputmethod_ability:ima_response_channel_proxy", + "${inputmethod_path}/interfaces/inner_api/inputmethod_controller:imc_response_channel_proxy", + + ] + + external_deps = [ + "ability_base:want", + "access_token:libaccesstoken_sdk", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "config_policy:configpolicy_util", + "data_share:datashare_common", + "data_share:datashare_consumer", + "googletest:gtest_main", + "hicollie:libhicollie", + "hilog:libhilog", + "init:libbeget_proxy", + "init:libbegetutil", + "input:libmmi-client", + "resource_management:global_resmgr", + ] +} + ohos_unittest("ImaTextEditTest") { branch_protector_ret = "pac_ret" sanitize = { diff --git a/test/unittest/cpp_test/src/sa_task_manager_test.cpp b/test/unittest/cpp_test/src/sa_task_manager_test.cpp new file mode 100644 index 000000000..8fed514b8 --- /dev/null +++ b/test/unittest/cpp_test/src/sa_task_manager_test.cpp @@ -0,0 +1,701 @@ +/* + * 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 "sa_task_manager.h" + +#include "response_data_util.h" +#include "service_response_data.h" +#undef private + +#include +#include +#include + +#include +#include + +#include "identity_checker_mock.h" + +using namespace testing; +using namespace testing::ext; +namespace OHOS { +namespace MiscServices { +constexpr uint32_t PAUSE_TIMEOUT = 100; // 100ms +constexpr uint32_t DELAY_TIME = 200; // 200ms +constexpr uint32_t SUCCESS_RESULT = 100; +constexpr uint32_t FAILED_RESULT = 999; +constexpr uint32_t WAIT_EXEC_END = 50000; // 50ms +constexpr uint32_t WAIT_PAUSE_EXEC_END = 500000; // 500ms +constexpr uint32_t CONSTANTS_1 = 1; +constexpr uint32_t CONSTANTS_2 = 2; +constexpr uint32_t CONSTANTS_5 = 5; +constexpr uint32_t CONSTANTS_12 = 12; +class SaTaskManagerTest : public testing::Test { +public: + static void SetUpTestCase() + { + IMSA_HILOGI("SaTaskManagerTest::SetUpTestCase"); + SaTaskManager::GetInstance().Init(); + SaTaskManager::GetInstance().identityChecker_ = std::make_shared(); + } + static void TearDownTestCase() + { + IMSA_HILOGI("SaTaskManagerTest::TearDownTestCase"); + } + void SetUp() + { + IMSA_HILOGI("SaTaskManagerTest::SetUp"); + SaTaskManagerTest::result_ = 0; + } + void TearDown() + { + IMSA_HILOGI("SaTaskManagerTest::TearDown"); + SaTaskManagerTest::result_ = 0; + } + + static bool TestSamePriorityTaskOrdering(SaTaskCode codeTypeBegin); + static bool TestSameInterrupt(SaTaskCode codeTypeBegin, bool isPauseTimeout); + static bool TestLowInterruptHigh(SaTaskCode higherTaskCode, SaTaskCode lowerTaskCode, bool isPauseTimeout); + static bool TestHighInterruptLow(SaTaskCode higherTaskCode, SaTaskCode lowerTaskCode, bool isPauseTimeout); + + static bool TestPauseAndExec(SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout); + static bool TestPauseAndExecWhiteListRequest( + SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout); + static bool TestPauseAndExecNonWhiteListRequest( + SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout); + + static void SetResult(uint32_t result); + static uint32_t GetResult(); + static int32_t StartPause(const PauseInfo &info, uint32_t timeoutMs); + static std::mutex mtx_; + static uint32_t result_; +}; +uint32_t SaTaskManagerTest::result_{ 0 }; +std::mutex SaTaskManagerTest::mtx_{}; + +bool SaTaskManagerTest::TestSamePriorityTaskOrdering(SaTaskCode codeTypeBegin) +{ + result_ = CONSTANTS_1; + uint32_t taskCode1 = static_cast(codeTypeBegin) + 1; + uint32_t taskCode2 = static_cast(codeTypeBegin) + 2; + IMSA_HILOGI("taskCode1: %{public}u, taskCode2: %{public}u", taskCode1, taskCode2); + auto action1 = [](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec task1 start"); + result_ += CONSTANTS_5; + IMSA_HILOGI("exec task1 end"); + return ErrorCode::NO_ERROR; + }; + auto task1 = std::make_shared(static_cast(taskCode1), action1); + auto action2 = [](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec task2 start"); + result_ *= CONSTANTS_2; + IMSA_HILOGI("exec task2 end"); + return ErrorCode::NO_ERROR; + }; + auto task2 = std::make_shared(static_cast(taskCode2), action2); + SaTaskManager::GetInstance().PostTask(task1); + SaTaskManager::GetInstance().PostTask(task2); + usleep(WAIT_EXEC_END); + return result_ == CONSTANTS_12; // (1 + 5) * 2 = 12 +} + +bool SaTaskManagerTest::TestSameInterrupt(SaTaskCode codeTypeBegin, bool isPauseTimeout) +{ + result_ = 0; + uint32_t taskCode1 = static_cast(codeTypeBegin) + 1; + uint32_t taskCode2 = static_cast(codeTypeBegin) + 2; + IMSA_HILOGI("taskCode1: %{public}u, taskCode2: %{public}u", taskCode1, taskCode2); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestLowInterruptHigh" }; + auto action1 = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + result_ += CONSTANTS_1; + IMSA_HILOGI("exec task1 start"); + return StartPause(info, PAUSE_TIMEOUT); + }; + auto task1 = std::make_shared(static_cast(taskCode1), action1); + + auto action2 = [](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec task2 start"); + result_ *= 2; + IMSA_HILOGI("exec task2 end"); + return ErrorCode::NO_ERROR; + }; + auto task2 = std::make_shared(static_cast(taskCode2), action2); + + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + SaTaskManager::GetInstance().PostTask(task1); + SaTaskManager::GetInstance().PostTask(task2); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + usleep(WAIT_PAUSE_EXEC_END); + uint32_t expectValue = 0; + if (isPauseTimeout) { + expectValue = (CONSTANTS_1 + FAILED_RESULT) * CONSTANTS_2; + } else { + expectValue = (CONSTANTS_1 + SUCCESS_RESULT) * CONSTANTS_2; + } + return result_ == expectValue; +} + +bool SaTaskManagerTest::TestLowInterruptHigh(SaTaskCode higherTaskCode, SaTaskCode lowerTaskCode, bool isPauseTimeout) +{ + result_ = 0; + IMSA_HILOGI("higherTaskCode: %{public}u, lowerTaskCode: %{public}u", static_cast(higherTaskCode), + static_cast(lowerTaskCode)); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestLowInterruptHigh" }; + // create higher task + auto higherAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + result_ += CONSTANTS_1; + IMSA_HILOGI("exec higherTask start"); + return StartPause(info, PAUSE_TIMEOUT); + }; + auto higherTask = std::make_shared(higherTaskCode, higherAction); + + // create lower task + auto lowerAction = [](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec lowerTask start"); + result_ *= CONSTANTS_2; + IMSA_HILOGI("exec lowerTask end"); + return ErrorCode::NO_ERROR; + }; + auto lowerTask = std::make_shared(lowerTaskCode, lowerAction); + + // create higher task's resume task + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + + // post order: higherTask -> lowerTask -> resumeTask + SaTaskManager::GetInstance().PostTask(higherTask); + SaTaskManager::GetInstance().PostTask(lowerTask); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + + // expect exec order: higherTask -> resumeTask -> lowerTask + uint32_t expectValue = 0; + if (isPauseTimeout) { + expectValue = (CONSTANTS_1 + FAILED_RESULT) * CONSTANTS_2; + } else { + expectValue = (CONSTANTS_1 + SUCCESS_RESULT) * CONSTANTS_2; + } + usleep(WAIT_PAUSE_EXEC_END); + return result_ == expectValue; +} + +bool SaTaskManagerTest::TestHighInterruptLow(SaTaskCode higherTaskCode, SaTaskCode lowerTaskCode, bool isPauseTimeout) +{ + result_ = 0; + IMSA_HILOGI("higherTaskCode: %{public}u, lowerTaskCode: %{public}u", static_cast(higherTaskCode), + static_cast(lowerTaskCode)); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestLowInterruptHigh" }; + auto lowerAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + result_ += CONSTANTS_1; + IMSA_HILOGI("exec lowerTask start"); + return StartPause(info, PAUSE_TIMEOUT); + }; + auto lowerTask = std::make_shared(static_cast(lowerTaskCode), lowerAction); + + auto higherAction = [](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec higherTask start"); + result_ *= CONSTANTS_2; + IMSA_HILOGI("exec higherTask end"); + return ErrorCode::NO_ERROR; + }; + auto higherTask = std::make_shared(static_cast(higherTaskCode), higherAction); + + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + + // post order: lowerTask -> higherTask -> resumeTask + SaTaskManager::GetInstance().PostTask(lowerTask); + SaTaskManager::GetInstance().PostTask(higherTask); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + + // expect exec order: lowerTask -> higherTask + uint32_t expectValue = CONSTANTS_1 * CONSTANTS_2; + usleep(WAIT_PAUSE_EXEC_END); + return result_ == expectValue; +} + +bool SaTaskManagerTest::TestPauseAndExec(SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout) +{ + result_ = 0; + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute001 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestPostTask006" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseTask start"); + SaTaskManagerTest::result_ = CONSTANTS_1; + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(pausedTaskCode, pauseAction); + + auto newAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec newTask start"); + SaTaskManagerTest::result_ *= CONSTANTS_2; + IMSA_HILOGI("exec newTask end"); + return ErrorCode::NO_ERROR; + }; + auto newTask = std::make_shared(newTaskCode, newAction); + + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(newTask); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + + uint32_t expectedValue = 0; + if (isPauseTimeout) { + expectedValue = (CONSTANTS_1 * CONSTANTS_2) + FAILED_RESULT; + } else { + expectedValue = (CONSTANTS_1 * CONSTANTS_2) + SUCCESS_RESULT; + } + usleep(WAIT_PAUSE_EXEC_END); + return result_ == expectedValue; +} + +bool SaTaskManagerTest::TestPauseAndExecWhiteListRequest( + SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout) +{ + result_ = 0; + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute001 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestPostTask006" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseTask start"); + SaTaskManagerTest::result_ = CONSTANTS_1; + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(pausedTaskCode, pauseAction); + + auto newAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec newTask start"); + SaTaskManagerTest::result_ *= CONSTANTS_2; + IMSA_HILOGI("exec newTask end"); + return ErrorCode::NO_ERROR; + }; + CallerInfo callerInfo = { .bundleName = info.target }; + IdentityCheckerMock::SetBundleName(info.target); + auto newTask = std::make_shared(newTaskCode, newAction, callerInfo); + + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(newTask); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + + uint32_t expectedValue = 0; + if (isPauseTimeout) { + expectedValue = (CONSTANTS_1 * CONSTANTS_2) + FAILED_RESULT; + } else { + expectedValue = (CONSTANTS_1 * CONSTANTS_2) + SUCCESS_RESULT; + } + usleep(WAIT_PAUSE_EXEC_END); + return result_ == expectedValue; +} + +bool SaTaskManagerTest::TestPauseAndExecNonWhiteListRequest( + SaTaskCode pausedTaskCode, SaTaskCode newTaskCode, bool isPauseTimeout) +{ + result_ = 0; + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute001 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "targetBundleName" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseTask start"); + SaTaskManagerTest::result_ = CONSTANTS_1; + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(pausedTaskCode, pauseAction); + + auto newAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec newTask start"); + SaTaskManagerTest::result_ *= CONSTANTS_2; + IMSA_HILOGI("exec newTask end"); + return ErrorCode::NO_ERROR; + }; + CallerInfo callerInfo = { .bundleName = info.target }; + IdentityCheckerMock::SetBundleName(info.target); + auto newTask = std::make_shared(newTaskCode, newAction, callerInfo); + + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeTask start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(info.type, callerInfo); + IMSA_HILOGI("exec resumeTask end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(newTask); + if (isPauseTimeout) { + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + } else { + SaTaskManager::GetInstance().PostTask(resumeTask); + } + + uint32_t expectedValue = 0; + if (isPauseTimeout) { + expectedValue = CONSTANTS_1 + FAILED_RESULT; + } else { + expectedValue = CONSTANTS_1 + SUCCESS_RESULT; + } + usleep(WAIT_PAUSE_EXEC_END); + return result_ == expectedValue; +} + +void SaTaskManagerTest::SetResult(uint32_t result) +{ + std::lock_guard lock(mtx_); + result_ = result; + IMSA_HILOGI("result: %{public}u", result_); +} + +uint32_t SaTaskManagerTest::GetResult() +{ + std::lock_guard lock(mtx_); + IMSA_HILOGI("result: %{public}u", result_); + return result_; +} + +int32_t SaTaskManagerTest::StartPause(const PauseInfo &info, uint32_t timeoutMs) +{ + SaActionFunc onComplete = [](ServiceResponseData &, ActionInnerData &) { + IMSA_HILOGI("onComplete"); + result_ += SUCCESS_RESULT; + return ErrorCode::NO_ERROR; + }; + SaActionFunc onTimeout = [](ServiceResponseData &, ActionInnerData &) { + IMSA_HILOGI("onTimeout"); + result_ += FAILED_RESULT; + return ErrorCode::ERROR_IMSA_IME_START_TIMEOUT; + }; + auto waitAction = std::make_unique(timeoutMs, info, onComplete, onTimeout); + return SaTaskManager::GetInstance().WaitExec(std::move(waitAction)); +} + +/** + * @tc.name: TestSamePriorityOrdering + * @tc.desc: + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestSamePriorityOrdering, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestSamePriorityOrdering START"); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_CRITICAL_CHANGE_BEGIN)); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_SWITCH_IME_BEGIN)); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_HIGHER_REQUEST_BEGIN)); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_NORMAL_REQUEST_BEGIN)); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_RESUME_BEGIN)); + EXPECT_TRUE(TestSamePriorityTaskOrdering(SaTaskCode::TASK_QUERY_BEGIN)); +} + +/** + * @tc.name: TestPauseAndResume001 + * @tc.desc: + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndResume001, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndResume001 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestPostTask001" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseAction start"); + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(SaTaskCode::SWITCH_INPUT_METHOD, pauseAction); + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeAction start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(PauseType::PAUSE_TYPE_START_IME, callerInfo); + IMSA_HILOGI("exec resumeAction end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(resumeTask); + usleep(WAIT_PAUSE_EXEC_END); + EXPECT_EQ(SaTaskManagerTest::GetResult(), SUCCESS_RESULT); +} + +/** + * @tc.name: TestPauseAndResume002 + * @tc.desc: resume timeout + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndResume002, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndResume002 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestPostTask001" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseAction start"); + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(SaTaskCode::SWITCH_INPUT_METHOD, pauseAction); + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeAction start"); + CallerInfo callerInfo = { .bundleName = info.target }; + SaTaskManager::GetInstance().TryResume(PauseType::PAUSE_TYPE_START_IME, callerInfo); + IMSA_HILOGI("exec resumeAction end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(resumeTask, DELAY_TIME); + usleep(WAIT_PAUSE_EXEC_END); + EXPECT_EQ(SaTaskManagerTest::GetResult(), FAILED_RESULT); +} + +/** + * @tc.name: TestPauseAndResume003 + * @tc.desc: resume failed + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndResume003, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndResume003 START"); + PauseInfo info = { .type = PauseType::PAUSE_TYPE_START_IME, .target = "TestPostTask001" }; + auto pauseAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec pauseAction start"); + return SaTaskManagerTest::StartPause(info, PAUSE_TIMEOUT); + }; + auto pauseTask = std::make_shared(SaTaskCode::SWITCH_INPUT_METHOD, pauseAction); + auto resumeAction = [info](ServiceResponseData &, ActionInnerData &) -> int32_t { + IMSA_HILOGI("exec resumeAction start"); + CallerInfo callerInfo = { .bundleName = "invalidValue" }; + SaTaskManager::GetInstance().TryResume(PauseType::PAUSE_TYPE_START_IME, callerInfo); + IMSA_HILOGI("exec resumeAction end"); + return ErrorCode::NO_ERROR; + }; + auto resumeTask = std::make_shared(SaTaskCode::SET_CORE_AND_AGENT, resumeAction); + SaTaskManager::GetInstance().PostTask(pauseTask); + SaTaskManager::GetInstance().PostTask(resumeTask); + usleep(WAIT_PAUSE_EXEC_END); + EXPECT_EQ(SaTaskManagerTest::GetResult(), FAILED_RESULT); +} + +/** + * @tc.name: TestPauseAndInterrupt001 + * @tc.desc: Same priority can not interrupt each other. Post order: task1->task2. Exec order: task1->task2. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndInterrupt001, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndInterrupt001 START"); + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::TASK_CRITICAL_CHANGE_BEGIN, true)); + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::TASK_CRITICAL_CHANGE_BEGIN, false)); + + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::TASK_SWITCH_IME_BEGIN, true)); + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::TASK_SWITCH_IME_BEGIN, false)); + + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::ON_FOCUSED, true)); + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::ON_FOCUSED, false)); + + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestSameInterrupt(SaTaskCode::START_INPUT, false)); +} + +/** + * @tc.name: TestPauseAndInterrupt002 + * @tc.desc: Post order: higher[paused]->lower->resume. Exec order: higher[paused]->resume->higher[resumed]->lowerTask + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndInterrupt002, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndInterrupt002 START"); + // critical task paused, can not be interrupted by lower task. + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::SWITCH_INPUT_METHOD, true)); + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::SWITCH_INPUT_METHOD, false)); + + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::ON_FOCUSED, true)); + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::ON_FOCUSED, false)); + + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::ON_WMS_SA_STARTED, SaTaskCode::START_INPUT, false)); + + // switch ime task paused, can not be interrupted by lower task. + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::ON_FOCUSED, true)); + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::ON_FOCUSED, false)); + + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestLowInterruptHigh(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::START_INPUT, false)); +} + +/** + * @tc.name: TestPauseAndInterrupt003 + * @tc.desc: Post order: lower[paused]->higher->resume, Execute order: lower[paused]->higher->end. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndInterrupt003, TestSize.Level0) +{ + // critical task can interrupt other paused task + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::SWITCH_INPUT_METHOD, true)); + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::SWITCH_INPUT_METHOD, false)); + + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::ON_FOCUSED, true)); + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::ON_FOCUSED, false)); + + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestHighInterruptLow(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::START_INPUT, false)); +} + +/** + * @tc.name: TestPauseAndPermitExecute001 + * @tc.desc: QUERY tasks can be executed when curTask_ is paused. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndPermitExecute001, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute001 START"); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::GET_CURRENT_INPUT_METHOD, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::GET_CURRENT_INPUT_METHOD, false)); + + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::GET_CURRENT_INPUT_METHOD, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::GET_CURRENT_INPUT_METHOD, false)); + + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::START_INPUT, SaTaskCode::GET_CURRENT_INPUT_METHOD, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::START_INPUT, SaTaskCode::GET_CURRENT_INPUT_METHOD, false)); +} + +/** + * @tc.name: TestPauseAndPermitExecute002 + * @tc.desc: RESUME tasks can be executed when curTask_ is paused. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndPermitExecute002, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute002 START"); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::SET_CORE_AND_AGENT, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::SET_CORE_AND_AGENT, false)); + + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::SET_CORE_AND_AGENT, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::SET_CORE_AND_AGENT, false)); + + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::START_INPUT, SaTaskCode::SET_CORE_AND_AGENT, true)); + EXPECT_TRUE(TestPauseAndExec(SaTaskCode::START_INPUT, SaTaskCode::SET_CORE_AND_AGENT, false)); +} + +/** + * @tc.name: TestPauseAndPermitExecute003 + * @tc.desc: REQUEST tasks from target app in WHITE_LIST can be executed when curTask_ is paused. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndPermitExecute003, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute003 START"); + EXPECT_TRUE( + TestPauseAndExecWhiteListRequest(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, true)); + EXPECT_TRUE( + TestPauseAndExecWhiteListRequest(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, false)); + + EXPECT_TRUE( + TestPauseAndExecWhiteListRequest(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, true)); + EXPECT_TRUE( + TestPauseAndExecWhiteListRequest(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, false)); + + EXPECT_TRUE(TestPauseAndExecWhiteListRequest(SaTaskCode::START_INPUT, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, true)); + EXPECT_TRUE(TestPauseAndExecWhiteListRequest(SaTaskCode::START_INPUT, SaTaskCode::UPDATE_LISTEN_EVENT_FLAG, false)); +} + +/** + * @tc.name: TestPauseAndPermitExecute004 + * @tc.desc: REQUEST tasks from target app not in WHITE_LIST will be dropped when curTask_ is paused. + * @tc.type: FUNC + * @tc.require: + * @tc.author: + */ +HWTEST_F(SaTaskManagerTest, TestPauseAndPermitExecute004, TestSize.Level0) +{ + IMSA_HILOGI("SaTaskManagerTest TestPauseAndPermitExecute004 START"); + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::ON_WMS_CONNECTED, SaTaskCode::START_INPUT, false)); + + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::START_INPUT, true)); + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::SWITCH_INPUT_METHOD, SaTaskCode::START_INPUT, false)); + + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::START_INPUT, SaTaskCode::SHOW_INPUT, true)); + EXPECT_TRUE(TestPauseAndExecNonWhiteListRequest(SaTaskCode::START_INPUT, SaTaskCode::SHOW_INPUT, false)); +} +} // namespace MiscServices +} // namespace OHOS -- Gitee