/*
 * Copyright (C) 2021-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "input_method_controller.h"

#include "securec.h"
#include <algorithm>
#include <cinttypes>

#include "block_data.h"
#include "global.h"
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
#include "histogram_plugin_macros.h"
#endif
#include "imc_hisysevent_reporter.h"
#include "ime_event_monitor_manager_impl.h"
#include "input_client_service_impl.h"
#include "input_data_channel_service_impl.h"
#include "input_method_agent_proxy.h"
#include "input_method_property.h"
#include "input_method_status.h"
#include "input_method_system_ability_proxy.h"
#include "input_method_tools.h"
#include "inputmethod_sysevent.h"
#include "inputmethod_trace.h"
#include "iservice_registry.h"
#include "keyevent_consumer_service_impl.h"
#include "notify_service_impl.h"
#include "on_demand_start_stop_sa.h"
#include "on_input_stop_notify_proxy.h"
#include "string_ex.h"
#include "string_utils.h"
#include "sys/prctl.h"
#include "system_ability_definition.h"
#include "system_cmd_channel_stub.h"
#include "system_ability_status_change_stub.h"

namespace OHOS {
namespace MiscServices {
using namespace MessageID;
using namespace std::chrono;
sptr<InputMethodController> InputMethodController::instance_;
std::shared_ptr<AppExecFwk::EventHandler> InputMethodController::handler_ { nullptr };
std::mutex InputMethodController::instanceLock_;
std::mutex InputMethodController::logLock_;
int InputMethodController::keyEventCountInPeriod_ = 0;
std::chrono::system_clock::time_point InputMethodController::startLogTime_ = system_clock::now();
std::mutex InputMethodController::printTextChangeMutex_;
int32_t InputMethodController::textChangeCountInPeriod_ = 0;
std::chrono::steady_clock::time_point InputMethodController::textChangeStartLogTime_ = steady_clock::now();
constexpr uint32_t MAX_ATTACH_TIMEOUT = 2500; // 2.5s
BlockQueue<InputMethodController::CtrlEventInfo> InputMethodController::ctrlEventQueue_ { MAX_ATTACH_TIMEOUT };
constexpr int32_t LOOP_COUNT = 5;
constexpr int32_t LOG_MAX_TIME = 20;
constexpr int32_t LOG_INSERT_MAX_TIME = 20;    // 20s
constexpr int32_t LOG_INSERT_MIN_TIME = 5;     // 5s
constexpr int32_t ATTACH_RETRY_INTERVAL = 50;  // 50ms
constexpr int32_t ATTACH_MAX_RETRY_TIMES = 30; // 30 times
constexpr int64_t DELAY_TIME = 100;
constexpr int32_t ACE_DEAL_TIME_OUT = 200;
constexpr int32_t MAX_PLACEHOLDER_SIZE = 255;  // 256 utf16 char
constexpr int32_t MAX_ABILITY_NAME_SIZE = 127; // 127 utf16 char
static constexpr int32_t MAX_TIMEOUT = 2500;
constexpr int64_t DISPATCH_KEYBOARD_TIME_OUT = 5; // 5ms
constexpr size_t MAX_AGENT_NUMBER = 2;
const std::string IME_MIRROR_NAME = "proxyIme_IME_MIRROR";
class SaMgrListener : public SystemAbilityStatusChangeStub {
public:
    explicit SaMgrListener(std::function<void()> handler) : func_(handler) {};
    ~SaMgrListener() override = default;
    void OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId) override;
    void OnRemoveSystemAbility(int32_t systemAbilityId, const std::string &deviceId) override {}

private:
    std::function<void()> func_;
};

std::mutex listenerMapMutex_;
sptr<ISystemAbilityStatusChange> saMgrListener_;

InputMethodController::InputMethodController()
{
    IMSA_HILOGD("IMC structure.");
}

InputMethodController::~InputMethodController() { }

sptr<InputMethodController> InputMethodController::GetInstance()
{
    static std::once_flag onceFlag;
    std::call_once(onceFlag, [&]() {
        if (instance_ == nullptr) {
            IMSA_HILOGD("instance_ is nullptr!");
            instance_ = new (std::nothrow) InputMethodController();
            if (instance_ == nullptr) {
                IMSA_HILOGE("failed to create InputMethodController!");
                return;
            }
            int32_t ret = instance_->Initialize();
            if (ret != ErrorCode::NO_ERROR) {
                InputMethodSysEvent::GetInstance().InputmethodFaultReporter(ret, "", "IMC initialize failed!");
            }
        }
    });
    return instance_;
}
// LCOV_EXCL_START
int32_t InputMethodController::RestoreListenEventFlag()
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    // 0 represent no need to check permission
    InputClientInfoInner infoInner = InputMethodTools::GetInstance().InputClientInfoToInner(clientInfo_);
    return proxy->UpdateListenEventFlag(infoInner, 0);
}
// LCOV_EXCL_STOP
int32_t InputMethodController::UpdateListenEventFlag(uint32_t finalEventFlag, uint32_t eventFlag, bool isOn)
{
    auto oldEventFlag = clientInfo_.eventFlag;
    clientInfo_.eventFlag = finalEventFlag;
    if (!isOn) {
        auto proxy = TryGetSystemAbilityProxy();
        if (proxy == nullptr) {
            IMSA_HILOGE("proxy is nullptr!");
            return ErrorCode::NO_ERROR;
        }
    }
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        if (isOn) {
            clientInfo_.eventFlag = oldEventFlag;
        }
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    InputClientInfoInner infoInner = InputMethodTools::GetInstance().InputClientInfoToInner(clientInfo_);
    auto ret = proxy->UpdateListenEventFlag(infoInner, eventFlag);
    if (ret != ErrorCode::NO_ERROR && isOn) {
        clientInfo_.eventFlag = oldEventFlag;
    }
    return ret;
}

void InputMethodController::SetControllerListener(std::shared_ptr<ControllerListener> controllerListener)
{
    IMSA_HILOGD("InputMethodController run in");
    controllerListener_ = std::move(controllerListener);
}

int32_t InputMethodController::Initialize()
{
    sptr<IInputClient> client = new (std::nothrow) InputClientServiceImpl();
    if (client == nullptr) {
        IMSA_HILOGE("failed to create client!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    sptr<IInputDataChannel> channel = new (std::nothrow) InputDataChannelServiceImpl();
    if (channel == nullptr) {
        IMSA_HILOGE("failed to new channel!");
        return ErrorCode::ERROR_NULL_POINTER;
    }

    SubscribeSaStart([this]() {
        this->RestoreListenInfoInSaDied();
        this->immersiveCache_.ResetQueried();
        }, INPUT_METHOD_SYSTEM_ABILITY_ID);

    InputAttribute attribute;
    attribute.inputPattern = InputAttribute::PATTERN_TEXT;
    clientInfo_.attribute = attribute;
    clientInfo_.client = client;
    clientInfo_.channel = channel->AsObject();

    // make AppExecFwk::EventHandler handler
    handler_ = std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner());
    return ErrorCode::NO_ERROR;
}

sptr<IInputMethodSystemAbility> InputMethodController::TryGetSystemAbilityProxy()
{
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    return GetSystemAbilityProxy(false);
#else
    return GetSystemAbilityProxy(true);
#endif
}

sptr<IInputMethodSystemAbility> InputMethodController::GetSystemAbilityProxy(bool ifRetry)
{
    std::lock_guard<std::mutex> 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<IRemoteObject> &remote) {
        OnRemoteSaDied(remote);
    });
    if ((systemAbility->IsProxyObject()) && (!systemAbility->AddDeathRecipient(deathRecipient_))) {
        IMSA_HILOGE("failed to add death recipient!");
        return nullptr;
    }
    abilityManager_ = iface_cast<IInputMethodSystemAbility>(systemAbility);
    return abilityManager_;
}
// LCOV_EXCL_START
void InputMethodController::RemoveDeathRecipient()
{
    std::lock_guard<std::mutex> lock(abilityLock_);
    if (abilityManager_ != nullptr && abilityManager_->AsObject() != nullptr && deathRecipient_ != nullptr) {
        abilityManager_->AsObject()->RemoveDeathRecipient(deathRecipient_);
    }
    deathRecipient_ = nullptr;
    abilityManager_ = nullptr;
}
// LCOV_EXCL_STOP
void InputMethodController::DeactivateClient()
{
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.state = ClientState::INACTIVE;
    }
    SendKeyboardStatus(KeyboardStatus::NONE);
}

void InputMethodController::CalibrateImmersiveParam(InputAttribute &inputAttribute, bool shouldOverrideImmersiveMode)
{
    // 0.Override immersiveMode to NONE if config requires it.
    if (shouldOverrideImmersiveMode) {
        IMSA_HILOGI("immersiveMode overridden to 0, original: %{public}d", inputAttribute.immersiveMode);
        inputAttribute.immersiveMode = 0;
    }
    // 1.The gradient mode and the fluid light mode can only be used when the immersive mode is enabled.
    if (inputAttribute.immersiveMode == 0) {
        if (inputAttribute.gradientMode != 0 || inputAttribute.fluidLightMode != 0) {
            IMSA_HILOGW("immersiveMode is NONE, but gradientMode=%{public}d, fluidLightMode=%{public}d",
                inputAttribute.gradientMode, inputAttribute.fluidLightMode);
            inputAttribute.gradientMode = 0;
            inputAttribute.fluidLightMode = 0;
        }
        return;
    }

    // 2.The fluid light mode can only be used when the gradient mode is enabled.
    if (inputAttribute.gradientMode == 0 && inputAttribute.fluidLightMode != 0) {
        IMSA_HILOGW("gradientMode is NONE, but fluidLightMode=%{public}d", inputAttribute.fluidLightMode);
        inputAttribute.fluidLightMode = 0;
        return;
    }
}

void InputMethodController::CalibrateInputPatternParam(InputAttribute &inputAttribute)
{
    if (inputAttribute.inputPattern != InputAttribute::PATTERN_ONE_TIME_CODE_NUMBER) {
        IMSA_HILOGD("no calibration required, inputPattern = %{public}d", inputAttribute.inputPattern);
        return;
    }
    inputAttribute.inputPattern = static_cast<int32_t>(TextInputType::NUMBER);
    inputAttribute.isOneTimeCodeNumberFlag = true;
    IMSA_HILOGW("isOneTimeCodeNumberFlag = %{public}d", inputAttribute.isOneTimeCodeNumberFlag);
}

bool InputMethodController::ShouldOverrideImmersiveMode(const TextConfig &textConfig)
{
    if (textConfig.inputAttribute.immersiveMode == 0) {
        return false;
    }
    if (IsSupportPcMode()) {
        return IsPcMode() ? IsDisablePcModeImmersiveMode() : IsDisableImmersiveMode();
    }
    return IsDisableImmersiveMode();
}

bool InputMethodController::IsDisableImmersiveMode()
{
    if (immersiveCache_.isDisableImmersiveModeQueried.load()) {
        return immersiveCache_.isDisableImmersiveModeCached.load();
    }
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        return false;
    }
    bool isDisable = false;
    auto ret = proxy->IsCapacitySupport(static_cast<int32_t>(CapacityType::DISABLE_IMMERSIVE_MODE), isDisable);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGD("IsCapacitySupport failed, ret: %{public}d", ret);
        return false;
    }
    IMSA_HILOGD("isDisableImmersiveMode: %{public}d", isDisable);
    immersiveCache_.isDisableImmersiveModeCached.store(isDisable);
    immersiveCache_.isDisableImmersiveModeQueried.store(true);
    return isDisable;
}

bool InputMethodController::IsSupportPcMode()
{
    if (immersiveCache_.isSupportPcModeQueried.load()) {
        return immersiveCache_.isSupportPcModeCached.load();
    }
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        return false;
    }
    bool isSupport = false;
    auto ret = proxy->IsCapacitySupport(static_cast<int32_t>(CapacityType::SUPPORT_PC_MODE), isSupport);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGD("IsCapacitySupport SUPPORT_PC_MODE failed, ret: %{public}d", ret);
        return false;
    }
    IMSA_HILOGD("isSupportPcMode: %{public}d", isSupport);
    immersiveCache_.isSupportPcModeCached.store(isSupport);
    immersiveCache_.isSupportPcModeQueried.store(true);
    return isSupport;
}

bool InputMethodController::IsDisablePcModeImmersiveMode()
{
    if (immersiveCache_.isDisablePcModeImmersiveModeQueried.load()) {
        return immersiveCache_.isDisablePcModeImmersiveModeCached.load();
    }
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        return false;
    }
    bool isDisable = false;
    auto ret = proxy->IsCapacitySupport(static_cast<int32_t>(CapacityType::DISABLE_PC_MODE_IMMERSIVE_MODE), isDisable);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGD("IsCapacitySupport DISABLE_PC_MODE_IMMERSIVE_MODE failed, ret: %{public}d", ret);
        return false;
    }
    IMSA_HILOGD("isDisablePcModeImmersiveMode: %{public}d", isDisable);
    immersiveCache_.isDisablePcModeImmersiveModeCached.store(isDisable);
    immersiveCache_.isDisablePcModeImmersiveModeQueried.store(true);
    return isDisable;
}

bool InputMethodController::IsPcMode()
{
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        return false;
    }
    bool isPcMode = false;
    auto ret = proxy->IsCapacitySupport(static_cast<int32_t>(CapacityType::IS_PC_MODE), isPcMode);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGD("IsCapacitySupport IS_PC_MODE failed, ret: %{public}d", ret);
        return false;
    }
    IMSA_HILOGD("isPcMode: %{public}d", isPcMode);
    return isPcMode;
}

void InputMethodController::SaveTextConfig(const TextConfig &textConfig)
{
    IMSA_HILOGD("textConfig: %{public}s.", textConfig.ToString().c_str());
    CursorInfo cursorInfo = textConfig.cursorInfo;
    uint32_t windowId = textConfig.windowId;
    GetWindowScaleCoordinate(windowId, cursorInfo);
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_ = textConfig;
        bool shouldOverride = ShouldOverrideImmersiveMode(textConfig);
        CalibrateImmersiveParam(textConfig_.inputAttribute, shouldOverride);
        CalibrateInputPatternParam(textConfig_.inputAttribute);
        textConfig_.cursorInfo = cursorInfo;
        StringUtils::TruncateUtf16String(textConfig_.inputAttribute.placeholder, MAX_PLACEHOLDER_SIZE);
        StringUtils::TruncateUtf16String(textConfig_.inputAttribute.abilityName, MAX_ABILITY_NAME_SIZE);
    }
    if (textConfig.range.start != INVALID_VALUE) {
        std::lock_guard<std::mutex> lock(editorContentLock_);
        selectOldBegin_ = selectNewBegin_;
        selectOldEnd_ = selectNewEnd_;
        selectNewBegin_ = textConfig.range.start;
        selectNewEnd_ = textConfig.range.end;
    }
}

int32_t InputMethodController::Attach(sptr<OnTextChangedListener> listener, ClientType type)
{
    return Attach(listener, true, type);
}

int32_t InputMethodController::Attach(sptr<OnTextChangedListener> listener, bool isShowKeyboard, ClientType type)
{
    InputAttribute attribute;
    attribute.inputPattern = InputAttribute::PATTERN_TEXT;
    return Attach(listener, isShowKeyboard, attribute, type);
}

int32_t InputMethodController::Attach(
    sptr<OnTextChangedListener> listener, bool isShowKeyboard, const InputAttribute &attribute, ClientType type)
{
    InputMethodSyncTrace tracer("InputMethodController Attach trace.");
    TextConfig textConfig;
    textConfig.inputAttribute = attribute;
    return Attach(listener, isShowKeyboard, textConfig, type);
}

InputMethodController::QueueGuard::QueueGuard(const std::string &name) : needPop_(true)
{
    CtrlEventInfo info = { std::chrono::steady_clock::now(), name };
    if (!ctrlEventQueue_.Push(info)) {
        needPop_ = false;
        IMSA_HILOGE("push %{public}s failed, queue size:%{public}zu", name.c_str(), ctrlEventQueue_.Size());
        return;
    }
    if (!ctrlEventQueue_.Wait(info)) {
        IMSA_HILOGE("%{public}s wait timeout, queue size:%{public}zu", name.c_str(), ctrlEventQueue_.Size());
    }
}
InputMethodController::QueueGuard::~QueueGuard()
{
    if (!needPop_) {
        return;
    }
    ctrlEventQueue_.Pop();
}

int32_t InputMethodController::GetClientType(ClientType &type)
{
    if (!IsBound()) {
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        type = clientInfo_.type;
    }
    IMSA_HILOGD("client type: %{public}u", static_cast<uint32_t>(type));
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::Attach(
    sptr<OnTextChangedListener> listener, bool isShowKeyboard, const TextConfig &textConfig, ClientType type)
{
    AttachOptions attachOptions;
    attachOptions.isShowKeyboard = isShowKeyboard;
    attachOptions.requestKeyboardReason = RequestKeyboardReason::NONE;
    return Attach(listener, attachOptions, textConfig, type);
}

int32_t InputMethodController::IsValidTextConfig(const TextConfig &textConfig)
{
    if (textConfig.inputAttribute.immersiveMode < static_cast<int32_t>(ImmersiveMode::NONE_IMMERSIVE) ||
        textConfig.inputAttribute.immersiveMode >= static_cast<int32_t>(ImmersiveMode::END)) {
        IMSA_HILOGE("invalid immersiveMode: %{public}d", textConfig.inputAttribute.immersiveMode);
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    if (textConfig.inputAttribute.gradientMode < static_cast<int32_t>(GradientMode::NONE) ||
        textConfig.inputAttribute.gradientMode >= static_cast<int32_t>(GradientMode::END)) {
        IMSA_HILOGW("invalid gradientMode: %{public}d", textConfig.inputAttribute.gradientMode);
    }
    if (textConfig.inputAttribute.fluidLightMode < static_cast<int32_t>(FluidLightMode::NONE) ||
        textConfig.inputAttribute.fluidLightMode >= static_cast<int32_t>(FluidLightMode::END)) {
        IMSA_HILOGW("invalid fluidLightMode: %{public}d", textConfig.inputAttribute.fluidLightMode);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::Attach(sptr<OnTextChangedListener> listener, const AttachOptions &attachOptions,
    const TextConfig &textConfig, ClientType type)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.attach", 1);
    HISTOGRAM_ENUMERATION("imekit.inputMethod.attach.request_keyboard_reason",
        static_cast<uint32_t>(attachOptions.requestKeyboardReason),
        static_cast<uint32_t>(RequestKeyboardReason::OTHER));
#endif
    auto task = [listener, attachOptions, textConfig, type, this](int32_t &ret) {
        ret = AttachExec(listener, attachOptions, textConfig, type);
        return ret != ErrorCode::ERROR_TRY_IME_START_FAILED;
    };
    int32_t ret = ErrorCode::NO_ERROR;
    if (BlockRetry(ATTACH_RETRY_INTERVAL, ATTACH_MAX_RETRY_TIMES, task, ret)) {
        return ret;
    }
    return ret;
}

int32_t InputMethodController::AttachExec(sptr<OnTextChangedListener> listener, const AttachOptions &attachOptions,
    const TextConfig &textConfig, ClientType type)
{
    QueueGuard guard(__func__);
    IMSA_HILOGI("isShowKeyboard %{public}d.", attachOptions.isShowKeyboard);
    InputMethodSyncTrace tracer("InputMethodController Attach with textConfig trace.");
    if (IsValidTextConfig(textConfig) != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("invalid textConfig.");
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    auto lastListener = GetTextListener();
    clientInfo_.isNotifyInputStart = lastListener != listener;
    if (clientInfo_.isNotifyInputStart) {
        sessionId_++;
    }
    IMSA_HILOGI("sessionId_ %{public}u", sessionId_.load());
    if (clientInfo_.isNotifyInputStart && lastListener != nullptr) {
        lastListener->OnDetachV2();
    }
    ClearEditorCache(clientInfo_.isNotifyInputStart, lastListener);
    SetTextListener(listener);
    SaveTextConfig(textConfig);
    GetTextConfig(clientInfo_.config);
    std::vector<sptr<IRemoteObject>> agents;
    std::vector<BindImeInfo> imeInfos;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = attachOptions.isShowKeyboard;
        clientInfo_.type = type;
        clientInfo_.config.requestKeyboardReason = attachOptions.requestKeyboardReason;
    }
    int32_t ret = StartInput(clientInfo_, agents, imeInfos);
    if (ret != ErrorCode::NO_ERROR) {
        auto evenInfo = HiSysOriginalInfo::Builder()
                            .SetInputPattern(textConfig.inputAttribute.inputPattern)
                            .SetIsShowKeyboard(attachOptions.isShowKeyboard)
                            .SetClientType(type).SetErrCode(ret).Build();
        ImcHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_ATTACH, *evenInfo);
        SetTextListener(nullptr);
        NotifyAttachFailure(ret);
        IMSA_HILOGI("bind imf failed: %{public}d.", ret);
        return ret;
    }
    clientInfo_.state = ClientState::ACTIVE;
    SetInputReady(agents, imeInfos);
    if (attachOptions.isShowKeyboard) {
        InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_SHOW_ATTACH);
    }
    IMSA_HILOGI("bind imf successfully.");
    return ErrorCode::NO_ERROR;
}

bool InputMethodController::IsKeyboardCallingProcess(int32_t pid, uint32_t windowId)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return false;
    }
    bool isKeyboardCallingProcess = false;
    auto ret = proxy->IsKeyboardCallingProcess(pid, windowId, isKeyboardCallingProcess);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("IsKeyboardCallingProcess failed: %{public}d.", ret);
        return false;
    }
    return isKeyboardCallingProcess;
}

int32_t InputMethodController::ShowTextInputInner(const AttachOptions &attachOptions, ClientType type)
{
    InputMethodSyncTrace tracer("IMC_ShowTextInput");
    if (!IsBound()) {
        IMSA_HILOGE("not bound!");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    IMSA_HILOGI("start.");
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = true;
    }
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_SHOW_ENEDITABLE);
    int32_t ret = ShowInput(clientInfo_.client, type, static_cast<int32_t>(attachOptions.requestKeyboardReason));
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to start input: %{public}d", ret);
        return ret;
    }
    isEditable_.store(true);
    IMSA_HILOGI("enter editable state.");
    return ret;
}

int32_t InputMethodController::HideTextInput()
{
    InputMethodSyncTrace tracer("IMC_HideTextInput");
    if (!IsBound()) {
        IMSA_HILOGE("not bound!");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    IMSA_HILOGI("start.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.hideTextInput", 1);
#endif
    isEditable_.store(false);
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_HIDE_UNEDITABLE);
    return HideInput(clientInfo_.client);
}

int32_t InputMethodController::HideCurrentInput()
{
    InputMethodSyncTrace tracer("IMC_HideCurrentInput");
    IMSA_HILOGD("InputMethodController::HideCurrentInput");
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = false;
    }
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_HIDE_NORMAL);
    uint32_t windowId = ImfCommonConst::INVALID_WINDOW_ID;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        windowId = clientInfo_.config.windowId;
    }
    return proxy->HideCurrentInputDeprecated(windowId);
}

int32_t InputMethodController::ShowCurrentInput()
{
    InputMethodSyncTrace tracer("IMC_ShowCurrentInput");
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGI("start.");
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = true;
    }
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_SHOW_NORMAL);
    uint32_t windowId = ImfCommonConst::INVALID_WINDOW_ID;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        windowId = clientInfo_.config.windowId;
    }
    return proxy->ShowCurrentInputDeprecated(windowId);
}

int32_t InputMethodController::Close()
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.detach", 1);
#endif
    int32_t defaultSessionId = -1;
    return Close(defaultSessionId);
}

int32_t InputMethodController::Close(int32_t clientSessionId)
{
    {
        QueueGuard guard(__func__);
    }
    if (IsBound()) {
        IMSA_HILOGI("start, clientSessionId: %{public}d.", clientSessionId);
    }

    auto listener = GetTextListener();
    if (listener != nullptr) {
        listener->OnDetachV2();
    }
    OperateIMEInfoCode infoCode = OperateIMEInfoCode::IME_UNBIND;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        if (clientInfo_.isShowKeyboard) {
            infoCode = OperateIMEInfoCode::IME_HIDE_UNBIND;
        }
    }
    InputMethodSyncTrace tracer("InputMethodController Close trace.");
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(infoCode);
    return ReleaseInput(clientInfo_.client, clientSessionId);
}

void InputMethodController::Reset()
{
    Close();
    RemoveDeathRecipient();
}

int32_t InputMethodController::RequestHideInput(uint32_t callingWndId, bool isFocusTriggered, uint64_t displayId,
    int32_t userId)
{
    IMSA_HILOGD("callingWndId/displayId/isFocusTriggered:%{public}u/%{public}" PRIu64 "/%{public}d.", callingWndId,
        displayId, isFocusTriggered);
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGD("InputMethodController start.");
    return proxy->RequestHideInput(callingWndId, displayId, isFocusTriggered, userId);
}

int32_t InputMethodController::DisplayOptionalInputMethod()
{
    IMSA_HILOGD("InputMethodController::DisplayOptionalInputMethod start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    return proxy->DisplayOptionalInputMethod();
}

bool InputMethodController::WasAttached()
{
    return isBound_.load();
}

int32_t InputMethodController::GetInputStartInfo(InputStartInfo &inputStartInfo)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return false;
    }
    return proxy->GetInputStartInfo(inputStartInfo);
}

std::shared_ptr<AppExecFwk::EventHandler> InputMethodController::GetMainHandler()
{
    return handler_;
}

int32_t InputMethodController::ListInputMethodCommon(InputMethodStatus status, std::vector<Property> &props,
    int32_t userId)
{
    IMSA_HILOGD("InputMethodController::ListInputMethodCommon start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    return proxy->ListInputMethod(status, props, userId);
}

int32_t InputMethodController::ListInputMethod(std::vector<Property> &props, int32_t userId)
{
    IMSA_HILOGD("InputMethodController::listInputMethod start.");
    return ListInputMethodCommon(ALL, props, userId);
}

int32_t InputMethodController::ListInputMethod(bool enable, std::vector<Property> &props, int32_t userId)
{
    IMSA_HILOGI("enable: %{public}s.", enable ? "ENABLE" : "DISABLE");
    return ListInputMethodCommon(enable ? ENABLE : DISABLE, props, userId);
}

int32_t InputMethodController::GetDefaultInputMethod(std::shared_ptr<Property> &property, int32_t userId)
{
    InputMethodSyncTrace tracer("IMC_GetDefaultInputMethod");
    IMSA_HILOGD("InputMethodController::GetDefaultInputMethod start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        proxy = nullptr;
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    Property prop;
    auto ret = proxy->GetDefaultInputMethod(prop, false, userId);
    if (ret != ErrorCode::NO_ERROR) {
        proxy = nullptr;
        return ret;
    }
    property = std::make_shared<Property>(prop);
    return ret;
}

int32_t InputMethodController::GetInputMethodConfig(OHOS::AppExecFwk::ElementName &inputMethodConfig, int32_t userId)
{
    InputMethodSyncTrace tracer("IMC_GetInputMethodConfig");
    IMSA_HILOGD("InputMethodController::inputMethodConfig start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    return proxy->GetInputMethodConfig(inputMethodConfig, userId);
}

std::shared_ptr<Property> InputMethodController::GetCurrentInputMethod(int32_t userId)
{
    InputMethodSyncTrace tracer("IMC_GetCurrentInputMethod");
    IMSA_HILOGD("InputMethodController::GetCurrentInputMethod start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return nullptr;
    }
    Property propertyData;
    proxy->GetCurrentInputMethod(userId, propertyData);
    auto property = std::make_shared<Property>(propertyData);
    return property;
}

int32_t InputMethodController::GetSoftKeyboardInfo(int32_t userId, BoundImeInfo &imeInfo)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    return proxy->GetSoftKeyboardInfo(userId, imeInfo);
}

std::shared_ptr<SubProperty> InputMethodController::GetCurrentInputMethodSubtype(int32_t userId)
{
    InputMethodSyncTrace tracer("IMC_GetCurrentInputMethodSubtype");
    IMSA_HILOGD("InputMethodController::GetCurrentInputMethodSubtype start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return nullptr;
    }
    SubProperty subPropertyData;
    proxy->GetCurrentInputMethodSubtype(subPropertyData, userId);
    auto subProperty = std::make_shared<SubProperty>(subPropertyData);
    return subProperty;
}

bool InputMethodController::IsDefaultImeSet(int32_t userId)
{
    IMSA_HILOGI("enter.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return false;
    }
    bool ret = false;
    proxy->IsDefaultImeSet(ret, userId);
    return ret;
}

int32_t InputMethodController::EnableIme(
    const std::string &bundleName, const std::string &extensionName, EnabledStatus status, int32_t userId)
{
    IMSA_HILOGI("enter.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.enableInputMethod", 1);
#endif
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return false;
    }
    return proxy->EnableIme(bundleName, extensionName, static_cast<int32_t>(status), userId);
}

int32_t InputMethodController::StartInput(
    InputClientInfo &inputClientInfo, std::vector<sptr<IRemoteObject>> &agents, std::vector<BindImeInfo> &imeInfos)
{
    IMSA_HILOGD("InputMethodController::StartInput start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    InputClientInfoInner inner = InputMethodTools::GetInstance().InputClientInfoToInner(inputClientInfo);
    int32_t ret = proxy->StartInput(inner, agents, imeInfos);
    return ret;
}
// LCOV_EXCL_START
int32_t InputMethodController::ReleaseInput(sptr<IInputClient> &client, int32_t clientSessionId)
{
    IMSA_HILOGD("InputMethodController::ReleaseInput start with clientSessionId: %{public}d.", clientSessionId);
    auto proxy = TryGetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    int32_t ret = proxy->ReleaseInput(client, sessionId_.load(), clientSessionId);
    if (ret == ErrorCode::NO_ERROR) {
        OnInputStop();
    }
    SetTextListener(nullptr);
    return ret;
}

int32_t InputMethodController::ShowInput(sptr<IInputClient> &client, ClientType type, int32_t requestKeyboardReason)
{
    IMSA_HILOGD("InputMethodController::ShowInput start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    uint32_t windowId = ImfCommonConst::INVALID_WINDOW_ID;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        windowId = clientInfo_.config.windowId;
    }
    return proxy->ShowInput(client, windowId, type, requestKeyboardReason);
}
// LCOV_EXCL_STOP
int32_t InputMethodController::HideInput(sptr<IInputClient> &client)
{
    IMSA_HILOGD("InputMethodController::HideInput start.");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    uint32_t windowId = ImfCommonConst::INVALID_WINDOW_ID;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        windowId = clientInfo_.config.windowId;
    }
    return proxy->HideInput(client, windowId);
}

void InputMethodController::OnRemoteSaDied(const wptr<IRemoteObject> &remote)
{
    IMSA_HILOGI("input method service death.");
    // imf sa died, current client callback inputStop
    InputStopInfo info;
    info.scene = InputStopScene::IMSA_DIED;
    ImeEventMonitorManagerImpl::GetInstance().OnInputStop(info);
    auto textListener = GetTextListener();
    if (textListener != nullptr && textConfig_.inputAttribute.isTextPreviewSupported) {
        IMSA_HILOGD("finish text preview.");
        textListener->FinishTextPreviewV2();
    }
    {
        std::lock_guard<std::mutex> lock(abilityLock_);
        abilityManager_ = nullptr;
    }
    ClearAgentInfo();
    if (handler_ == nullptr) {
        IMSA_HILOGE("handler_ is nullptr!");
        return;
    }
    RestoreClientInfoInSaDied();
}

void InputMethodController::RestoreListenInfoInSaDied()
{
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        if (clientInfo_.eventFlag == NO_EVENT_ON) {
            return;
        }
    }
    isDiedRestoreListen_.store(false);
    auto restoreListenTask = [=]() {
        if (isDiedRestoreListen_.load()) {
            return;
        }
        auto ret = RestoreListenEventFlag();
        if (ret == ErrorCode::NO_ERROR) {
            isDiedRestoreListen_.store(true);
            IMSA_HILOGI("try to RestoreListen success.");
        }
    };
    for (int i = 0; i < LOOP_COUNT; i++) {
        handler_->PostTask(
            restoreListenTask, "OnRemoteSaDied", DELAY_TIME * (i + 1), AppExecFwk::EventQueue::Priority::VIP);
    }
}

void InputMethodController::RestoreClientInfoInSaDied()
{
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return;
    }
    auto attach = [=]() -> bool {
        TextConfig tempConfig {};
        {
            std::lock_guard<std::mutex> lock(textConfigLock_);
            tempConfig = textConfig_;
            tempConfig.range.start = selectNewBegin_;
            tempConfig.range.end = selectNewEnd_;
        }
        {
            std::lock_guard<std::mutex> lock(cursorInfoMutex_);
            tempConfig.cursorInfo = cursorInfo_;
        }
        auto listener = GetTextListener();
        bool isShowKeyboard = false;
        {
            std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
            isShowKeyboard = clientInfo_.isShowKeyboard;
        }
        auto errCode = Attach(listener, isShowKeyboard, tempConfig);
        IMSA_HILOGI("attach end, errCode: %{public}d", errCode);
        return errCode == ErrorCode::NO_ERROR;
    };
    if (attach()) {
        return;
    }
    isDiedAttached_.store(false);
    auto attachTask = [this, attach]() {
        if (isDiedAttached_.load()) {
            return;
        }
        attach();
    };
    for (int i = 0; i < LOOP_COUNT; i++) {
        handler_->PostTask(attachTask, "OnRemoteSaDied", DELAY_TIME * (i + 1), AppExecFwk::EventQueue::Priority::VIP);
    }
}

int32_t InputMethodController::DiscardTypingText()
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.discardTypingText", 1);
#endif
    if (!IsBound()) {
        IMSA_HILOGE("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }

    return SendRequestToAllAgents([](std::shared_ptr<IInputMethodAgent> agent) -> int32_t {
        return agent->DiscardTypingText();
    });
}

int32_t InputMethodController::OnCursorUpdate(CursorInfo cursorInfo)
{
    if (!IsBound()) {
        IMSA_HILOGD("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    uint32_t windowId = 0;
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        windowId = textConfig_.windowId;
    }
    GetWindowScaleCoordinate(windowId, cursorInfo);
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_.cursorInfo = cursorInfo;
    }
    {
        std::lock_guard<std::mutex> lk(cursorInfoMutex_);
        if (cursorInfo_ == cursorInfo) {
            IMSA_HILOGD("same to last update.");
            return ErrorCode::NO_ERROR;
        }
        cursorInfo_ = cursorInfo;
    }
    IMSA_HILOGI("left: %{public}d, top: %{public}d, height: %{public}d.", static_cast<int32_t>(cursorInfo.left),
        static_cast<int32_t>(cursorInfo.top), static_cast<int32_t>(cursorInfo.height));
    return SendRequestToAllAgents([cursorInfo](std::shared_ptr<IInputMethodAgent> agent) -> int32_t {
        agent->OnCursorUpdate(cursorInfo.left, cursorInfo.top, cursorInfo.height);
        return ErrorCode::NO_ERROR;
    });
}

int32_t InputMethodController::OnSelectionChange(std::u16string text, int start, int end)
{
    if (!IsBound()) {
        IMSA_HILOGD("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_.range.start = start;
        textConfig_.range.end = end;
    }
    {
        std::lock_guard<std::mutex> lock(editorContentLock_);
        if (isTextNotified_.exchange(true) && textString_ == text && selectNewBegin_ == start && selectNewEnd_ == end) {
            IMSA_HILOGD("same to last update.");
            return ErrorCode::NO_ERROR;
        }
        textString_ = text;
        selectOldBegin_ = selectNewBegin_;
        selectOldEnd_ = selectNewEnd_;
        selectNewBegin_ = start;
        selectNewEnd_ = end;
    }
    IMSA_HILOGI("IMC size: %{public}zu, range: %{public}d/%{public}d/%{public}d/%{public}d.", text.size(),
        selectOldBegin_, selectOldEnd_, start, end);
    std::string testString = Str16ToStr8(text);
    return SendRequestToAllAgents(
        [testString, selectOldBegin = selectOldBegin_, selectOldEnd = selectOldEnd_, selectNewBegin = selectNewBegin_,
            selectNewEnd = selectNewEnd_](std::shared_ptr<IInputMethodAgent> agent) -> int32_t {
            agent->OnSelectionChange(testString, selectOldBegin, selectOldEnd, selectNewBegin, selectNewEnd);
            return ErrorCode::NO_ERROR;
        });
}

int32_t InputMethodController::OnConfigurationChange(Configuration info)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.updateAttribute", 1);
#endif
    if (!IsBound()) {
        IMSA_HILOGD("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    bool oldSecurityFlag = textConfig_.inputAttribute.GetSecurityFlag();
    InputAttribute attribute;
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_.inputAttribute.enterKeyType = static_cast<int32_t>(info.GetEnterKeyType());
        textConfig_.inputAttribute.inputPattern = static_cast<int32_t>(info.GetTextInputType());
        if (info.HasConsumeKeyEvents()) {
            textConfig_.inputAttribute.consumeKeyEvents = info.GetConsumeKeyEvents();
        }
        attribute = textConfig_.inputAttribute;
    }
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    IMSA_HILOGI(
        "IMC enterKeyType: %{public}d, textInputType: %{public}d, consumeKeyEvents: %{public}d.",
        attribute.enterKeyType, attribute.inputPattern, attribute.consumeKeyEvents);
    if (oldSecurityFlag != attribute.GetSecurityFlag()) {
        GetTextConfig(clientInfo_.config);
        std::vector<sptr<IRemoteObject>> agents;
        std::vector<BindImeInfo> imeInfos;
        int32_t ret = StartInput(clientInfo_, agents, imeInfos);
        if (ret != ErrorCode::NO_ERROR) {
            return ret;
        }
        SetInputReady(agents, imeInfos);
    }

    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    InputAttributeInner inner = InputMethodTools::GetInstance().AttributeToInner(attribute);
    agent->OnAttributeChange(inner);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetLeft(int32_t length, std::u16string &text)
{
    InputMethodSyncTrace tracer("IMC_GetForward");
    IMSA_HILOGD("start, length: %{public}d", length);
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or listener is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_GET_TEXT_BEFORE_CURSOR),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    int64_t start = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    {
        InputMethodSyncTrace aceTracer("ACE_GetForward");
        text = listener->GetLeftTextOfCursorV2(length);
    }
    PrintLogIfAceTimeout(start);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetRight(int32_t length, std::u16string &text)
{
    IMSA_HILOGD("start, length: %{public}d", length);
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener_ is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_GET_TEXT_AFTER_CURSOR),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    text = listener->GetRightTextOfCursorV2(length);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetTextIndexAtCursor(int32_t &index)
{
    IMSA_HILOGD("start.");
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener_ is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_GET_TEXT_INDEX_AT_CURSOR),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    index = listener->GetTextIndexAtCursorV2();
    return ErrorCode::NO_ERROR;
}

void InputMethodController::PrintKeyEventLog()
{
    std::lock_guard<std::mutex> lock(logLock_);
    auto now = system_clock::now();
    if (keyEventCountInPeriod_ == 0) {
        startLogTime_ = now;
    }
    keyEventCountInPeriod_++;
    if (std::chrono::duration_cast<seconds>(now - startLogTime_).count() >= LOG_MAX_TIME) {
        auto start = std::chrono::duration_cast<seconds>(startLogTime_.time_since_epoch()).count();
        auto end = std::chrono::duration_cast<seconds>(now.time_since_epoch()).count();
        IMSA_HILOGI("KeyEventCountInPeriod: %{public}d, startTime: %{public}lld, endTime: %{public}lld",
            keyEventCountInPeriod_, start, end);
        keyEventCountInPeriod_ = 0;
    }
}

int32_t InputMethodController::DispatchKeyEvent(std::shared_ptr<MMI::KeyEvent> keyEvent, KeyEventCallback callback)
{
    int64_t startTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    PrintKeyEventLog();
    KeyEventInfo keyEventInfo = { std::chrono::system_clock::now(), keyEvent };
    keyEventQueue_.Push(keyEventInfo);
    InputMethodSyncTrace tracer("DispatchKeyEvent trace");
    keyEventQueue_.Wait(keyEventInfo);
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        keyEventQueue_.Pop();
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    if (keyEvent == nullptr) {
        IMSA_HILOGE("keyEvent is nullptr!");
        keyEventQueue_.Pop();
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        keyEventQueue_.Pop();
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    IMSA_HILOGD("start.");
    sptr<IRemoteObject> channelObject = nullptr;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        channelObject = clientInfo_.channel;
    }
    if (channelObject == nullptr) {
        IMSA_HILOGE("channelObject is nullptr!");
        keyEventQueue_.Pop();
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    auto cbId = keyEventRetHandler_.AddKeyEventCbInfo({ keyEvent, callback });
    KeyEventValue keyEventValue;
    keyEventValue.event = keyEvent;
    auto ret = agent->DispatchKeyEvent(keyEventValue, cbId, channelObject);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to DispatchKeyEvent: %{public}d", ret);
        keyEventRetHandler_.RemoveKeyEventCbInfo(cbId);
    }
    keyEventQueue_.Pop();
    int64_t endTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (endTime - startTime > DISPATCH_KEYBOARD_TIME_OUT) {
        IMSA_HILOGW("DispatchKeyEvent timeout: [%{public}" PRId64 ", %{public}" PRId64 "].", startTime, endTime);
    }
    return ret;
}

void InputMethodController::HandleKeyEventResult(uint64_t cbId, bool consumeResult)
{
    keyEventRetHandler_.HandleKeyEventResult(cbId, consumeResult);
}

int32_t InputMethodController::GetEnterKeyType(int32_t &keyType)
{
    IMSA_HILOGD("InputMethodController::GetEnterKeyType start.");
    std::lock_guard<std::mutex> lock(textConfigLock_);
    keyType = textConfig_.inputAttribute.enterKeyType;
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetInputPattern(int32_t &inputpattern)
{
    IMSA_HILOGD("InputMethodController::GetInputPattern start.");
    std::lock_guard<std::mutex> lock(textConfigLock_);
    inputpattern = textConfig_.inputAttribute.inputPattern;
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetTextConfig(TextTotalConfig &config)
{
    std::lock_guard<std::mutex> lock(textConfigLock_);
    config.inputAttribute = textConfig_.inputAttribute;
    config.cursorInfo = textConfig_.cursorInfo;
    config.windowId = textConfig_.windowId;
    config.positionY = textConfig_.positionY;
    config.height = textConfig_.height;
    config.privateCommand = textConfig_.privateCommand;
    config.abilityToken = textConfig_.abilityToken;
    if (textConfig_.range.start == INVALID_VALUE) {
        IMSA_HILOGD("SelectionRange is invalid.");
    } else {
        {
            std::lock_guard<std::mutex> editorLock(editorContentLock_);
            config.textSelection.oldBegin = selectOldBegin_;
            config.textSelection.oldEnd = selectOldEnd_;
        }
        config.textSelection.newBegin = textConfig_.range.start;
        config.textSelection.newEnd = textConfig_.range.end;
    }
    IMSA_HILOGD("textConfig: %{public}s.", config.ToString().c_str());
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetCurrentCursorInfo(CursorInfo &cursorInfo)
{
    IMSA_HILOGD("InputMethodController::GetCurrentCursorInfo start.");
    std::lock_guard<std::mutex> lock(textConfigLock_);
    cursorInfo.left = textConfig_.cursorInfo.left;
    cursorInfo.top = textConfig_.cursorInfo.top;
    cursorInfo.width = textConfig_.cursorInfo.width;
    cursorInfo.height = textConfig_.cursorInfo.height;
    IMSA_HILOGD("GetCurrentCursorInfo: left=%{public}f, top=%{public}f, width=%{public}f, height=%{public}f.",
        cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::GetCursorInfo(CursorInfo &cursorInfo, int32_t userId)
{
    IMSA_HILOGD("InputMethodController::GetCursorInfo start, userId: %{public}d.", userId);
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    CursorInfoInner cursorInfoInner;
    int32_t ret = proxy->GetCursorInfo(userId, cursorInfoInner);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCursorInfo failed: %{public}d", ret);
        return ret;
    }
    cursorInfo = InputMethodTools::GetInstance().InnerToCursorInfo(cursorInfoInner);
    if (IsEditable()) {
        GetCurrentCursorInfo(cursorInfo);
    }
    CursorInfo invalidCursor = { -1.0, -1.0, -1.0, -1.0, 0 };
    if (cursorInfo == invalidCursor) {
        cursorInfo = { 0, 0, 0, 0, 0 };
    }
    IMSA_HILOGD(
        "cursorInfo:left=%{public}f,top=%{public}f,width=%{public}f,height=%{public}f,displayId=%{public}" PRIu64 "",
        cursorInfo.left, cursorInfo.top, cursorInfo.width, cursorInfo.height, cursorInfo.displayId);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::SetCallingWindow(uint32_t windowId)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.setCallingWindow", 1);
#endif
    if (!IsBound()) {
        IMSA_HILOGD("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_.windowId = windowId;
    }
    auto ret = SetCallingWindowByIMSA(windowId);
    if (ret == ErrorCode::NO_ERROR) {
        return ret;
    }
    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    IMSA_HILOGI("windowId: %{public}d.", windowId);
    agent->SetCallingWindow(windowId);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::SetCallingWindowByIMSA(uint32_t windowId)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    auto ret = proxy->SetCallingWindow(windowId, clientInfo_.client);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed:%{public}d!", ret);
        return ret;
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::ShowSoftKeyboardInner(ClientType type)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    IMSA_HILOGI("clientType:%{public}d.", type);
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = true;
    }
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_SHOW_NORMAL);
    return proxy->ShowCurrentInput(type);
}

int32_t InputMethodController::ShowSoftKeyboardInner(uint64_t displayId, ClientType type)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    IMSA_HILOGI("displayId/clientType:%{public}" PRIu64 "/%{public}d.", displayId, type);
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_SHOW_NORMAL);
    return proxy->ShowCurrentInput(displayId, type);
}

int32_t InputMethodController::HideSoftKeyboard()
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.hideSoftKeyboard", 1);
#endif
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGI("start.");
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = false;
    }
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_HIDE_NORMAL);
    return proxy->HideCurrentInput();
}

int32_t InputMethodController::HideSoftKeyboard(uint64_t displayId)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.hideSoftKeyboard", 1);
#endif
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGI("displayId:%{public}" PRIu64 ".", displayId);
    InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_HIDE_NORMAL);
    return proxy->HideCurrentInput(displayId);
}

int32_t InputMethodController::StopInputSession()
{
    IMSA_HILOGI("start.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.stopInputSession", 1);
#endif
    isEditable_.store(false);
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    uint32_t windowId = ImfCommonConst::INVALID_WINDOW_ID;
    if (IsBound()) {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        windowId = clientInfo_.config.windowId;
    }
    return proxy->StopInputSession(windowId);
}

int32_t InputMethodController::ShowOptionalInputMethod()
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGI("IMC start.");
    return proxy->DisplayOptionalInputMethod();
}

int32_t InputMethodController::ListInputMethodSubtype(const Property &property, std::vector<SubProperty> &subProps,
    int32_t userId)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGD("ime bundleName: %{public}s.", property.name.c_str());
    return proxy->ListInputMethodSubtype(property.name, subProps, userId);
}

int32_t InputMethodController::ListCurrentInputMethodSubtype(std::vector<SubProperty> &subProps, int32_t userId)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGD("start.");
    return proxy->ListCurrentInputMethodSubtype(subProps, userId);
}

int32_t InputMethodController::SwitchInputMethod(
    SwitchTrigger trigger, const std::string &name, const std::string &subName, int32_t userId)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.switchInputMethod", 1);
#endif
    InputMethodSyncTrace tracer("IMC_SwitchInputMethod");
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGI("name: %{public}s, subName: %{public}s, trigger: %{public}d.", name.c_str(), subName.c_str(),
        static_cast<uint32_t>(trigger));
    auto ret = proxy->SwitchInputMethod(name, subName, static_cast<uint32_t>(trigger), userId);
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    if (ret != ErrorCode::NO_ERROR) {
        int32_t errcode = ImfHiSysEventUtil::ConvertErrorCode(ret);
        HISTOGRAM_ENUMERATION("imekit.inputMethod.switchInputMethod.error_code", errcode, ErrorCode::ERROR_IMSA_END);
    }
#endif
    return ret;
}

int32_t InputMethodController::SetEDCDefaultInputMethod(const std::string &edcBackupImeName)
{
    if (edcBackupImeName.empty()) {
        IMSA_HILOGE("Invalid parameter: edcBackupImeName is empty");
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }

    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("Proxy is nullptr");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }

    return proxy->SetEDCDefaultInputMethod(edcBackupImeName);
}

int32_t InputMethodController::SetSimpleKeyboardEnabled(bool enable)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.setSimpleKeyboardEnabled", 1);
#endif
    InputMethodSyncTrace tracer("IMC_SetSimpleKeyboardEnabled");
    std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
    clientInfo_.config.isSimpleKeyboardEnabled = enable;
    return ErrorCode::NO_ERROR;
}

void InputMethodController::OnInputReady(sptr<IRemoteObject> agentObject, const BindImeInfo &imeInfo)
{
    IMSA_HILOGD("InputMethodController start.");
    if (imeInfo.bundleName != IME_MIRROR_NAME) {
        SetBindImeInfo(std::make_pair(imeInfo.pid, imeInfo.bundleName));
        isBound_.store(true);
        isEditable_.store(true);
    } else {
        IMSA_HILOGD("[ImeMirrorTag] proxyIme_IME_MIRROR no need to set bindImeInfo");
    }

    if (agentObject == nullptr) {
        IMSA_HILOGE("agentObject is nullptr!");
        return;
    }
    SetAgent(agentObject, imeInfo.bundleName);
}
// LCOV_EXCL_START
void InputMethodController::SetInputReady(
    const std::vector<sptr<IRemoteObject>> &agentObjects, const std::vector<BindImeInfo> &imeInfos)
{
    if (agentObjects.size() != imeInfos.size()) {
        IMSA_HILOGE("[ImeMirrorTag]agentObjects.size() != imeInfos.size()!");
        return;
    }
    for (size_t i = 0; i < agentObjects.size(); i++) {
        OnInputReady(agentObjects[i], imeInfos[i]);
    }
}

void InputMethodController::OnTmpInputStop(const sptr<IRemoteObject> &proxy)
{
    ClearAgentInfo();
    bool isNewEditor = false;
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        isNewEditor = clientInfo_.isNotifyInputStart;
    }
    auto listener = GetTextListener();
    if (!isNewEditor && listener != nullptr) {
        IMSA_HILOGD("listener is not nullptr!");
        if (textConfig_.inputAttribute.isTextPreviewSupported) {
            IMSA_HILOGD("finish text preview.");
            listener->FinishTextPreviewV2();
        }
    }
    isBound_.store(false);
    isEditable_.store(false);
    isTextNotified_.store(false);
    keyEventRetHandler_.ClearKeyEventCbInfo();
    if (proxy == nullptr) {
        IMSA_HILOGD("proxy is nullptr!");
        return;
    }
    auto channelProxy = std::make_shared<OnInputStopNotifyProxy>(proxy);
    channelProxy->NotifyOnInputStopFinished();
}

// LCOV_EXCL_STOP
void InputMethodController::OnInputStop(bool isStopInactiveClient, const sptr<IRemoteObject> &proxy,
    bool isSendKeyboardStatus, bool isStopByMultiPreemptInProc)
{
    if (isStopByMultiPreemptInProc) {
        OnTmpInputStop(proxy);
        return;
    }
    ClearAgentInfo();
    auto listener = GetTextListener();
    if (listener != nullptr) {
        IMSA_HILOGD("listener is not nullptr!");
        if (textConfig_.inputAttribute.isTextPreviewSupported) {
            IMSA_HILOGD("finish text preview.");
            listener->FinishTextPreviewV2();
        }
        if (isSendKeyboardStatus && (!isStopInactiveClient || !listener->IsFromTs())) {
            listener->SendKeyboardStatusV2(KeyboardStatus::HIDE);
        }
    }
    isBound_.store(false);
    isEditable_.store(false);
    isTextNotified_.store(false);
    keyEventRetHandler_.ClearKeyEventCbInfo();
    {
        std::lock_guard<std::mutex> lock(editorContentLock_);
        textString_ = Str8ToStr16("");
        selectOldBegin_ = INVALID_VALUE;
        selectOldEnd_ = INVALID_VALUE;
        selectNewBegin_ = INVALID_VALUE;
        selectNewEnd_ = INVALID_VALUE;
    }
    auto now = steady_clock::now();
    if (std::chrono::duration_cast<seconds>(now - textChangeStartLogTime_).count() > LOG_INSERT_MIN_TIME &&
        std::chrono::duration_cast<seconds>(now - textChangeStartLogTime_).count() < LOG_INSERT_MAX_TIME) {
        IMSA_HILOGW("unbind before insertText PrintTextChangeLogCount: %{public}d !", textChangeCountInPeriod_);
    }
    if (proxy == nullptr) {
        IMSA_HILOGD("proxy is nullptr!");
        return;
    }
    auto channelProxy = std::make_shared<OnInputStopNotifyProxy>(proxy);
    channelProxy->NotifyOnInputStopFinished();
}

void InputMethodController::OnImeMirrorStop(sptr<IRemoteObject> object)
{
    std::lock_guard guard(agentLock_);
    auto pos = find_if(agentInfoList_.begin(), agentInfoList_.end(), [object](const AgentInfo &agentInfo) {
        return agentInfo.agentObject == object;
    });
    if (pos == agentInfoList_.end()) {
        IMSA_HILOGE("[ImeMirrorTag] object is not in agentInfoList.");
        return;
    }
    pos->agentObject = nullptr;
    pos->agent = nullptr;
    agentInfoList_.erase(pos);
    IMSA_HILOGI("[ImeMirrorTag]remove agent");
}

void InputMethodController::ClearEditorCache(bool isNewEditor, sptr<OnTextChangedListener> lastListener)
{
    IMSA_HILOGD("isNewEditor: %{public}d.", isNewEditor);
    bool isTextPreviewSupported = false;
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        isTextPreviewSupported = textConfig_.inputAttribute.isTextPreviewSupported;
    }
    if (isNewEditor && isBound_.load() && lastListener != nullptr && isTextPreviewSupported) {
        IMSA_HILOGD("last editor FinishTextPreview");
        lastListener->FinishTextPreviewV2();
    }
    {
        std::lock_guard<std::mutex> lock(editorContentLock_);
        // reset old range when editor changes or first attach
        if (isNewEditor || !isBound_.load()) {
            isTextNotified_.store(false);
            textString_ = Str8ToStr16("");
            selectOldBegin_ = INVALID_VALUE;
            selectOldEnd_ = INVALID_VALUE;
            selectNewBegin_ = INVALID_VALUE;
            selectNewEnd_ = INVALID_VALUE;
        }
    }
    {
        std::lock_guard<std::mutex> lock(textConfigLock_);
        textConfig_ = {};
    }
    {
        std::lock_guard<std::mutex> lock(cursorInfoMutex_);
        cursorInfo_ = {};
    }
    {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        auto isSimpleKeyboardEnabled = clientInfo_.config.isSimpleKeyboardEnabled;
        clientInfo_.config = {};
        clientInfo_.config.isSimpleKeyboardEnabled = isSimpleKeyboardEnabled; // global scope
    }
}

void InputMethodController::SelectByRange(int32_t start, int32_t end)
{
    IMSA_HILOGD("InputMethodController start: %{public}d, end: %{public}d.", start, end);
    auto listener = GetTextListener();
    if (IsEditable() && listener != nullptr) {
        listener->HandleSetSelectionV2(start, end);
    } else {
        IMSA_HILOGE("not editable or textListener_ is nullptr!");
    }

    if (controllerListener_ != nullptr) {
        controllerListener_->OnSelectByRange(start, end);
    } else {
        IMSA_HILOGE("controllerListener_ is nullptr!");
    }
}

void InputMethodController::SelectByMovement(int32_t direction, int32_t cursorMoveSkip)
{
    IMSA_HILOGD(
        "InputMethodController start, direction: %{public}d, cursorMoveSkip: %{public}d", direction, cursorMoveSkip);
    auto listener = GetTextListener();
    if (IsEditable() && listener != nullptr) {
        listener->HandleSelectV2(CURSOR_DIRECTION_BASE_VALUE + direction, cursorMoveSkip);
    } else {
        IMSA_HILOGE("not editable or textListener_ is nullptr!");
    }

    if (controllerListener_ != nullptr) {
        controllerListener_->OnSelectByMovement(direction);
    } else {
        IMSA_HILOGE("controllerListener_ is nullptr!");
    }
}

int32_t InputMethodController::HandleExtendAction(int32_t action)
{
    IMSA_HILOGD("InputMethodController start, action: %{public}d.", action);
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener is nullptr!");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    listener->HandleExtendActionV2(action);
    return ErrorCode::NO_ERROR;
}

sptr<OnTextChangedListener> InputMethodController::GetTextListener()
{
    std::lock_guard<std::mutex> lock(textListenerLock_);
    return textListener_;
}

void InputMethodController::SetTextListener(sptr<OnTextChangedListener> listener)
{
    std::lock_guard<std::mutex> lock(textListenerLock_);
    textListener_ = listener;
}

bool InputMethodController::IsEditable()
{
    std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
    if (clientInfo_.state != ClientState::ACTIVE) {
        IMSA_HILOGD("client is not active.");
        return false;
    }
    if (!isEditable_.load()) {
        IMSA_HILOGD("not in editable state.");
        return false;
    }
    return true;
}

bool InputMethodController::IsBound()
{
    std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
    if (clientInfo_.state != ClientState::ACTIVE) {
        IMSA_HILOGD("client is not active.");
        return false;
    }
    if (!isBound_.load()) {
        IMSA_HILOGD("not bound.");
        return false;
    }
    return true;
}

int32_t InputMethodController::InsertText(const std::u16string &text)
{
    InputMethodSyncTrace tracer("IMC_InsertText");
    IMSA_HILOGD("start.");
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener is nullptr!");
        ReportBaseTextOperation(
            static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_INSERT_TEXT), ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    int64_t start = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    {
        InputMethodSyncTrace aceTracer("ACE_InsertText");
        IMSA_HILOGD("ACE InsertText.");
        listener->InsertTextV2(text);
    }
    PrintTextChangeLog();
    PrintLogIfAceTimeout(start);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::DeleteForward(int32_t length)
{
    InputMethodSyncTrace tracer("IMC_DeleteForward");
    IMSA_HILOGD("start, length: %{public}d.", length);
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_DELETE_FORWARD),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    int64_t start = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    {
        InputMethodSyncTrace aceTracer("ACE_DeleteForward");
        // reverse for compatibility
        listener->DeleteBackwardV2(length);
    }
    PrintLogIfAceTimeout(start);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::DeleteBackward(int32_t length)
{
    IMSA_HILOGD("InputMethodController start, length: %{public}d.", length);
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_DELETE_BACKWARD),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    // reverse for compatibility
    listener->DeleteForwardV2(length);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::MoveCursor(Direction direction)
{
    IMSA_HILOGD("InputMethodController start, direction: %{public}d.", static_cast<int32_t>(direction));
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or textListener_ is nullptr!");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    listener->MoveCursorV2(direction);
    return ErrorCode::NO_ERROR;
}

void InputMethodController::SendKeyboardStatus(KeyboardStatus status)
{
    IMSA_HILOGD("InputMethodController status: %{public}d.", static_cast<int32_t>(status));
    auto listener = GetTextListener();
    if (listener == nullptr) {
        IMSA_HILOGE("listener is nullptr!");
        return;
    }
    listener->SendKeyboardStatusV2(status);
    if (status == KeyboardStatus::HIDE) {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = false;
    }
}

void InputMethodController::NotifyPanelStatusInfo(const PanelStatusInfo &info)
{
    IMSA_HILOGD("InputMethodController start, type: %{public}d, flag: %{public}d, visible: %{public}d, trigger: "
                "%{public}d, sessionId: %{public}u, clientSessionId: %{public}d.",
        static_cast<PanelType>(info.panelInfo.panelType), static_cast<PanelFlag>(info.panelInfo.panelFlag),
        info.visible, static_cast<Trigger>(info.trigger), info.sessionId, info.clientSessionId);
    auto listener = GetTextListener();
    if (listener == nullptr) {
        IMSA_HILOGE("listener is nullptr!");
        return;
    }
    if (info.panelInfo.panelType == PanelType::SOFT_KEYBOARD &&
            info.panelInfo.panelFlag != PanelFlag::FLG_CANDIDATE_COLUMN) {
        info.visible ? SendKeyboardStatus(KeyboardStatus::SHOW) : SendKeyboardStatus(KeyboardStatus::HIDE);
    }

    if (info.visible || info.sessionId == 0 || info.sessionId == sessionId_) {
        listener->NotifyPanelStatusInfoV2(info);
    }
    if (info.panelInfo.panelType == PanelType::SOFT_KEYBOARD &&
        info.panelInfo.panelFlag != PanelFlag::FLG_CANDIDATE_COLUMN && !info.visible) {
        std::lock_guard<std::recursive_mutex> lock(clientInfoLock_);
        clientInfo_.isShowKeyboard = false;
    }
}

void InputMethodController::NotifyKeyboardHeight(uint32_t height)
{
    IMSA_HILOGD("InputMethodController start, height: %{public}u.", height);
    auto listener = GetTextListener();
    if (listener == nullptr) {
        IMSA_HILOGE("listener is nullptr!");
        return;
    }
    listener->NotifyKeyboardHeightV2(height);
}

int32_t InputMethodController::SendFunctionKey(int32_t functionKey)
{
    IMSA_HILOGD("InputMethodController start, functionKey: %{public}d", static_cast<int32_t>(functionKey));
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or listener is nullptr!");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    FunctionKey funcKey;
    funcKey.SetEnterKeyType(static_cast<EnterKeyType>(functionKey));
    listener->SendFunctionKeyV2(funcKey);
    SendRequestToImeMirrorAgent([&functionKey](std::shared_ptr<IInputMethodAgent> agent) -> int32_t {
        return agent->OnFunctionKey(functionKey);
    });
    return ErrorCode::NO_ERROR;
}

bool InputMethodController::IsInputTypeSupported(InputType type)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGI("type: %{public}d.", static_cast<int32_t>(type));
    bool ret = false;
    proxy->IsInputTypeSupported(static_cast<int32_t>(type), ret);
    return ret;
}

bool InputMethodController::IsCurrentImeByPid(int32_t pid, int32_t userId)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return false;
    }
    bool ret = false;
    proxy->IsCurrentImeByPid(pid, ret, userId);
    return ret;
}

int32_t InputMethodController::StartInputType(InputType type, bool isPersistence)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGI("type: %{public}d, isPersistence: %{public}d.", static_cast<int32_t>(type), isPersistence);
    return proxy->StartInputType(static_cast<int32_t>(type), isPersistence);
}

int32_t InputMethodController::StartInputTypeAsync(InputType type, bool isPersistence)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGI("type: %{public}d, isPersistence: %{public}d.", static_cast<int32_t>(type), isPersistence);
    return proxy->StartInputTypeAsync(static_cast<int32_t>(type), isPersistence);
}

int32_t InputMethodController::IsPanelShown(const PanelInfo &panelInfo, bool &isShown)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGD("type: %{public}d, flag: %{public}d.", static_cast<int32_t>(panelInfo.panelType),
        static_cast<int32_t>(panelInfo.panelFlag));
    return proxy->IsPanelShown(panelInfo, isShown);
}

int32_t InputMethodController::IsPanelShown(uint64_t displayId, const PanelInfo &panelInfo, bool &isShown)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGD("displayId/type/flag:%{public}" PRIu64 "/%{public}d/%{public}d.", displayId,
                static_cast<int32_t>(panelInfo.panelType), static_cast<int32_t>(panelInfo.panelFlag));
    return proxy->IsPanelShown(displayId, panelInfo, isShown);
}

void InputMethodController::SetAgent(const sptr<IRemoteObject> &agentObject, const std::string &bundleName)
{
    std::lock_guard<std::mutex> autoLock(agentLock_);
    for (auto &agentInfo : agentInfoList_) {
        if (agentInfo.agent != nullptr && agentInfo.agentObject.GetRefPtr() == agentObject.GetRefPtr()) {
            IMSA_HILOGD("[ImeMirrorTag]agent has already been set.");
            return;
        }
    }

    if (agentInfoList_.size() >= MAX_AGENT_NUMBER) {
        IMSA_HILOGW("[ImeMirrorTag]agent num is too many");
    }
    AgentInfo agentInfo;
    agentInfo.agent = std::make_shared<InputMethodAgentProxy>(agentObject);
    agentInfo.agentObject = agentObject;
    agentInfo.imeType = (bundleName == IME_MIRROR_NAME) ? ImeType::IME_MIRROR : ImeType::NONE;
    agentInfoList_.push_back(agentInfo);
}

std::shared_ptr<IInputMethodAgent> InputMethodController::GetAgent()
{
    std::lock_guard<std::mutex> autoLock(agentLock_);
    for (auto &agentInfo : agentInfoList_) {
        if (agentInfo.agent != nullptr && agentInfo.imeType != ImeType::IME_MIRROR) {
            return agentInfo.agent;
        }
    }
    IMSA_HILOGE("no agent found!");
    return nullptr;
}

void InputMethodController::PrintLogIfAceTimeout(int64_t start)
{
    int64_t end = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (end < start || start < 0) {
        return;
    }
    if (end - start > ACE_DEAL_TIME_OUT) {
        IMSA_HILOGW("timeout: [%{public}" PRId64 ", %{public}" PRId64 "].", start, end);
    }
}

void InputMethodController::PrintTextChangeLog()
{
    std::lock_guard<std::mutex> lock(printTextChangeMutex_);
    auto now = steady_clock::now();
    if (textChangeCountInPeriod_ == 0) {
        textChangeStartLogTime_ = now;
    }
    textChangeCountInPeriod_++;
    if (std::chrono::duration_cast<seconds>(now - textChangeStartLogTime_).count() >= LOG_INSERT_MAX_TIME) {
        IMSA_HILOGW("PrintTextChangeLogCount: %{public}d, ACE_InsertText success!", textChangeCountInPeriod_);
        textChangeCountInPeriod_ = 0;
    }
}

int32_t InputMethodController::ReceivePrivateCommand(
    const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    auto listener = GetTextListener();
    if (listener == nullptr) {
        IMSA_HILOGE("listener is nullptr!");
        return ErrorCode::ERROR_EX_NULL_POINTER;
    }
    IMSA_HILOGD("IMC in.");
    auto ret = listener->ReceivePrivateCommandV2(privateCommand);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("ReceivePrivateCommand err, ret: %{public}d!", ret);
        return ErrorCode::ERROR_TEXT_LISTENER_ERROR;
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::SendPrivateCommand(
    const std::unordered_map<std::string, PrivateDataValue> &privateCommand, bool validateDefaultIme)
{
    if (!IsBound()) {
        IMSA_HILOGD("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGD("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    if (!TextConfig::IsPrivateCommandValid(privateCommand)) {
        IMSA_HILOGE("invalid private command size!");
        return ErrorCode::ERROR_INVALID_PRIVATE_COMMAND_SIZE;
    }
    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    IMSA_HILOGD("IMC start.");
    Value value(privateCommand);
    return agent->SendPrivateCommand(value);
}

void InputMethodController::SetBindImeInfo(const std::pair<int64_t, std::string> &imeInfo)
{
    std::lock_guard<std::mutex> lock(bindImeInfoLock_);
    bindImeInfo_ = imeInfo;
}
// LCOV_EXCL_START
std::pair<int64_t, std::string> InputMethodController::GetBindImeInfo()
{
    std::lock_guard<std::mutex> lock(bindImeInfoLock_);
    return bindImeInfo_;
}

int32_t InputMethodController::SetPreviewTextInner(const std::string &text, const Range &range)
{
    InputMethodSyncTrace tracer("IMC_SetPreviewText");
    IMSA_HILOGD("IMC start.");
    if (!textConfig_.inputAttribute.isTextPreviewSupported) {
        IMSA_HILOGE("text preview do not supported!");
        return ErrorCode::ERROR_TEXT_PREVIEW_NOT_SUPPORTED;
    }
    auto listener = GetTextListener();
    if (!IsEditable() || listener == nullptr) {
        IMSA_HILOGE("not editable or listener is nullptr!");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    int32_t ret = 0;
    int64_t start = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    {
        InputMethodSyncTrace aceTracer("ACE_SetPreviewText");
        ret = listener->SetPreviewTextV2(Str8ToStr16(text), range);
    }
    PrintTextChangeLog();
    PrintLogIfAceTimeout(start);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to SetPreviewText: %{public}d!", ret);
        return ret == -1 ? ErrorCode::ERROR_INVALID_RANGE : ErrorCode::ERROR_TEXT_LISTENER_ERROR;
    }
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
int32_t InputMethodController::FinishTextPreview()
{
    InputMethodSyncTrace tracer("IMC_FinishTextPreview");
    IMSA_HILOGD("IMC start.");
    if (!textConfig_.inputAttribute.isTextPreviewSupported) {
        IMSA_HILOGD("text preview do not supported!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_FINISH_TEXT_PREVIEW),
            ErrorCode::ERROR_TEXT_PREVIEW_NOT_SUPPORTED);
        return ErrorCode::ERROR_TEXT_PREVIEW_NOT_SUPPORTED;
    }
    auto listener = GetTextListener();
    if (!isBound_.load() || listener == nullptr) {
        IMSA_HILOGW("not bound or listener is nullptr!");
        ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_FINISH_TEXT_PREVIEW),
            ErrorCode::ERROR_CLIENT_NOT_EDITABLE);
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    {
        InputMethodSyncTrace aceTracer("ACE_FinishTextPreview");
        listener->FinishTextPreviewV2();
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodController::SendMessage(const ArrayBuffer &arrayBuffer)
{
    if (!IsBound()) {
        IMSA_HILOGE("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGE("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    if (!ArrayBuffer::IsSizeValid(arrayBuffer)) {
        IMSA_HILOGE("arrayBuffer size is invalid!");
        return ErrorCode::ERROR_INVALID_ARRAY_BUFFER_SIZE;
    }
    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return agent->SendMessage(arrayBuffer);
}

int32_t InputMethodController::RecvMessage(const ArrayBuffer &arrayBuffer)
{
    if (!IsBound()) {
        IMSA_HILOGE("not bound.");
        return ErrorCode::ERROR_CLIENT_NOT_BOUND;
    }
    if (!IsEditable()) {
        IMSA_HILOGE("not editable.");
        return ErrorCode::ERROR_CLIENT_NOT_EDITABLE;
    }
    auto msgHandlerCallback = GetMsgHandlerCallback();
    if (msgHandlerCallback == nullptr) {
        IMSA_HILOGW("Message handler was not regist!");
        return ErrorCode::ERROR_MSG_HANDLER_NOT_REGIST;
    }
    return msgHandlerCallback->OnMessage(arrayBuffer);
}

int32_t InputMethodController::RegisterMsgHandler(const std::shared_ptr<MsgHandlerCallbackInterface> &msgHandler)
{
    IMSA_HILOGI("isRegist: %{public}d", msgHandler != nullptr);
    std::shared_ptr<MsgHandlerCallbackInterface> exMsgHandler = nullptr;
    {
        std::lock_guard<decltype(msgHandlerMutex_)> lock(msgHandlerMutex_);
        exMsgHandler = msgHandler_;
        msgHandler_ = msgHandler;
    }
    if (exMsgHandler != nullptr) {
        IMSA_HILOGI("Trigger exMessageHandler OnTerminated.");
        exMsgHandler->OnTerminated();
    }
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_START
std::shared_ptr<MsgHandlerCallbackInterface> InputMethodController::GetMsgHandlerCallback()
{
    std::lock_guard<decltype(msgHandlerMutex_)> lock(msgHandlerMutex_);
    return msgHandler_;
}
// LCOV_EXCL_STOP
int32_t InputMethodController::GetInputMethodState(EnabledStatus &state)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t stateData = 0;
    int32_t ret = proxy->GetInputMethodState(stateData);
    state = static_cast<EnabledStatus>(stateData);
    return ret;
}

int32_t InputMethodController::SetPreviewText(const std::string &text, const Range &range)
{
    auto ret = SetPreviewTextInner(text, range);
    ReportBaseTextOperation(static_cast<int32_t>(IInputDataChannelIpcCode::COMMAND_SET_PREVIEW_TEXT), ret);
    return ret;
}

int32_t InputMethodController::ShowTextInput(ClientType type)
{
    AttachOptions attachOptions;
    return ShowTextInput(attachOptions, type);
}

int32_t InputMethodController::ShowTextInput(const AttachOptions &attachOptions, ClientType type)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.showTextInput", 1);
    HISTOGRAM_ENUMERATION("imekit.inputMethod.attach.request_keyboard_reason",
        static_cast<uint32_t>(attachOptions.requestKeyboardReason),
        static_cast<uint32_t>(RequestKeyboardReason::OTHER));
#endif
    auto ret = ShowTextInputInner(attachOptions, type);
    ReportClientShow(static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_INPUT), ret, type);
    return ret;
}

int32_t InputMethodController::ShowSoftKeyboard(ClientType type)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.showSoftKeyboard", 1);
#endif
    auto ret = ShowSoftKeyboardInner(type);
    ReportClientShow(static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT), ret, type);
    return ret;
}

int32_t InputMethodController::ShowSoftKeyboard(uint64_t displayId, ClientType type)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethod.showSoftKeyboard", 1);
#endif
    auto ret = ShowSoftKeyboardInner(displayId, type);
    ReportClientShow(static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT), ret, type);
    return ret;
}
// LCOV_EXCL_START
void InputMethodController::ReportClientShow(int32_t eventCode, int32_t errCode, ClientType type)
{
    IMSA_HILOGD("HiSysEvent report start!");
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetClientType(type)
                        .SetEventCode(eventCode)
                        .SetErrCode(errCode)
                        .Build();
    ImcHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_SHOW, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", errCode);
}
// LCOV_EXCL_STOP
void InputMethodController::ReportBaseTextOperation(int32_t eventCode, int32_t errCode)
{
    IMSA_HILOGD("HiSysEvent report start!");
    auto imeInfo = GetBindImeInfo();
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetEventCode(eventCode)
                        .SetPeerName(imeInfo.second)
                        .SetPeerPid(imeInfo.first)
                        .SetClientType(clientInfo_.type)
                        .SetErrCode(errCode)
                        .Build();
    ImcHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::BASE_TEXT_OPERATOR, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", errCode);
}

void InputMethodController::UpdateTextPreviewState(bool isSupport)
{
    if (textConfig_.inputAttribute.isTextPreviewSupported == isSupport) {
        return;
    }
    textConfig_.inputAttribute.isTextPreviewSupported = isSupport;
    auto agent = GetAgent();
    if (agent == nullptr) {
        IMSA_HILOGE("agent is nullptr!");
        return;
    }
    InputAttributeInner inner = InputMethodTools::GetInstance().AttributeToInner(textConfig_.inputAttribute);
    agent->OnAttributeChange(inner);
}

int32_t InputMethodController::SendPrivateData(const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    auto proxy = GetSystemAbilityProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    Value value(privateCommand);
    return proxy->SendPrivateData(value);
}

int32_t InputMethodController::RegisterWindowScaleCallbackHandler(WindowScaleCallback &&callback)
{
    IMSA_HILOGD("isRegister: %{public}d", callback != nullptr);
    std::lock_guard<std::mutex> lock(windowScaleCallbackMutex_);
    windowScaleCallback_ = std::move(callback);
    return static_cast<int32_t>(ErrorCode::NO_ERROR);
}
// LCOV_EXCL_START
void InputMethodController::GetWindowScaleCoordinate(uint32_t windowId, CursorInfo &cursorInfo)
{
    WindowScaleCallback handler = nullptr;
    {
        std::lock_guard<std::mutex> lock(windowScaleCallbackMutex_);
        handler = windowScaleCallback_;
    }
    if (handler == nullptr) {
        IMSA_HILOGD("handler is nullptr");
        return;
    }
    handler(windowId, cursorInfo);
}

void InputMethodController::ClearAgentInfo()
{
    std::lock_guard guard(agentLock_);
    for (auto &agentInfo : agentInfoList_) {
        agentInfo.agent = nullptr;
        agentInfo.agentObject = nullptr;
    }

    IMSA_HILOGD("Clear all agent info");
    agentInfoList_.clear();
}
// LCOV_EXCL_STOP
int32_t InputMethodController::SendRequestToAllAgents(std::function<int32_t(std::shared_ptr<IInputMethodAgent>)> task)
{
    std::lock_guard guard(agentLock_);
    int32_t finalRet = ErrorCode::NO_ERROR;
    for (auto &agentInfo : agentInfoList_) {
        if (agentInfo.imeType == ImeType::IME_MIRROR && textConfig_.inputAttribute.IsSecurityImeFlag()) {
            IMSA_HILOGW("password not allow send to ime mirror");
            continue;
        }
        if (agentInfo.agent == nullptr) {
            if (agentInfo.imeType == ImeType::IME_MIRROR) {
                IMSA_HILOGW("ime mirror agent is null");
                continue;
            }
            IMSA_HILOGE("agent is null");
            return ErrorCode::ERROR_CLIENT_NULL_POINTER;
        }
        auto ret = task(agentInfo.agent);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("failed, ret = %{public}d", ret);
        }
        // Only update final return code for non-imeMirror IMEs
        // ImeMirror IME errors don't affect the overall result
        if (agentInfo.imeType != ImeType::IME_MIRROR) {
            finalRet = ret;
        }
    }
    IMSA_HILOGD("end, finalRet = %{public}d", finalRet);
    return finalRet;
}

int32_t InputMethodController::SendRequestToImeMirrorAgent(
    std::function<int32_t(std::shared_ptr<IInputMethodAgent>)> task)
{
    std::lock_guard guard(agentLock_);
    auto itr = std::find_if(agentInfoList_.begin(), agentInfoList_.end(), [](const AgentInfo &info) {
        return info.imeType == ImeType::IME_MIRROR;
    });
    if (itr == agentInfoList_.end()) {
        IMSA_HILOGD("ime mirror agent is not exist");
        return ErrorCode::ERROR_IME_NOT_FOUND;
    }

    if (textConfig_.inputAttribute.IsSecurityImeFlag()) {
        IMSA_HILOGE("text type is security, can not send function key");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }

    return task(itr->agent);
}

void InputMethodController::NotifyAttachFailure(int32_t errCode)
{
    auto listener = GetImcInnerListener();
    if (listener == nullptr) {
        return;
    }
    AttachFailureReason reason{ AttachFailureReason::SERVICE_ABNORMAL };
    auto iter = ATTACH_FAILURE_REASON_MAP.find(errCode);
    if (iter != ATTACH_FAILURE_REASON_MAP.end()) {
        reason = iter->second;
    }
    listener->OnAttachmentDidFail(reason);
}

void InputMethodController::SetImcInnerListener(const std::shared_ptr<ImcInnerListener> &imcInnerListener)
{
    std::lock_guard<std::mutex> lock(imcInnerListenerLock_);
    if (imcInnerListener_ != nullptr) {
        return;
    }
    imcInnerListener_ = imcInnerListener;
}

std::shared_ptr<ImcInnerListener> InputMethodController::GetImcInnerListener()
{
    std::lock_guard<std::mutex> lock(imcInnerListenerLock_);
    return imcInnerListener_;
}
// LCOV_EXCL_START
void OnTextChangedListener::InsertTextV2(const std::u16string &text)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        InsertText(text);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, text]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("InsertTextV2 listener is nullptr!");
            return;
        }
        listener->InsertText(text);
    };
    eventHandler->PostTask(task, "InsertTextV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::DeleteForwardV2(int32_t length)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        DeleteForward(length);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, length]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("DeleteForwardV2 listener is nullptr!");
            return;
        }
        listener->DeleteForward(length);
    };
    eventHandler->PostTask(task, "DeleteForwardV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::DeleteBackwardV2(int32_t length)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        DeleteBackward(length);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, length]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("DeleteBackwardV2 listener is nullptr!");
            return;
        }
        listener->DeleteBackward(length);
    };
    eventHandler->PostTask(task, "DeleteBackwardV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::SendKeyboardStatusV2(const KeyboardStatus &keyboardStatus)
{
    auto eventHandler = GetEventHandler();
    auto weakPtr = wptr<OnTextChangedListener>(this);
    if (eventHandler == nullptr) {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SendKeyboardStatusV2 listener is nullptr!");
            return;
        }
        SendKeyboardStatus(keyboardStatus);
        return;
    }
    auto task = [weakPtr, keyboardStatus]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SendKeyboardStatusV2 listener is nullptr!");
            return;
        }
        listener->SendKeyboardStatus(keyboardStatus);
    };
    eventHandler->PostTask(task, "SendKeyboardStatusV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::SendFunctionKeyV2(const FunctionKey &functionKey)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        SendFunctionKey(functionKey);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, functionKey]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SendFunctionKeyV2 listener is nullptr!");
            return;
        }
        listener->SendFunctionKey(functionKey);
    };
    eventHandler->PostTask(task, "SendFunctionKeyV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::MoveCursorV2(const Direction &direction)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        MoveCursor(direction);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, direction]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("MoveCursorV2 listener is nullptr!");
            return;
        }
        listener->MoveCursor(direction);
    };
    eventHandler->PostTask(task, "MoveCursorV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::HandleExtendActionV2(int32_t action)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        HandleExtendAction(action);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, action]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("HandleExtendActionV2 listener is nullptr!");
            return;
        }
        listener->HandleExtendAction(action);
    };
    eventHandler->PostTask(task, "HandleExtendActionV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

std::u16string OnTextChangedListener::GetLeftTextOfCursorV2(int32_t number)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        return GetLeftTextOfCursor(number);
    }
    auto textResultHandler = std::make_shared<BlockData<std::u16string>>(MAX_TIMEOUT);
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, textResultHandler, number]() {
        std::u16string text;
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("GetLeftTextOfCursorV2 listener is nullptr!");
        } else {
            text = listener->GetLeftTextOfCursor(number);
        }
        if (textResultHandler != nullptr) {
            textResultHandler->SetValue(text);
        }
    };
    eventHandler->PostTask(task, "GetLeftTextOfCursorV2", 0, AppExecFwk::EventQueue::Priority::VIP);
    std::u16string text;
    if (textResultHandler != nullptr && !textResultHandler->GetValue(text)) {
        IMSA_HILOGW("GetLeftTextOfCursorV2 timeout");
    }
    return text;
}

std::u16string OnTextChangedListener::GetRightTextOfCursorV2(int32_t number)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        return GetRightTextOfCursor(number);
    }
    auto textResultHandler = std::make_shared<BlockData<std::u16string>>(MAX_TIMEOUT);
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, textResultHandler, number]() {
        std::u16string text;
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("GetRightTextOfCursorV2 listener is nullptr!");
        } else {
            text = listener->GetRightTextOfCursor(number);
        }
        if (textResultHandler != nullptr) {
            textResultHandler->SetValue(text);
        }
    };
    eventHandler->PostTask(task, "GetRightTextOfCursorV2", 0, AppExecFwk::EventQueue::Priority::VIP);
    std::u16string text;
    if (textResultHandler != nullptr && !textResultHandler->GetValue(text)) {
        IMSA_HILOGW("GetRightTextOfCursorV2 timeout");
    }
    return text;
}

int32_t OnTextChangedListener::GetTextIndexAtCursorV2()
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        return GetTextIndexAtCursor();
    }
    auto textResultHandler = std::make_shared<BlockData<int32_t>>(MAX_TIMEOUT, -1);
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, textResultHandler]() {
        int32_t index = -1;
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("GetTextIndexAtCursorV2 listener is nullptr!");
        } else {
            index = listener->GetTextIndexAtCursor();
        }
        if (textResultHandler != nullptr) {
            textResultHandler->SetValue(index);
        }
    };
    eventHandler->PostTask(task, "GetTextIndexAtCursorV2", 0, AppExecFwk::EventQueue::Priority::VIP);
    int32_t index = -1;
    if (textResultHandler != nullptr && !textResultHandler->GetValue(index)) {
        IMSA_HILOGW("GetTextIndexAtCursorV2 timeout");
    }
    return index;
}

void OnTextChangedListener::SendKeyEventFromInputMethodV2(const KeyEvent &event)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        SendKeyEventFromInputMethod(event);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, event]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SendKeyEventFromInputMethodV2 listener is nullptr!");
            return;
        }
        listener->SendKeyEventFromInputMethod(event);
    };
    eventHandler->PostTask(task, "SendKeyEventFromInputMethodV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::NotifyPanelStatusInfoV2(const PanelStatusInfo &info)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        NotifyPanelStatusInfo(info);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, info]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("NotifyPanelStatusInfoV2 listener is nullptr!");
            return;
        }
        listener->NotifyPanelStatusInfo(info);
    };
    eventHandler->PostTask(task, "NotifyPanelStatusInfoV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::NotifyKeyboardHeightV2(uint32_t height)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        NotifyKeyboardHeight(height);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, height]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("NotifyKeyboardHeightV2 listener is nullptr!");
            return;
        }
        listener->NotifyKeyboardHeight(height);
    };
    eventHandler->PostTask(task, "NotifyKeyboardHeightV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::SetKeyboardStatusV2(bool status)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        SetKeyboardStatus(status);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, status]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SetKeyboardStatusV2 listener is nullptr!");
            return;
        }
        listener->SetKeyboardStatus(status);
    };
    eventHandler->PostTask(task, "SetKeyboardStatusV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::HandleSetSelectionV2(int32_t start, int32_t end)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        HandleSetSelection(start, end);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, start, end]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("HandleSetSelectionV2 listener is nullptr!");
            return;
        }
        listener->HandleSetSelection(start, end);
    };
    eventHandler->PostTask(task, "HandleSetSelectionV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::HandleSelectV2(int32_t keyCode, int32_t cursorMoveSkip)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        HandleSelect(keyCode, cursorMoveSkip);
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, keyCode, cursorMoveSkip]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("HandleSelectV2 listener is nullptr!");
            return;
        }
        listener->HandleSelect(keyCode, cursorMoveSkip);
    };
    eventHandler->PostTask(task, "HandleSelectV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

int32_t OnTextChangedListener::ReceivePrivateCommandV2(
    const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        return ReceivePrivateCommand(privateCommand);
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, privateCommand]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("ReceivePrivateCommandV2 listener is nullptr!");
            return;
        }
        listener->ReceivePrivateCommand(privateCommand);
    };
    eventHandler->PostTask(task, "ReceivePrivateCommandV2", 0, AppExecFwk::EventQueue::Priority::VIP);
    return ErrorCode::NO_ERROR;
}

int32_t OnTextChangedListener::SetPreviewTextV2(const std::u16string &text, const Range &range)
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        return SetPreviewText(text, range);
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr, text, range]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("SetPreviewTextV2 listener is nullptr!");
            return;
        }
        listener->SetPreviewText(text, range);
    };
    eventHandler->PostTask(task, "SetPreviewTextV2", 0, AppExecFwk::EventQueue::Priority::VIP);
    return ErrorCode::NO_ERROR;
}

void OnTextChangedListener::FinishTextPreviewV2()
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        FinishTextPreview();
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("FinishTextPreviewV2 listener is nullptr!");
            return;
        }
        listener->FinishTextPreview();
    };
    eventHandler->PostTask(task, "FinishTextPreviewV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

void OnTextChangedListener::OnDetachV2()
{
    auto eventHandler = GetEventHandler();
    if (eventHandler == nullptr) {
        OnDetach();
        return;
    }
    auto weakPtr = wptr<OnTextChangedListener>(this);
    auto task = [weakPtr]() {
        auto listener = weakPtr.promote();
        if (listener == nullptr) {
            IMSA_HILOGE("OnDetachV2 listener is nullptr!");
            return;
        }
        listener->OnDetach();
    };
    eventHandler->PostTask(task, "OnDetachV2", 0, AppExecFwk::EventQueue::Priority::VIP);
}

bool InputMethodController::SubscribeSaStart(std::function<void()> handler, int32_t saId)
{
    if (handler == nullptr) {
        IMSA_HILOGE("handler is nullptr");
        return false;
    }
    {
        std::lock_guard<std::mutex> lockGuard(listenerMapMutex_);
        if (saMgrListener_ != nullptr) {
            IMSA_HILOGW("saId:%{public}d is already registered", saId);
            return true;
        }
    }

    auto abilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (abilityManager == nullptr) {
        IMSA_HILOGE("abilityManager is nullptr, saId: %{public}d", saId);
        return false;
    }
    sptr<ISystemAbilityStatusChange> listener = new (std::nothrow) SaMgrListener([handler]() {
        if (handler != nullptr) {
            handler();
        }
    });
    if (listener == nullptr) {
        IMSA_HILOGE("failed to create listener, saId: %{public}d", saId);
        return false;
    }
    int32_t ret = abilityManager->SubscribeSystemAbility(saId, listener);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("subscribe system ability failed, ret: %{public}d, saId: %{public}d", ret, saId);
        return false;
    }
    {
        std::lock_guard<std::mutex> lockGuard(listenerMapMutex_);
        saMgrListener_ = listener;
    }
    IMSA_HILOGD("subscribe system ability success, saId: %{public}d", saId);
    return true;
}

void SaMgrListener::OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
{
    IMSA_HILOGD("systemAbilityId: %{public}d.", systemAbilityId);
    if (systemAbilityId != INPUT_METHOD_SYSTEM_ABILITY_ID) {
        return;
    }
    if (func_ != nullptr) {
        func_();
    }
}
// LCOV_EXCL_STOP
} // namespace MiscServices
} // namespace OHOS