/*
 * 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_ability.h"

#include <unistd.h>

#include <cinttypes>
#include <utility>

#include "global.h"
#include "ima_hisysevent_reporter.h"
#include "input_method_agent_service_impl.h"
#include "input_method_core_service_impl.h"
#include "input_method_system_ability_proxy.h"
#include "input_method_tools.h"
#include "input_method_utils.h"
#include "inputmethod_sysevent.h"
#include "inputmethod_trace.h"
#include "iservice_registry.h"
#include "itypes_util.h"
#include "message_parcel.h"
#include "on_demand_start_stop_sa.h"
#include "string_ex.h"
#include "sys/prctl.h"
#include "system_ability_definition.h"
#include "task_manager.h"
#include "tasks/task.h"
#include "tasks/task_imsa.h"
#include "variant_util.h"
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
#include "histogram_plugin_macros.h"
#endif

namespace OHOS {
namespace MiscServices {
using namespace MessageID;
using namespace std::chrono;
constexpr double INVALID_CURSOR_VALUE = -1.0;
constexpr int32_t INVALID_SELECTION_VALUE = -1;
constexpr uint32_t FIND_PANEL_RETRY_INTERVAL = 10;
constexpr uint32_t MAX_RETRY_TIMES = 100;
constexpr uint32_t START_INPUT_CALLBACK_TIMEOUT_MS = 1000;
constexpr uint32_t INVALID_SECURITY_MODE = -1;
constexpr uint32_t MAX_PRIVATE_COMMAND_QUEUE_SIZE = 10;

InputMethodAbility::InputMethodAbility()
{
    Initialize();
}

InputMethodAbility::~InputMethodAbility()
{
    IMSA_HILOGI("InputMethodAbility::~InputMethodAbility.");
}

InputMethodAbility &InputMethodAbility::GetInstance()
{
    static InputMethodAbility instance;
    return instance;
}

sptr<IInputMethodSystemAbility> InputMethodAbility::GetImsaProxy()
{
    std::lock_guard<std::mutex> lock(abilityLock_);
    if (abilityManager_ != nullptr) {
        return abilityManager_;
    }
    IMSA_HILOGI("InputMethodAbility get imsa proxy.");
    auto systemAbility = OnDemandStartStopSa::GetInputMethodSystemAbility();
    if (systemAbility == nullptr) {
        IMSA_HILOGE("systemAbility is nullptr!");
        return nullptr;
    }
    if (deathRecipient_ == nullptr) {
        deathRecipient_ = new (std::nothrow) InputDeathRecipient();
        if (deathRecipient_ == nullptr) {
            IMSA_HILOGE("failed to new death recipient!");
            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_;
}

int32_t InputMethodAbility::SetCoreAndAgent()
{
    IMSA_HILOGD("InputMethodAbility, start.");
    TaskManager::GetInstance().SetInited(true);

    if (isBound_.load()) {
        IMSA_HILOGD("already bound.");
        return ErrorCode::NO_ERROR;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t ret = proxy->SetCoreAndAgent(coreStub_, agentStub_->AsObject());
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("set failed, ret: %{public}d!", ret);
        return ret;
    }
    isBound_.store(true);
    IMSA_HILOGD("set successfully.");
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::InitConnect()
{
    IMSA_HILOGD("InputMethodAbility, init connect.");
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t ret = proxy->InitConnect();
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("set failed, ret: %{public}d!", ret);
        return ret;
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::RegisterProxyIme(uint64_t displayId)
{
    IMSA_HILOGD("IMA, displayId: %{public}" PRIu64 "", displayId);
    TaskManager::GetInstance().SetInited(true);

    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    if (agentStub_ == nullptr) {
        IMSA_HILOGE("agent nullptr");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t ret = proxy->RegisterProxyIme(displayId, coreStub_, agentStub_->AsObject());
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed, displayId: %{public}" PRIu64 ", ret: %{public}d!", displayId, ret);
        return ret;
    }
    isBound_.store(true);
    isProxyIme_.store(true);
    IMSA_HILOGD("set successfully, displayId: %{public}" PRIu64 "", displayId);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::UnregisterProxyIme(uint64_t displayId, UnRegisteredType type)
{
    isBound_.store(false);
    isProxyIme_.store(false);
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    return proxy->UnregisterProxyIme(displayId, static_cast<int32_t>(type));
}

int32_t InputMethodAbility::BindImeMirror()
{
    TaskManager::GetInstance().SetInited(true);

    if (isBound_.load()) {
        IMSA_HILOGD("[ImeMirrorTag]already bound.");
        return ErrorCode::NO_ERROR;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("[ImeMirrorTag]imsa proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    if (agentStub_ == nullptr) {
        IMSA_HILOGE("[ImeMirrorTag]agent nullptr");
        return ErrorCode::ERROR_NULL_POINTER;
    }

    auto ret = proxy->BindImeMirror(coreStub_, agentStub_->AsObject());
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("[ImeMirrorTag]failed, ret: %{public}d!", ret);
        return ret;
    }
    IMSA_HILOGD("[ImeMirrorTag]register imsa start event");
    imeMirrorMgr_.SubscribeSaStart(
        [this]() {
            if (!imeMirrorMgr_.IsImeMirrorEnable()) {
                IMSA_HILOGI("[ImeMirrorTag]imsa started, no need resume ime mirror");
                return;
            }
            IMSA_HILOGI("[ImeMirrorTag]imsa started, resume ime mirror");
            BindImeMirror();
        },
        INPUT_METHOD_SYSTEM_ABILITY_ID);
    isBound_.store(true);
    imeMirrorMgr_.SetImeMirrorEnable(true);
    IMSA_HILOGI("[ImeMirrorTag]set successfully");
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::UnbindImeMirror()
{
    isBound_.store(false);
    imeMirrorMgr_.SetImeMirrorEnable(false);
    imeMirrorMgr_.UnSubscribeSaStart(INPUT_METHOD_SYSTEM_ABILITY_ID);
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("[ImeMirrorTag]imsa proxy is nullptr!");
        return ErrorCode::ERROR_SERVICE_START_FAILED;
    }
    return proxy->UnbindImeMirror();
}

void InputMethodAbility::Initialize()
{
    IMSA_HILOGD("IMA init.");
    sptr<InputMethodCoreStub> coreStub = new (std::nothrow) InputMethodCoreServiceImpl();
    if (coreStub == nullptr) {
        IMSA_HILOGE("failed to create core!");
        return;
    }
    sptr<InputMethodAgentStub> agentStub = new (std::nothrow) InputMethodAgentServiceImpl();
    if (agentStub == nullptr) {
        IMSA_HILOGE("failed to create agent!");
        return;
    }
    agentStub_ = agentStub;
    coreStub_ = coreStub;
}

void InputMethodAbility::SetConfigurationUpdate(std::function<void(Rosen::DisplayId displayId)> configurationUpdate)
{
    configurationUpdate_ = configurationUpdate;
}

void InputMethodAbility::ConfigurationUpdate(Rosen::DisplayId displayId)
{
    if (configurationUpdate_ != nullptr) {
        configurationUpdate_(displayId);
    }
}

void InputMethodAbility::SetImeListener(std::shared_ptr<InputMethodEngineListener> imeListener)
{
    IMSA_HILOGD("InputMethodAbility start.");
    if (imeListener_ == nullptr) {
        imeListener_ = std::move(imeListener);
    }
}

std::shared_ptr<InputMethodEngineListener> InputMethodAbility::GetImeListener()
{
    return imeListener_;
}

void InputMethodAbility::SetKdListener(std::shared_ptr<KeyboardListener> kdListener)
{
    IMSA_HILOGD("InputMethodAbility start.");
    if (kdListener_ == nullptr) {
        kdListener_ = std::move(kdListener);
    }
}

void InputMethodAbility::SetTextInputClientListener(std::shared_ptr<TextInputClientListener> textInputClientListener)
{
    IMSA_HILOGD("InputMethodAbility start.");
    if (textInputClientListener_ == nullptr) {
        textInputClientListener_ = std::move(textInputClientListener);
    }
}

void InputMethodAbility::OnInitInputControlChannel(sptr<IRemoteObject> channelObj)
{
    IMSA_HILOGD("InputMethodAbility::OnInitInputControlChannel start.");
    SetInputControlChannel(channelObj);
}

int32_t InputMethodAbility::StartInputInner(const InputClientInfo &clientInfo, bool isBindFromClient)
{
    SetBindClientInfo(clientInfo);
    if (clientInfo.channel == nullptr) {
        IMSA_HILOGE("channelObject is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    IMSA_HILOGI("IMA showKeyboard:%{public}d,bindFromClient:%{public}d.", clientInfo.isShowKeyboard, isBindFromClient);
    SetInputDataChannel(clientInfo.channel);
    auto attribute = GetInputAttribute();
    ConfigurationUpdate(attribute.callingDisplayId);
    if ((clientInfo.needHide && !isProxyIme_.load()) ||
        IsDisplayChanged(attribute.callingDisplayId, clientInfo.config.inputAttribute.callingDisplayId)) {
        IMSA_HILOGD("pwd or normal input pattern changed, need hide panel first.");
        auto panel = GetSoftKeyboardPanel();
        if (panel != nullptr) {
            panel->HidePanel();
        }
    }
    int32_t ret = isBindFromClient
                      ? InvokeStartInputCallback(clientInfo.config, clientInfo.isNotifyInputStart)
                      : InvokeStartInputCallbackWithInfoRestruct(clientInfo.config, clientInfo.isNotifyInputStart);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to invoke callback, ret: %{public}d!", ret);
        return ret;
    }
    auto time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    auto showPanel = [&, needShow = clientInfo.isShowKeyboard, startTime = time] {
        auto endTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
        int32_t ret = ErrorCode::NO_ERROR;
        if (needShow) {
            ret = ShowKeyboardWithoutLock(cmdId_, InputStartScene::ATTACH);
        } else {
            NotifyInputStartToClients(InputStartScene::ATTACH, false);
        }
        ReportImeStartInput(
            static_cast<int32_t>(IInputMethodCoreIpcCode::COMMAND_START_INPUT), ret, needShow, endTime - startTime);
        isImeTerminating_.store(false);
    };
    uint64_t seqId = Task::GetNextSeqId();
    if (imeListener_ == nullptr || !imeListener_->PostTaskToEventHandler(
        [seqId] { TaskManager::GetInstance().Complete(seqId); }, "task_manager_complete")) {
        showPanel();
        return ErrorCode::NO_ERROR;
    }
    TaskManager::GetInstance().WaitExec(seqId, START_INPUT_CALLBACK_TIMEOUT_MS, showPanel);
    return ErrorCode::NO_ERROR;
}

bool InputMethodAbility::IsDisplayChanged(uint64_t oldDisplayId, uint64_t newDisplayId)
{
    if (oldDisplayId == newDisplayId) {
        IMSA_HILOGD("screen not changed!");
        return false;
    }
    IMSA_HILOGD("screen changed!");
    return true;
}

void InputMethodAbility::OnSetSubtype(SubProperty subProperty)
{
    if (imeListener_ != nullptr) {
        imeListener_->OnSetSubtype(subProperty);
    }
}

void InputMethodAbility::OnSetInputType(InputType inputType, uint64_t displayId)
{
    inputType_ = inputType;
    if (displayId == ImfCommonConst::DEFAULT_DISPLAY_ID) {
        NotifyPanelStatus(false);
    }
    IMSA_HILOGD("OnSetInputType, inputType = %{public}d, displayId = %{public}" PRIu64 "",
        static_cast<int32_t>(inputType), displayId);
}

InputType InputMethodAbility::GetInputType()
{
    return inputType_;
}

void InputMethodAbility::ClearDataChannel(const sptr<IRemoteObject> &channel)
{
    std::lock_guard<std::mutex> lock(dataChannelLock_);
    if (dataChannelObject_ == nullptr || channel == nullptr) {
        IMSA_HILOGD("dataChannelObject_ already nullptr.");
        return;
    }
    if (dataChannelObject_.GetRefPtr() == channel.GetRefPtr()) {
        if (dataChannelDeathRecipient_ != nullptr) {
            if (!dataChannelObject_->RemoveDeathRecipient(dataChannelDeathRecipient_)) {
                IMSA_HILOGE("RemoveDeathRecipient failed");
            }
            dataChannelDeathRecipient_ = nullptr;
        }
        dataChannelObject_ = nullptr;
        if (dataChannelProxyWrap_ != nullptr) {
            dataChannelProxyWrap_->ClearRspHandlers();
        }
        dataChannelProxyWrap_ = nullptr;
        IMSA_HILOGD("end.");
    }
}

int32_t InputMethodAbility::StopInput(
    sptr<IRemoteObject> channelObject, uint32_t sessionId, int32_t clientSessionId)
{
    std::lock_guard<std::recursive_mutex> lock(keyboardCmdLock_);
    int32_t cmdCount = ++cmdId_;
    IMSA_HILOGI("IMA, sessionId: %{public}u, clientSessionId: %{public}d", sessionId, clientSessionId);
    HideKeyboardImplWithoutLock(cmdCount, sessionId, clientSessionId);
    ClearBindInfo(channelObject);
    ClearInputType();
    if (imeListener_ != nullptr) {
        imeListener_->OnInputFinish();
    }
    return ErrorCode::NO_ERROR;
}

void InputMethodAbility::ClearBindInfo(const sptr<IRemoteObject> &channel)
{
    ClearDataChannel(channel);
    ClearInputAttribute();
    ClearAttachOptions();
    ClearBindClientInfo();
}

int32_t InputMethodAbility::DispatchKeyEvent(
    const std::shared_ptr<MMI::KeyEvent> &keyEvent, uint64_t cbId, const sptr<IRemoteObject> &channelObject)
{
    if (keyEvent == nullptr) {
        IMSA_HILOGE("keyEvent is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    if (kdListener_ == nullptr) {
        IMSA_HILOGE("kdListener_ is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    IMSA_HILOGD("InputMethodAbility, start.");

    if (!kdListener_->OnDealKeyEvent(keyEvent, cbId, channelObject)) {
        IMSA_HILOGE("keyEvent not deal!");
        return ErrorCode::ERROR_DISPATCH_KEY_EVENT;
    }
    return ErrorCode::NO_ERROR;
}

void InputMethodAbility::SetCallingWindow(uint32_t rawEditorWindowId, const FocusedInfo &focusedInfo)
{
    IMSA_HILOGD("InputMethodAbility rawEditorWindowId/focusedInfo: %{public}u/%{public}s.", rawEditorWindowId,
        focusedInfo.ToString().c_str());
    {
        std::lock_guard<std::mutex> lock(inputAttrLock_);
        inputAttribute_.windowId = focusedInfo.keyboardWindowId;
        inputAttribute_.editorWindowId = focusedInfo.windowId;
    }
    panels_.ForEach([keyboardWindowId = focusedInfo.keyboardWindowId](
                        const PanelType &panelType, const std::shared_ptr<InputMethodPanel> &panel) {
        panel->SetCallingWindow(keyboardWindowId);
        return false;
    });
    if (imeListener_ != nullptr) {
        imeListener_->OnSetCallingWindow(rawEditorWindowId);
    }
    NotifyInputStartToClients(InputStartScene::WINDOW_CHANGED);
    OnCallingDisplayIdChanged(focusedInfo.displayId, focusedInfo.keyboardDisplayId);
}

void InputMethodAbility::OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height)
{
    if (kdListener_ == nullptr) {
        IMSA_HILOGE("kdListener_ is nullptr!");
        return;
    }
    IMSA_HILOGD("IMA, x: %{public}d, y: %{public}d, height: %{public}d.", positionX, positionY, height);
    kdListener_->OnCursorUpdate(positionX, positionY, height);
}

void InputMethodAbility::OnSelectionChange(
    std::u16string text, int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd)
{
    if (kdListener_ == nullptr) {
        IMSA_HILOGE("kdListener_ is nullptr!");
        return;
    }
    kdListener_->OnTextChange(Str16ToStr8(text));
    kdListener_->OnSelectionChange(oldBegin, oldEnd, newBegin, newEnd);
}

void InputMethodAbility::OnAttributeChange(InputAttribute attribute)
{
    if (kdListener_ == nullptr) {
        IMSA_HILOGE("kdListener_ is nullptr!");
        return;
    }
    IMSA_HILOGD("enterKeyType: %{public}d, inputPattern: %{public}d, consumeKeyEvents: %{public}d.",
        attribute.enterKeyType, attribute.inputPattern, attribute.consumeKeyEvents);
    attribute.bundleName = GetInputAttribute().bundleName;
    attribute.windowId = GetInputAttribute().windowId;
    attribute.callingDisplayId = GetInputAttribute().callingDisplayId;
    SetInputAttribute(attribute);
    // add for mod inputPattern when panel show
    NotifyPanelStatus(false);
    kdListener_->OnEditorAttributeChange(attribute);
}

void InputMethodAbility::OnFunctionKey(int32_t funcKey)
{
    if (kdListener_ == nullptr) {
        IMSA_HILOGE("kdListener_ is nullptr!");
        return;
    }
    kdListener_->OnFunctionKey(funcKey);
}

int32_t InputMethodAbility::OnStopInputService(bool isTerminateIme)
{
    IMSA_HILOGI("isTerminateIme: %{public}d.", isTerminateIme);
    isBound_.store(false);
    auto imeListener = GetImeListener();
    if (imeListener == nullptr) {
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    if (isTerminateIme) {
        isImeTerminating_.store(true);
        return imeListener->OnInputStop();
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::OnDiscardTypingText()
{
    auto imeListener = GetImeListener();
    if (imeListener == nullptr) {
        IMSA_HILOGE("imeListener is nullptr!");
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    return imeListener_->OnDiscardTypingText();
}

int32_t InputMethodAbility::HideKeyboard()
{
    std::lock_guard<std::recursive_mutex> lock(keyboardCmdLock_);
    int32_t cmdCount = ++cmdId_;
    return HideKeyboardImplWithoutLock(cmdCount, 0);
}

int32_t InputMethodAbility::HideKeyboardImplWithoutLock(int32_t cmdId, uint32_t sessionId, int32_t clientSessionId)
{
    NotifyInputStopToClients();
    if (cmdId != cmdId_) {
        IMSA_HILOGE("current is not last cmd cur: %{public}d, cmdId_: %{public}d!", cmdId, cmdId_);
        return ErrorCode::NO_ERROR;
    }
    return HideKeyboard(Trigger::IMF, sessionId, clientSessionId);
}

int32_t InputMethodAbility::ShowKeyboard(int32_t requestKeyboardReason)
{
    std::lock_guard<std::recursive_mutex> lock(keyboardCmdLock_);
    int32_t cmdCount = ++cmdId_;
    HandleRequestKeyboardReasonChanged(static_cast<RequestKeyboardReason>(requestKeyboardReason));
    return ShowKeyboardWithoutLock(cmdCount, InputStartScene::SHOW_KEYBOARD);
}

int32_t InputMethodAbility::ShowKeyboardWithoutLock(int32_t cmdId, InputStartScene scene)
{
    auto ret = ShowKeyboardImplWithoutLock(cmdId);
    NotifyInputStartToClients(scene);
    return ret;
}

int32_t InputMethodAbility::ShowKeyboardImplWithoutLock(int32_t cmdId)
{
    if (cmdId != cmdId_) {
        IMSA_HILOGE("current is not last cmd cur: %{public}d, cmdId_: %{public}d!", cmdId, cmdId_);
        return ErrorCode::NO_ERROR;
    }
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener is nullptr!");
        return ErrorCode::ERROR_IME;
    }
    IMSA_HILOGI("IMA start.");
    if (panels_.Contains(SOFT_KEYBOARD)) {
        auto panel = GetSoftKeyboardPanel();
        if (panel == nullptr) {
            IMSA_HILOGE("panel is nullptr!");
            return ErrorCode::ERROR_IME;
        }
        auto flag = panel->GetPanelFlag();
        imeListener_->OnKeyboardStatus(true);
        if (flag == FLG_CANDIDATE_COLUMN) {
            IMSA_HILOGI("panel flag is candidate, no need to show.");
            NotifyKeyboardHeight(0, flag);
            return ErrorCode::NO_ERROR;
        }
        return ShowPanel(panel, flag, Trigger::IMF);
    }
    isShowAfterCreate_.store(true);
    IMSA_HILOGI("panel not create.");
    auto channel = GetInputDataChannelProxy();
    if (channel != nullptr) {
        channel->SendKeyboardStatus(static_cast<int32_t>(KeyboardStatus::SHOW));
    }
    imeListener_->OnKeyboardStatus(true);
    return ErrorCode::NO_ERROR;
}

void InputMethodAbility::NotifyInputStartToClients(InputStartScene scene, bool isShowKeyboard)
{
    if (isProxyIme_.load()) {
        IMSA_HILOGD("proxy ime, no need to notify.");
        return;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return;
    }
    InputStartInfo info;
    info.scene = scene;
    info.clientInfo.isShowKeyboard = isShowKeyboard;
    info.clientInfo.windowId = GetInputAttribute().editorWindowId;
    info.clientInfo.displayId = GetInputAttribute().editorDisplayId;
    info.clientInfo.requestKeyboardReason = static_cast<int32_t>(GetAttachOptions().requestKeyboardReason);

    auto panel = GetSoftKeyboardPanel();
    if (panel == nullptr) {
        info.imeInfo.status = InputWindowStatus::HIDE;
        proxy->NotifyInputStart(info);
        return;
    }
    info.imeInfo.panelFlag = panel->GetPanelFlag();
    info.imeInfo.status = panel->IsShowing() ? InputWindowStatus::SHOW : InputWindowStatus::HIDE;
    info.imeInfo.displayId = ImfCommonConst::DEFAULT_DISPLAY_ID;
    if (info.imeInfo.status == InputWindowStatus::HIDE) {
        proxy->NotifyInputStart(info);
        return;
    }
    info.imeInfo.displayId = GetInputAttribute().callingDisplayId;
    if (scene != InputStartScene::ATTACH && scene != InputStartScene::SHOW_KEYBOARD) {
        auto ret = panel->GetDisplayId(info.imeInfo.displayId);
        if (ret != ErrorCode::NO_ERROR) {
            info.imeInfo.displayId = GetInputAttribute().callingDisplayId;
        }
    }
    proxy->NotifyInputStart(info);
}

void InputMethodAbility::NotifyInputStopToClients()
{
    if (isProxyIme_.load()) {
        IMSA_HILOGD("proxy ime, no need to notify.");
        return;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("imsa proxy is nullptr!");
        return;
    }
    InputStopInfo info;
    info.scene = InputStopScene::CLIENT_TRIGGER;
    info.displayId = GetInputAttribute().editorDisplayId;
    proxy->NotifyInputStop(info);
}

void InputMethodAbility::NotifyPanelStatusInfo(PanelStatusInfo &info)
{
    // When panelFlag is CANDIDATE_COLUMN and inputType is not VOICEKB_INPUT, no need to notify.
    auto channel = GetInputDataChannelProxy();
    NotifyPanelStatusInfo(info, channel);
}

int32_t InputMethodAbility::InvokeStartInputCallbackWithInfoRestruct(
    const TextTotalConfig &textConfig, bool isNotifyInputStart)
{
    TextTotalConfig newTextConfig = {};
    int32_t ret = GetTextConfig(newTextConfig);
    if (ret == ErrorCode::NO_ERROR) {
        newTextConfig.inputAttribute.bundleName = textConfig.inputAttribute.bundleName;
        newTextConfig.inputAttribute.callingDisplayId = textConfig.inputAttribute.callingDisplayId;
        newTextConfig.inputAttribute.windowId = textConfig.inputAttribute.windowId;
        newTextConfig.inputAttribute.editorDisplayId = textConfig.inputAttribute.editorDisplayId;
        newTextConfig.inputAttribute.editorWindowId = textConfig.inputAttribute.editorWindowId;
        newTextConfig.isSimpleKeyboardEnabled = textConfig.isSimpleKeyboardEnabled;
        return InvokeStartInputCallback(newTextConfig, isNotifyInputStart);
    }
    IMSA_HILOGW("failed to get text config, ret: %{public}d.", ret);
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener_ is nullptr!");
        return ErrorCode::ERROR_IME;
    }
    if (isNotifyInputStart) {
        imeListener_->OnInputStart();
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::InvokeStartInputCallback(const TextTotalConfig &textConfig, bool isNotifyInputStart)
{
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener_ is nullptr!");
        return ErrorCode::ERROR_IME;
    }
    positionY_ = textConfig.positionY;
    height_ = textConfig.height;
    NotifyInfoToWmsInStartInput(textConfig);
    IMSA_HILOGI("attributeInfo:%{public}s, isNotifyInputStartInfo:%{public}d/%{public}d.",
        textConfig.inputAttribute.InfoLog().c_str(), isNotifyInputStart, isInputStartNotified_);
    SetInputAttribute(textConfig.inputAttribute);
    if (kdListener_ != nullptr) {
        kdListener_->OnEditorAttributeChange(textConfig.inputAttribute);
    }
    AttachOptions options;
    options.requestKeyboardReason = textConfig.requestKeyboardReason;
    options.isSimpleKeyboardEnabled = textConfig.isSimpleKeyboardEnabled;
    InvokeAttachOptionsCallback(options, isNotifyInputStart || !isInputStartNotified_);
    if (isNotifyInputStart || !isInputStartNotified_) {
        isInputStartNotified_ = true;
        IMSA_HILOGD("OnInputStart begin");
        imeListener_->OnInputStart();
    }
    if (TextConfig::IsPrivateCommandValid(textConfig.privateCommand) && IsDefaultIme()) {
        imeListener_->ReceivePrivateCommand(textConfig.privateCommand);
    }
    if (kdListener_ != nullptr) {
        if (textConfig.cursorInfo.left != INVALID_CURSOR_VALUE) {
            kdListener_->OnCursorUpdate(
                textConfig.cursorInfo.left, textConfig.cursorInfo.top, textConfig.cursorInfo.height);
        }
        if (textConfig.textSelection.newBegin == INVALID_SELECTION_VALUE ||
            (textConfig.textSelection.newBegin == textConfig.textSelection.oldBegin &&
                textConfig.textSelection.newEnd == textConfig.textSelection.oldEnd)) {
            IMSA_HILOGD("invalid selection or no selection change");
        } else {
            kdListener_->OnSelectionChange(textConfig.textSelection.oldBegin, textConfig.textSelection.oldEnd,
                textConfig.textSelection.newBegin, textConfig.textSelection.newEnd);
        }
    }
    if (textConfig.windowId != INVALID_WINDOW_ID) {
        imeListener_->OnSetCallingWindow(textConfig.windowId);
    }
    return ErrorCode::NO_ERROR;
}

void InputMethodAbility::HandleRequestKeyboardReasonChanged(const RequestKeyboardReason &requestKeyboardReason)
{
    AttachOptions options;
    options.requestKeyboardReason = requestKeyboardReason;
    options.isSimpleKeyboardEnabled = GetAttachOptions().isSimpleKeyboardEnabled;
    InvokeAttachOptionsCallback(options);
}

void InputMethodAbility::InvokeAttachOptionsCallback(const AttachOptions &options, bool isFirstNotify)
{
    auto oldOptions = GetAttachOptions();
    if (!isFirstNotify && oldOptions.isSimpleKeyboardEnabled == options.isSimpleKeyboardEnabled
        && options.requestKeyboardReason == oldOptions.requestKeyboardReason) {
        return;
    }
    SetAttachOptions(options);
    if (textInputClientListener_ != nullptr) {
        textInputClientListener_->OnAttachOptionsChanged(options);
    }
}

void InputMethodAbility::SetAttachOptions(const AttachOptions &options)
{
    std::lock_guard<std::mutex> lock(attachOptionsLock_);
    attachOptions_ = options;
}

void InputMethodAbility::ClearAttachOptions()
{
    std::lock_guard<std::mutex> lock(attachOptionsLock_);
    attachOptions_ = {};
}

AttachOptions InputMethodAbility::GetAttachOptions()
{
    std::lock_guard<std::mutex> lock(attachOptionsLock_);
    return attachOptions_;
}

int32_t InputMethodAbility::InsertText(const std::string &text, const AsyncIpcCallBack &callback)
{
    InputMethodSyncTrace tracer("IMA_InsertText");
    IMSA_HILOGD("InputMethodAbility start.");
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    return channel->InsertText(text, callback);
}

int32_t InputMethodAbility::DeleteForward(int32_t length, const AsyncIpcCallBack &callback)
{
    InputMethodSyncTrace tracer("IMA_DeleteForward");
    IMSA_HILOGD("InputMethodAbility start, length: %{public}d.", length);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    return channel->DeleteForward(length, callback);
}

int32_t InputMethodAbility::DeleteBackward(int32_t length, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility start, length: %{public}d.", length);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    return channel->DeleteBackward(length, callback);
}

int32_t InputMethodAbility::SendFunctionKey(int32_t funcKey, const AsyncIpcCallBack &callback)
{
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->SendFunctionKey(funcKey, callback);
}

int32_t InputMethodAbility::HideKeyboardSelf()
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.KeyboardController.hide", 1);
#endif
    // Current Ime is exiting, hide softkeyboard will cause the TextFiled to lose focus.
    if (isImeTerminating_.load()) {
        IMSA_HILOGI("Current Ime is terminating, no need to hide keyboard.");
        return ErrorCode::NO_ERROR;
    }
    InputMethodSyncTrace tracer("IMA_HideKeyboardSelf start.");
    auto ret = HideKeyboard(Trigger::IME_APP, 0);
    if (ret == ErrorCode::NO_ERROR) {
        InputMethodSysEvent::GetInstance().OperateSoftkeyboardBehaviour(OperateIMEInfoCode::IME_HIDE_SELF);
    }
    return ret == ErrorCode::ERROR_CLIENT_NULL_POINTER ? ret : ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::SendExtendAction(int32_t action, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility, action: %{public}d.", action);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->HandleExtendAction(action, callback);
}

int32_t InputMethodAbility::GetTextBeforeCursor(int32_t number, std::u16string &text, const AsyncIpcCallBack &callback)
{
    InputMethodSyncTrace tracer("IMA_GetForward");
    IMSA_HILOGD("InputMethodAbility, number: %{public}d.", number);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    std::string textu8 = "";
    auto ret = channel->GetTextBeforeCursor(number, textu8, callback);
    text = Str8ToStr16(textu8);
    return ret;
}

int32_t InputMethodAbility::GetTextAfterCursor(
    int32_t number, std::u16string &text, const AsyncIpcCallBack &callback)
{
    InputMethodSyncTrace tracer("IMA_GetTextAfterCursor");
    IMSA_HILOGD("InputMethodAbility, number: %{public}d.", number);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    std::string textu8 = "";
    auto ret = channel->GetTextAfterCursor(number, textu8, callback);
    text = Str8ToStr16(textu8);
    return ret;
}

int32_t InputMethodAbility::MoveCursor(int32_t keyCode, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility, keyCode: %{public}d.", keyCode);
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->MoveCursor(keyCode, callback);
}

int32_t InputMethodAbility::SelectByRange(int32_t start, int32_t end, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility, start: %{public}d, end: %{public}d", start, end);
    if (start < 0 || end < 0) {
        IMSA_HILOGE("check parameter failed, start: %{public}d, end: %{public}d!", start, end);
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    auto dataChannel = GetInputDataChannelProxyWrap();
    if (dataChannel == nullptr) {
        IMSA_HILOGE("datachannel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return dataChannel->SelectByRange(start, end, callback);
}

int32_t InputMethodAbility::SelectByMovement(int32_t direction, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility, direction: %{public}d.", direction);
    auto dataChannel = GetInputDataChannelProxyWrap();
    if (dataChannel == nullptr) {
        IMSA_HILOGE("datachannel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return dataChannel->SelectByMovement(direction, 0, callback);
}

int32_t InputMethodAbility::GetEnterKeyType(int32_t &keyType)
{
    IMSA_HILOGD("InputMethodAbility start.");
    auto channel = GetInputDataChannelProxy();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->GetEnterKeyType(keyType);
}

int32_t InputMethodAbility::GetInputPattern(int32_t &inputPattern)
{
    IMSA_HILOGD("InputMethodAbility start.");
    auto channel = GetInputDataChannelProxy();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->GetInputPattern(inputPattern);
}

int32_t InputMethodAbility::GetTextIndexAtCursor(int32_t &index, const AsyncIpcCallBack &callback)
{
    IMSA_HILOGD("InputMethodAbility start.");
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return channel->GetTextIndexAtCursor(index, callback);
}

int32_t InputMethodAbility::GetTextConfig(TextTotalConfig &textConfig)
{
    IMSA_HILOGI("InputMethodAbility start.");
    auto channel = GetInputDataChannelProxy();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    TextTotalConfigInner textConfigInner = InputMethodTools::GetInstance().TextTotalConfigToInner(textConfig);
    auto ret = channel->GetTextConfig(textConfigInner);
    if (ret == ErrorCode::NO_ERROR) {
        textConfig = InputMethodTools::GetInstance().InnerToTextTotalConfig(textConfigInner);
        textConfig.inputAttribute.bundleName = GetInputAttribute().bundleName;
        textConfig.inputAttribute.callingDisplayId = GetInputAttribute().callingDisplayId;
        textConfig.inputAttribute.windowId = GetInputAttribute().windowId;
        textConfig.inputAttribute.editorDisplayId = GetInputAttribute().editorDisplayId;
        textConfig.inputAttribute.editorWindowId = GetInputAttribute().editorWindowId;
    }
    return ret;
}

void InputMethodAbility::SetInputDataChannel(const sptr<IRemoteObject> &object)
{
    IMSA_HILOGD("SetInputDataChannel start.");
    if (object == nullptr) {
        IMSA_HILOGE("object is nullptr.");
        return;
    }
    std::lock_guard<std::mutex> lock(dataChannelLock_);
    if (dataChannelObject_ != nullptr && object != nullptr && object.GetRefPtr() == dataChannelObject_.GetRefPtr()) {
        IMSA_HILOGD("datachannel has already been set.");
        return;
    }
    auto channelProxy = std::make_shared<InputDataChannelProxy>(object);
    if (channelProxy == nullptr) {
        IMSA_HILOGE("failed to create channel proxy!");
        return;
    }
    sptr<IRemoteObject> agentObject = nullptr;
    if (agentStub_ != nullptr) {
        agentObject = agentStub_->AsObject();
    }
    auto channelWrap = std::make_shared<InputDataChannelProxyWrap>(channelProxy, agentObject);
    if (channelWrap == nullptr) {
        IMSA_HILOGE("failed to create channel wrap!");
        return;
    }
    if (dataChannelProxyWrap_ != nullptr) {
        dataChannelProxyWrap_->ClearRspHandlers();
    }
    if (dataChannelObject_ != nullptr && dataChannelDeathRecipient_ != nullptr) {
        if (!dataChannelObject_->RemoveDeathRecipient(dataChannelDeathRecipient_)) {
            IMSA_HILOGE("RemoveDeathRecipient failed");
        }
        dataChannelDeathRecipient_ = nullptr;
    }

    dataChannelProxyWrap_ = channelWrap;
    dataChannelObject_ = object;

    dataChannelDeathRecipient_ = new (std::nothrow) InputDeathRecipient();
    if (dataChannelDeathRecipient_ != nullptr) {
        dataChannelDeathRecipient_->SetDeathRecipient(
            [this, object](const wptr<IRemoteObject> &remote) { OnInputDataChannelDied(object); });
        if (object->IsProxyObject() && !object->AddDeathRecipient(dataChannelDeathRecipient_)) {
            IMSA_HILOGE("failed to add death recipient!");
        }
    }
}

void InputMethodAbility::OnInputDataChannelDied(const sptr<IRemoteObject> &dataChannelObject)
{
    IMSA_HILOGW("data channel died!");
    ClearDataChannel(dataChannelObject);
}

bool InputMethodAbility::NotifyInfoToWmsInStartInput(const TextTotalConfig &textConfig)
{
    auto imeListener = GetImeListener();
    if (imeListener == nullptr) {
        IMSA_HILOGE("imeListener is nullptr!");
        return false;
    }
    auto task = [this, textConfig]() {
        panels_.ForEach([&textConfig](const PanelType &type, const std::shared_ptr<InputMethodPanel> &panel) {
            if (panel == nullptr) {
                return false;
            }
            if (type == SOFT_KEYBOARD && panel->GetPanelFlag() == FLG_FIXED && panel->IsShowing()) {
                panel->SetTextFieldAvoidInfo(textConfig.positionY, textConfig.height);
            }
            return false;
        });
    };
    return imeListener->PostTaskToEventHandler(task, "NotifyInfoToWms");
}

std::shared_ptr<InputDataChannelProxyWrap> InputMethodAbility::GetInputDataChannelProxyWrap()
{
    if (imeMirrorMgr_.IsImeMirrorEnable()) {
        IMSA_HILOGW("[ImeMirrorTag] not allow send to data channel");
        return nullptr;
    }
    std::lock_guard<std::mutex> lock(dataChannelLock_);
    return dataChannelProxyWrap_;
}

std::shared_ptr<InputDataChannelProxy> InputMethodAbility::GetInputDataChannelProxy()
{
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        return nullptr;
    }
    return channel->GetDataChannel();
}

void InputMethodAbility::SetInputControlChannel(sptr<IRemoteObject> &object)
{
    IMSA_HILOGD("SetInputControlChannel start.");
    std::lock_guard<std::mutex> lock(controlChannelLock_);
    std::shared_ptr<InputControlChannelProxy> channelProxy = std::make_shared<InputControlChannelProxy>(object);
    if (channelProxy == nullptr) {
        IMSA_HILOGD("channelProxy is nullptr!");
        return;
    }
    controlChannel_ = channelProxy;
}

void InputMethodAbility::ClearInputControlChannel()
{
    std::lock_guard<std::mutex> lock(controlChannelLock_);
    controlChannel_ = nullptr;
}

std::shared_ptr<InputControlChannelProxy> InputMethodAbility::GetInputControlChannel()
{
    std::lock_guard<std::mutex> lock(controlChannelLock_);
    return controlChannel_;
}

void InputMethodAbility::OnRemoteSaDied(const wptr<IRemoteObject> &object)
{
    IMSA_HILOGI("input method service died.");
    isBound_.store(false);
    ClearDataChannel(dataChannelObject_);
    ClearInputControlChannel();
    ClearSystemCmdChannel();
    {
        std::lock_guard<std::mutex> lock(abilityLock_);
        abilityManager_ = nullptr;
    }
    if (imeListener_ != nullptr) {
        imeListener_->OnInputStop();
    }
}

int32_t InputMethodAbility::GetSecurityMode(int32_t &security)
{
    IMSA_HILOGI("InputMethodAbility start.");
    int32_t securityMode = securityMode_.load();
    if (securityMode != static_cast<int32_t>(INVALID_SECURITY_MODE)) {
        IMSA_HILOGD("Get cache security mode: %{public}d.", securityMode);
        security = securityMode;
        return ErrorCode::NO_ERROR;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("Imsa proxy is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto ret = proxy->GetSecurityMode(security);
    if (ret == ErrorCode::NO_ERROR) {
        securityMode_.store(security);
    }
    return ret;
}

void InputMethodAbility::ClearSystemCmdChannel()
{
    std::lock_guard<std::mutex> lock(systemCmdChannelLock_);
    if (systemCmdChannelProxy_ == nullptr) {
        IMSA_HILOGD("systemCmdChannelProxy_ already nullptr.");
        return;
    }
    systemCmdChannelProxy_ = nullptr;
    IMSA_HILOGD("end.");
}

sptr<SystemCmdChannelProxy> InputMethodAbility::GetSystemCmdChannelProxy()
{
    std::lock_guard<std::mutex> lock(systemCmdChannelLock_);
    return systemCmdChannelProxy_;
}

int32_t InputMethodAbility::OnConnectSystemCmd(const sptr<IRemoteObject> &channel)
{
    IMSA_HILOGD("InputMethodAbility start.");
    sptr<SystemCmdChannelProxy> cmdChannel = new (std::nothrow) SystemCmdChannelProxy(channel);
    if (cmdChannel == nullptr) {
        IMSA_HILOGE("failed to create channel proxy!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    {
        std::lock_guard<std::mutex> lock(systemCmdChannelLock_);
        systemCmdChannelProxy_ = cmdChannel;
    }
    PushPrivateCommand();
    auto panel = GetSoftKeyboardPanel();
    if (panel != nullptr) {
        NotifyPanelStatus(false);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::OnSecurityChange(int32_t security)
{
    IMSA_HILOGI("InputMethodAbility start.");
    securityMode_.store(security);
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener_ is nullptr!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    imeListener_->OnSecurityChange(security);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::AdjustKeyboard()
{
    if (panels_.Contains(SOFT_KEYBOARD)) {
        auto panel = GetSoftKeyboardPanel();
        if (panel == nullptr) {
            IMSA_HILOGE("panel is nullptr!");
            return ErrorCode::ERROR_IME;
        }
        auto flag = panel->GetPanelFlag();
        if (flag != FLG_FIXED) {
            IMSA_HILOGI("panel flag is not fix, no need to adjust.");
            return ErrorCode::NO_ERROR;
        }
        return panel->AdjustKeyboard();
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::CreatePanel(const std::shared_ptr<AbilityRuntime::Context> &context,
    const PanelInfo &panelInfo, std::shared_ptr<InputMethodPanel> &inputMethodPanel)
{
    IMSA_HILOGI("InputMethodAbility start.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.createPanel", 1);
#endif
    auto panelHeightCallback = [this](uint32_t panelHeight, PanelFlag panelFlag) {
        NotifyKeyboardHeight(panelHeight, panelFlag);
    };
    auto flag = panels_.ComputeIfAbsent(panelInfo.panelType,
        [panelHeightCallback, &panelInfo, &context, &inputMethodPanel](
            const PanelType &panelType, std::shared_ptr<InputMethodPanel> &panel) {
            inputMethodPanel = std::make_shared<InputMethodPanel>();
            inputMethodPanel->SetPanelHeightCallback(panelHeightCallback);
            auto ret = inputMethodPanel->CreatePanel(context, panelInfo);
            if (ret == ErrorCode::NO_ERROR) {
                panel = inputMethodPanel;
                return true;
            }
            inputMethodPanel = nullptr;
            return false;
        });
    if (flag && isShowAfterCreate_.load() && panelInfo.panelType == SOFT_KEYBOARD &&
        panelInfo.panelFlag != FLG_CANDIDATE_COLUMN) {
        isShowAfterCreate_.store(false);
        auto task = std::make_shared<TaskImsaShowKeyboard>();
        TaskManager::GetInstance().PostTask(task);
    }
    return flag ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
}

int32_t InputMethodAbility::DestroyPanel(const std::shared_ptr<InputMethodPanel> &inputMethodPanel)
{
    IMSA_HILOGI("InputMethodAbility start.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.destroyPanel", 1);
#endif
    if (inputMethodPanel == nullptr) {
        IMSA_HILOGE("panel is nullptr!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    auto ret = inputMethodPanel->DestroyPanel();
    if (ret == ErrorCode::NO_ERROR) {
        PanelType panelType = inputMethodPanel->GetPanelType();
        panels_.Erase(panelType);
    }
    return ret;
}

int32_t InputMethodAbility::ShowPanel(const std::shared_ptr<InputMethodPanel> &inputMethodPanel)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.panel.show", 1);
#endif
    std::lock_guard<std::recursive_mutex> lock(keyboardCmdLock_);
    if (inputMethodPanel == nullptr) {
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    return ShowPanel(inputMethodPanel, inputMethodPanel->GetPanelFlag(), Trigger::IME_APP);
}

int32_t InputMethodAbility::HidePanel(const std::shared_ptr<InputMethodPanel> &inputMethodPanel)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.panel.hide", 1);
#endif
    if (inputMethodPanel == nullptr) {
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    // Current Ime is exiting, hide softkeyboard will cause the TextFiled to lose focus.
    if (isImeTerminating_.load() && inputMethodPanel->GetPanelType() == PanelType::SOFT_KEYBOARD) {
        IMSA_HILOGI("Current Ime is terminating, no need to hide keyboard.");
        return ErrorCode::NO_ERROR;
    }
    if (isShowAfterCreate_.load() && inputMethodPanel->GetPanelType() == PanelType::SOFT_KEYBOARD &&
        inputMethodPanel->GetPanelFlag() != PanelFlag::FLG_CANDIDATE_COLUMN) {
        isShowAfterCreate_.store(false);
    }
    std::lock_guard<std::recursive_mutex> lock(keyboardCmdLock_);
    return HidePanel(inputMethodPanel, inputMethodPanel->GetPanelFlag(), Trigger::IME_APP, 0);
}

int32_t InputMethodAbility::ShowPanel(
    const std::shared_ptr<InputMethodPanel> &inputMethodPanel, PanelFlag flag, Trigger trigger)
{
    if (inputMethodPanel == nullptr) {
        return ErrorCode::ERROR_IMA_NULLPTR;
    }
    if (trigger == Trigger::IME_APP && GetInputDataChannelProxyWrap() == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    if (flag == FLG_FIXED && inputMethodPanel->GetPanelType() == SOFT_KEYBOARD) {
        auto ret = inputMethodPanel->SetTextFieldAvoidInfo(positionY_, height_);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("failed to set keyBoard, ret: %{public}d!", ret);
        }
    }
    auto ret = inputMethodPanel->ShowPanel(trigger, GetInputAttribute().windowId);
    if (ret == ErrorCode::NO_ERROR) {
        NotifyPanelStatus(false, FLG_FIXED, true);
        PanelStatusInfo info;
        info.panelInfo.panelType = inputMethodPanel->GetPanelType();
        info.panelInfo.panelFlag = flag;
        info.visible = true;
        info.trigger = trigger;
        NotifyPanelStatusInfo(info);
    }
    return ret;
}

int32_t InputMethodAbility::HidePanel(const std::shared_ptr<InputMethodPanel> &inputMethodPanel, PanelFlag flag,
    Trigger trigger, uint32_t sessionId, int32_t clientSessionId)
{
    if (inputMethodPanel == nullptr) {
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    auto ret = inputMethodPanel->HidePanel();
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGD("failed, ret: %{public}d", ret);
        return ret;
    }
    PanelStatusInfo info;
    info.panelInfo.panelType = inputMethodPanel->GetPanelType();
    info.panelInfo.panelFlag = flag;
    info.visible = false;
    info.trigger = trigger;
    info.sessionId = sessionId;
    info.clientSessionId = clientSessionId;
    NotifyPanelStatusInfo(info);
    if (trigger == Trigger::IMF && inputMethodPanel->GetPanelType() == PanelType::SOFT_KEYBOARD) {
        AsyncIpcCallBack callback = [](int32_t code, const ResponseData &data) {};
        FinishTextPreview(callback);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::NotifyPanelStatus(bool isUseParameterFlag, PanelFlag panelFlag, bool isCheckFuncButton)
{
    auto panel = GetSoftKeyboardPanel();
    if (panel == nullptr) {
        IMSA_HILOGE("panel is null");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto keyboardSize = panel->GetKeyboardSize();
    PanelFlag curPanelFlag = isUseParameterFlag ? panelFlag : panel->GetPanelFlag();
    SysPanelStatus sysPanelStatus = { inputType_, curPanelFlag, keyboardSize.width, keyboardSize.height };
    sysPanelStatus.isPanelRaised = panel->IsKeyboardBottomElevated(curPanelFlag);
    sysPanelStatus.needFuncButton = panel->IsFunctionButtonVisible(sysPanelStatus.isPanelRaised);
    auto systemChannel = GetSystemCmdChannelProxy();
    if (systemChannel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    SetSysPanelStatus(sysPanelStatus);
    return systemChannel->NotifyPanelStatus(sysPanelStatus);
}

void InputMethodAbility::SetInputAttribute(const InputAttribute &inputAttribute)
{
    std::lock_guard<std::mutex> lock(inputAttrLock_);
    auto callingScreenId = inputAttribute_.callingScreenId;
    if (inputAttribute.callingDisplayId != inputAttribute_.callingDisplayId) {
        const auto display = Rosen::DisplayManager::GetInstance().GetDisplayById(inputAttribute.callingDisplayId);
        if (display != nullptr) {
            callingScreenId = display->GetScreenId();
        } else {
            IMSA_HILOGE("failed to get display by id %{public}" PRIu64 ".", inputAttribute.callingDisplayId);
            callingScreenId = 0;
        }
    }
    inputAttribute_ = inputAttribute;
    inputAttribute_.callingScreenId = callingScreenId;
}

void InputMethodAbility::ClearInputAttribute()
{
    std::lock_guard<std::mutex> lock(inputAttrLock_);
    auto callingDisplayId = inputAttribute_.callingDisplayId;
    auto callingScreenId = inputAttribute_.callingScreenId;
    inputAttribute_ = {};
    inputAttribute_.callingDisplayId = callingDisplayId;
    inputAttribute_.callingScreenId = callingScreenId;
}

InputAttribute InputMethodAbility::GetInputAttribute()
{
    std::lock_guard<std::mutex> lock(inputAttrLock_);
    return inputAttribute_;
}

int32_t InputMethodAbility::HideKeyboard(Trigger trigger, uint32_t sessionId, int32_t clientSessionId)
{
    isShowAfterCreate_.store(false);
    InputMethodSyncTrace tracer("IMA_HideKeyboard");
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener_ is nullptr!");
        return ErrorCode::ERROR_IME;
    }
    IMSA_HILOGD("IMA, trigger: %{public}d.", static_cast<int32_t>(trigger));
    if (panels_.Contains(SOFT_KEYBOARD)) {
        auto panel = GetSoftKeyboardPanel();
        if (panel == nullptr) {
            IMSA_HILOGE("panel is nullptr!");
            return ErrorCode::ERROR_IME;
        }
        auto flag = panel->GetPanelFlag();
        imeListener_->OnKeyboardStatus(false);
        if (flag == FLG_CANDIDATE_COLUMN && inputType_ != InputType::VOICEKB_INPUT) {
            IMSA_HILOGI("panel flag is candidate, no need to hide.");
            return ErrorCode::NO_ERROR;
        }
        return HidePanel(panel, flag, trigger, sessionId, clientSessionId);
    }
    IMSA_HILOGI("panel is not created.");
    imeListener_->OnKeyboardStatus(false);
    auto channel = GetInputDataChannelProxy();
    if (channel != nullptr) {
        channel->SendKeyboardStatus(static_cast<int32_t>(KeyboardStatus::HIDE));
    }
    auto controlChannel = GetInputControlChannel();
    if (controlChannel != nullptr && trigger == Trigger::IME_APP) {
        controlChannel->HideKeyboardSelf();
    }
    return ErrorCode::NO_ERROR;
}

std::shared_ptr<InputMethodPanel> InputMethodAbility::GetSoftKeyboardPanel()
{
    auto result = panels_.Find(SOFT_KEYBOARD);
    if (!result.first) {
        return nullptr;
    }
    auto panel = result.second;
    if (!BlockRetry(FIND_PANEL_RETRY_INTERVAL, MAX_RETRY_TIMES, [panel]() -> bool {
            return panel != nullptr && panel->windowId_ != InputMethodPanel::INVALID_WINDOW_ID;
        })) {
        return nullptr;
    }
    return panel;
}

bool InputMethodAbility::IsCurrentIme()
{
    IMSA_HILOGD("InputMethodAbility start.");
    if (isCurrentIme_) {
        return true;
    }
    std::lock_guard<std::mutex> lock(imeCheckMutex_);
    if (isCurrentIme_) {
        return true;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("failed to get imsa proxy!");
        return false;
    }

    bool ret = false;
    proxy->IsCurrentIme(ret);
    if (ret) {
        isCurrentIme_ = true;
    }
    return ret;
}

bool InputMethodAbility::IsDefaultIme()
{
    IMSA_HILOGD("InputMethodAbility start");
    if (isDefaultIme_) {
        return true;
    }
    std::lock_guard<std::mutex> lock(defaultImeCheckMutex_);
    if (isDefaultIme_) {
        return true;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("failed to get imsa proxy!");
        return false;
    }
    auto ret = proxy->IsDefaultIme();
    if (ret == ErrorCode::NO_ERROR) {
        isDefaultIme_ = true;
        return true;
    }
    IMSA_HILOGE("call IsDefaultIme failed, ret: %{public}d!", ret);
    return false;
}

bool InputMethodAbility::IsEnable(uint64_t displayId)
{
    if (imeListener_ == nullptr) {
        return false;
    }
    return imeListener_->IsEnable(displayId);
}

bool InputMethodAbility::IsCallbackRegistered(const std::string &type)
{
    if (imeListener_ == nullptr) {
        return false;
    }
    return imeListener_->IsCallbackRegistered(type);
}

bool InputMethodAbility::IsSystemApp()
{
    IMSA_HILOGD("InputMethodAbility start");
    if (isSystemApp_) {
        return true;
    }
    std::lock_guard<std::mutex> lock(systemAppCheckMutex_);
    if (isSystemApp_) {
        return true;
    }
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("failed to get imsa proxy!");
        return false;
    }
    bool ret = false;
    proxy->IsSystemApp(ret);
    if (ret) {
        isSystemApp_ = true;
    }
    return ret;
}

int32_t InputMethodAbility::ExitCurrentInputType()
{
    IMSA_HILOGD("InputMethodAbility start.");
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.exitCurrentInputType", 1);
#endif
    ClearInputType();
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("failed to get imsa proxy!");
        return false;
    }
    auto ret = proxy->ExitCurrentInputType();
    if (ret == ErrorCode::NO_ERROR) {
        NotifyPanelStatus(false);
    }
    return ret;
}

void InputMethodAbility::ClearInputType()
{
    std::lock_guard<std::mutex> lock(inputTypeLock_);
    if (inputType_ != InputType::SECURITY_INPUT) {
        inputType_ = InputType::NONE;
    }
}

int32_t InputMethodAbility::IsPanelShown(const PanelInfo &panelInfo, bool &isShown)
{
    isShown = false;
    auto result = panels_.Find(panelInfo.panelType);
    if (!result.first) {
        IMSA_HILOGI("panel type: %{public}d not found.", static_cast<int32_t>(panelInfo.panelType));
        return ErrorCode::NO_ERROR;
    }
    auto panel = result.second;
    if (panel->GetPanelType() == PanelType::SOFT_KEYBOARD && panel->GetPanelFlag() != panelInfo.panelFlag) {
        IMSA_HILOGI("queried flag: %{public}d, current flag: %{public}d, panel not found.",
            static_cast<int32_t>(panelInfo.panelFlag), static_cast<int32_t>(panel->GetPanelFlag()));
        return ErrorCode::NO_ERROR;
    }
    isShown = panel->IsShowing();
    IMSA_HILOGI("type: %{public}d, flag: %{public}d, result: %{public}d.", static_cast<int32_t>(panelInfo.panelType),
        static_cast<int32_t>(panelInfo.panelFlag), isShown);
    return ErrorCode::NO_ERROR;
}

void InputMethodAbility::OnClientInactive(const sptr<IRemoteObject> &channel)
{
    IMSA_HILOGI("client inactive.");
    if (imeListener_ != nullptr) {
        imeListener_->OnInputFinish();
    }
    auto channelProxy = std::make_shared<InputDataChannelProxy>(channel);
    if (channelProxy == nullptr) {
        IMSA_HILOGE("failed to create channel proxy!");
        return;
    }
    auto panel = GetSoftKeyboardPanel();
    if (imeListener_ != nullptr && panel != nullptr && panel->GetPanelFlag() != PanelFlag::FLG_FIXED) {
        imeListener_->OnKeyboardStatus(false);
    }
    panels_.ForEach([this, &channelProxy](const PanelType &panelType, const std::shared_ptr<InputMethodPanel> &panel) {
        if (panel == nullptr) {
            return false;
        }
        if (panelType == PanelType::SOFT_KEYBOARD && panel->GetPanelFlag() == PanelFlag::FLG_FIXED) {
            return false;
        }
        auto ret = panel->HidePanel();
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("failed, ret: %{public}d", ret);
            return false;
        }
        PanelStatusInfo info;
        info.panelInfo.panelType = panel->GetPanelType();
        info.panelInfo.panelFlag = panel->GetPanelFlag();
        info.visible = false;
        info.trigger = Trigger::IME_APP;
        NotifyPanelStatusInfo(info, channelProxy);
        // finish previewing text when soft keyboard hides
        if (panel->GetPanelType() == PanelType::SOFT_KEYBOARD) {
            AsyncIpcCallBack callback = [](int32_t code, const ResponseData &data) {};
            FinishTextPreview(callback);
        }
        return false;
    });
    // cannot clear inputAttribute,otherwise it will affect hicar
    ClearDataChannel(channel);
    ClearAttachOptions();
    ClearBindClientInfo();
}

void InputMethodAbility::NotifyKeyboardHeight(uint32_t panelHeight, PanelFlag panelFlag)
{
    auto channel = GetInputDataChannelProxy();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return;
    }
    IMSA_HILOGD("notify panel height: %{public}u, flag: %{public}d.", panelHeight, static_cast<int32_t>(panelFlag));
    if (panelFlag != PanelFlag::FLG_FIXED) {
        channel->NotifyKeyboardHeight(0);
        return;
    }
    channel->NotifyKeyboardHeight(panelHeight);
}

int32_t InputMethodAbility::SendPrivateCommand(const std::unordered_map<std::string, PrivateDataValue> &privateCommand,
    bool validateDefaultIme)
{
    if (validateDefaultIme && !IsDefaultIme()) {
        IMSA_HILOGE("current is not default ime!");
        return ErrorCode::ERROR_NOT_DEFAULT_IME;
    }
    if (!TextConfig::IsPrivateCommandValid(privateCommand)) {
        IMSA_HILOGE("privateCommand is limit 32KB, count limit 5!");
        return ErrorCode::ERROR_INVALID_PRIVATE_COMMAND_SIZE;
    }
    if (TextConfig::IsSystemPrivateCommand(privateCommand)) {
        auto systemChannel = GetSystemCmdChannelProxy();
        if (systemChannel == nullptr) {
            if (!IsSystemPanelSupported()) {
                IMSA_HILOGW("not input method panel!");
                return ErrorCode::NO_ERROR;
            }
            UpdatePrivateCommand(privateCommand);
            IMSA_HILOGE("channel is nullptr!");
            return ErrorCode::ERROR_SYSTEM_CMD_CHANNEL_ERROR;
        }
        Value commandValueMap(privateCommand);
        return systemChannel->SendPrivateCommand(commandValueMap);
    } else {
        auto channel = GetInputDataChannelProxy();
        if (channel == nullptr) {
            IMSA_HILOGE("channel is nullptr!");
            return ErrorCode::ERROR_CLIENT_NULL_POINTER;
        }
        Value commandValueMap(privateCommand);
        return channel->SendPrivateCommand(commandValueMap);
    }
}

void InputMethodAbility::PushPrivateCommand()
{
    if (privateCommandData_.size() == 0) {
        return;
    }
    auto systemChannel = GetSystemCmdChannelProxy();
    if (systemChannel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return;
    }

    std::lock_guard<std::mutex> lock(privateCommandLock_);
    for (const auto &privateCommand : privateCommandData_) {
        Value commandValueMap(privateCommand);
        auto ret = systemChannel->SendPrivateCommand(commandValueMap);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("send privateCommand failed!");
        }
    }
    privateCommandData_.clear();
}

void InputMethodAbility::UpdatePrivateCommand(
    const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    std::lock_guard<std::mutex> lock(privateCommandLock_);
    if (privateCommandData_.size() >= MAX_PRIVATE_COMMAND_QUEUE_SIZE) {
        privateCommandData_.erase(privateCommandData_.begin());
    }
    privateCommandData_.push_back(privateCommand);
}

int32_t InputMethodAbility::ReceivePrivateCommand(
    const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    if (imeListener_ == nullptr) {
        IMSA_HILOGE("imeListener is nullptr!");
        return ErrorCode::ERROR_IME;
    }
    imeListener_->ReceivePrivateCommand(privateCommand);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::SetPreviewText(
    const std::string &text, const Range &range, const AsyncIpcCallBack &callback)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.setPreviewText", 1);
#endif
    InputMethodSyncTrace tracer("IMA_SetPreviewText");
    auto dataChannel = GetInputDataChannelProxyWrap();
    if (dataChannel == nullptr) {
        IMSA_HILOGE("dataChannel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    RangeInner rangeInner = InputMethodTools::GetInstance().RangeToInner(range);
    return dataChannel->SetPreviewText(text, rangeInner, callback);
}

int32_t InputMethodAbility::FinishTextPreview(const AsyncIpcCallBack &callback)
{
#ifdef HIVIEWDFX_API_METRICS_EXT_ENABLE
    HISTOGRAM_BOOLEAN("imekit.inputMethodEngine.finishTextPreview", 1);
#endif
    InputMethodSyncTrace tracer("IMA_FinishTextPreview");
    auto dataChannel = GetInputDataChannelProxyWrap();
    if (dataChannel == nullptr) {
        IMSA_HILOGE("dataChannel is nullptr!");
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    return dataChannel->FinishTextPreview(callback);
}

int32_t InputMethodAbility::GetCallingWindowInfo(CallingWindowInfo &windowInfo)
{
    IMSA_HILOGD("IMA start.");
    auto channel = GetInputDataChannelProxy();
    if (channel == nullptr) {
        IMSA_HILOGE("channel is nullptr!");
        return ErrorCode::ERROR_CLIENT_NOT_FOUND;
    }
    auto panel = GetSoftKeyboardPanel();
    if (panel == nullptr) {
        IMSA_HILOGE("panel not found!");
        return ErrorCode::ERROR_PANEL_NOT_FOUND;
    }
    auto ret = panel->SetCallingWindow(GetInputAttribute().windowId);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to set calling window, ret: %{public}d!", ret);
        return ret;
    }
    ret = panel->GetCallingWindowInfo(windowInfo);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to get calling window, ret: %{public}d", ret);
    }
    return ret;
}

void InputMethodAbility::NotifyPanelStatusInfo(
    PanelStatusInfo &info, std::shared_ptr<InputDataChannelProxy> &channelProxy)
{
    // When panelFlag is CANDIDATE_COLUMN and inputType is not VOICEKB_INPUT, no need to notify.
    info.inputType = GetInputType();
    if (info.panelInfo.panelFlag == PanelFlag::FLG_CANDIDATE_COLUMN && info.inputType != InputType::VOICEKB_INPUT) {
        return;
    }
    if (channelProxy != nullptr) {
        PanelStatusInfoInner inner = InputMethodTools::GetInstance().PanelStatusInfoToInner(info);
        channelProxy->NotifyPanelStatusInfo(inner);
    }

    auto controlChannel = GetInputControlChannel();
    if (controlChannel != nullptr && info.panelInfo.panelType == PanelType::SOFT_KEYBOARD &&
        info.trigger == Trigger::IME_APP && !info.visible && !isImeTerminating_.load()) {
        controlChannel->HideKeyboardSelf();
    }
}

int32_t InputMethodAbility::SendMessage(const ArrayBuffer &arrayBuffer)
{
    int32_t securityMode = INVALID_SECURITY_MODE;
    auto ret = GetSecurityMode(securityMode);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("Get security mode failed!");
        return ret;
    }
    if (!ArrayBuffer::IsSizeValid(arrayBuffer)) {
        IMSA_HILOGE("arrayBuffer size is invalid!");
        return ErrorCode::ERROR_INVALID_ARRAY_BUFFER_SIZE;
    }
    if (securityMode != static_cast<int32_t>(SecurityMode::FULL)) {
        IMSA_HILOGE("Security mode must be FULL!.");
        return ErrorCode::ERROR_SECURITY_MODE_OFF;
    }
    auto dataChannel = GetInputDataChannelProxy();
    if (dataChannel == nullptr) {
        IMSA_HILOGE("datachannel is nullptr.");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return dataChannel->SendMessage(arrayBuffer);
}

int32_t InputMethodAbility::RecvMessage(const ArrayBuffer &arrayBuffer)
{
    int32_t securityMode = -1;
    auto ret = GetSecurityMode(securityMode);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("Get security mode failed!");
        return ret;
    }
    if (securityMode != static_cast<int32_t>(SecurityMode::FULL)) {
        IMSA_HILOGE("Security mode must be FULL!.");
        return ErrorCode::ERROR_SECURITY_MODE_OFF;
    }
    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 InputMethodAbility::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;
}

std::shared_ptr<MsgHandlerCallbackInterface> InputMethodAbility::GetMsgHandlerCallback()
{
    std::lock_guard<decltype(msgHandlerMutex_)> lock(msgHandlerMutex_);
    return msgHandler_;
}

int32_t InputMethodAbility::StartInput(const InputClientInfo &clientInfo, bool isBindFromClient)
{
    auto ret = StartInputInner(clientInfo, isBindFromClient);
    if (ret == ErrorCode::NO_ERROR) {
        return ret;
    }
    ReportImeStartInput(
        static_cast<int32_t>(IInputMethodCoreIpcCode::COMMAND_START_INPUT), ret, clientInfo.isShowKeyboard);
    return ret;
}

void InputMethodAbility::SetBindClientInfo(const InputClientInfo &clientInfo)
{
    std::lock_guard<std::mutex> lock(bindClientInfoLock_);
    bindClientInfo_ = { clientInfo.pid, clientInfo.type, clientInfo.name };
}

HiSysEventClientInfo InputMethodAbility::GetBindClientInfo()
{
    std::lock_guard<std::mutex> lock(bindClientInfoLock_);
    return bindClientInfo_;
}

void InputMethodAbility::ClearBindClientInfo()
{
    std::lock_guard<std::mutex> lock(bindClientInfoLock_);
    bindClientInfo_ = { };
}

void InputMethodAbility::ReportImeStartInput(
    int32_t eventCode, int32_t errCode, bool isShowKeyboard, int64_t consumeTime)
{
    IMSA_HILOGD("HiSysEvent report start:[%{public}d, %{public}d]!", eventCode, errCode);
    auto clientInfo = GetBindClientInfo();
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetPeerName(clientInfo.name)
                        .SetPeerPid(clientInfo.pid)
                        .SetIsShowKeyboard(isShowKeyboard)
                        .SetEventCode(eventCode)
                        .SetErrCode(errCode)
                        .SetImeCbTime(consumeTime)
                        .Build();
    ImaHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::IME_START_INPUT, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end:[%{public}d, %{public}d]!", eventCode, errCode);
}

int32_t InputMethodAbility::OnCallingDisplayIdChanged(uint64_t editorDisplayId, uint64_t keyboardDisplayId)
{
    auto oldKeyboardDisplayId = GetInputAttribute().callingDisplayId;
    auto oldEditorDisplayId = GetInputAttribute().editorDisplayId;
    IMSA_HILOGI("IMA oldEditorDisplayId/oldKeyboardDisplayId/editorDisplayId/keyboardDisplayId: %{public}" PRIu64
                "/%{public}" PRIu64 "/%{public}" PRIu64 "/%{public}" PRIu64 ".",
        oldEditorDisplayId, oldKeyboardDisplayId, editorDisplayId, keyboardDisplayId);
    if (oldKeyboardDisplayId == keyboardDisplayId && editorDisplayId == oldEditorDisplayId) {
        return ErrorCode::NO_ERROR;
    }
    {
        std::lock_guard<std::mutex> lock(inputAttrLock_);
        inputAttribute_.callingDisplayId = keyboardDisplayId;
        inputAttribute_.editorDisplayId = editorDisplayId;
        const auto display = Rosen::DisplayManager::GetInstance().GetDisplayById(keyboardDisplayId);
        if (display != nullptr) {
            inputAttribute_.callingScreenId = display->GetScreenId();
        } else {
            IMSA_HILOGE("failed to get display by id %{public}" PRIu64 ".", keyboardDisplayId);
            inputAttribute_.callingScreenId = 0;
        }
    }
    NotifyInputStartToClients(InputStartScene::DISPLAY_CHANGED);
    if (oldKeyboardDisplayId == keyboardDisplayId) {
        return ErrorCode::NO_ERROR;
    }
    auto panel = GetSoftKeyboardPanel();
    if (panel != nullptr && panel->GetPanelFlag() == PanelFlag::FLG_FIXED) {
        HidePanel(panel, PanelFlag::FLG_FIXED, Trigger::IMF, 0);
    }
    ConfigurationUpdate(keyboardDisplayId);
    if (imeListener_ == nullptr) {
        IMSA_HILOGD("imeListener_ is nullptr!");
        return ErrorCode::NO_ERROR;
    }
    imeListener_->OnCallingDisplayIdChanged(keyboardDisplayId);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::OnSendPrivateData(const std::unordered_map<std::string, PrivateDataValue> &privateCommand)
{
    auto ret = ReceivePrivateCommand(privateCommand);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("OnSendPrivateData failed!");
    }
    IMSA_HILOGD("InputMethodAbility ReceivePrivateCommand success.");
    return ret;
}

bool InputMethodAbility::HandleUnconsumedKey(const std::shared_ptr<MMI::KeyEvent> &keyEvent)
{
    if (keyEvent == nullptr) {
        IMSA_HILOGE("keyEvent nullptr");
        return false;
    }
    auto channel = GetInputDataChannelProxyWrap();
    if (channel == nullptr) {
        IMSA_HILOGD("channel is nullptr!");
        return false;
    }
    if (!GetInputAttribute().needAutoInputNumkey) {
        IMSA_HILOGD("no need");
        return false;
    }
    if (keyEvent->GetKeyAction() != MMI::KeyEvent::KEY_ACTION_DOWN) {
        IMSA_HILOGD("not down key");
        return false;
    }
    if (keyEvent->GetPressedKeys().size() > 1) {
        IMSA_HILOGD("only handle single key");
        return false;
    }
    int32_t keyCode = keyEvent->GetKeyCode();
    std::string inputNumber;
    AsyncIpcCallBack callback = [](int32_t code, const ResponseData &data) {};
    if (MMI::KeyEvent::KEYCODE_0 <= keyCode && keyCode <= MMI::KeyEvent::KEYCODE_9) {
        IMSA_HILOGI("auto input a number");
        channel->InsertText(std::to_string(keyCode - MMI::KeyEvent::KEYCODE_0), callback);
        return true;
    }
    if (!keyEvent->GetFunctionKey(MMI::KeyEvent::NUM_LOCK_FUNCTION_KEY)) {
        IMSA_HILOGD("num lock off");
        return false;
    }
    if (MMI::KeyEvent::KEYCODE_NUMPAD_0 <= keyCode && keyCode <= MMI::KeyEvent::KEYCODE_NUMPAD_9) {
        IMSA_HILOGI("auto input a number");
        channel->InsertText(std::to_string(keyCode - MMI::KeyEvent::KEYCODE_NUMPAD_0), callback);
        return true;
    }
    return false;
}

int32_t InputMethodAbility::OnResponse(uint64_t msgId, int32_t code, const ResponseData &data)
{
    auto channel = GetInputDataChannelProxyWrap();
    if (channel != nullptr) {
        ResponseInfo rspInfo = { code, data };
        channel->HandleResponse(msgId, rspInfo);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::IsCapacitySupport(int32_t capacity, bool &isSupport)
{
    auto proxy = GetImsaProxy();
    if (proxy == nullptr) {
        IMSA_HILOGE("failed to get imsa proxy!");
        return ErrorCode::ERROR_NULL_POINTER;
    }

    return proxy->IsCapacitySupport(capacity, isSupport);
}

bool InputMethodAbility::IsSystemPanelSupported()
{
    if (isSysPanelSupport_ != 0) {
        return isSysPanelSupport_ == 1;
    }
    std::lock_guard<std::mutex> lock(isSysPanelSupportMutex_);
    bool isSupportTemp = false;
    int32_t ret = IsCapacitySupport(static_cast<int32_t>(CapacityType::SYSTEM_PANEL), isSupportTemp);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("IsCapacitySupport failed, ret:%{public}d", ret);
        return false;
    }
    isSysPanelSupport_ = isSupportTemp ? 1 : -1;
    IMSA_HILOGI("isSupportTemp:%{public}d", isSupportTemp);
    return isSupportTemp;
}

int32_t InputMethodAbility::OnNotifyPreemption()
{
    IMSA_HILOGD("start.");
    StopInput(dataChannelObject_, 0);
    isBound_.store(false);
    auto imeListener = GetImeListener();
    if (imeListener == nullptr) {
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    IMSA_HILOGD("notifyPreemption begin.");
    imeListener->NotifyPreemption();
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodAbility::HandleKeyEventResult(
    uint64_t cbId, bool consumeResult, const sptr<IRemoteObject> &channelObject)
{
    IMSA_HILOGD("run in:%{public}" PRIu64 "/%{public}d.", cbId, consumeResult);
    if (channelObject == nullptr) {
        IMSA_HILOGE("channelObject is nullptr:%{public}" PRIu64 ".", cbId);
        return ErrorCode::ERROR_IMA_CHANNEL_NULLPTR;
    }
    auto channel = std::make_shared<InputDataChannelProxy>(channelObject);
    return channel->HandleKeyEventResult(cbId, consumeResult);
}

void InputMethodAbility::RemoveDeathRecipient()
{
    std::lock_guard<std::mutex> lock(abilityLock_);
    if (abilityManager_ == nullptr || deathRecipient_ == nullptr) {
        IMSA_HILOGE("abilityManager_ or deathRecipient_ is nullptr");
        return;
    }

    auto remoteObject = abilityManager_->AsObject();
    if (remoteObject == nullptr) {
        IMSA_HILOGE("remoteObject is nullptr");
        return;
    }

    if (!remoteObject->RemoveDeathRecipient(deathRecipient_)) {
        IMSA_HILOGE("RemoveDeathRecipient failed");
    }
}

void InputMethodAbility::SetSysPanelStatus(const SysPanelStatus &sysPanelStatus)
{
    std::lock_guard<std::mutex> lock(sysPanelStatusLock_);
    sysPanelStatus_ = sysPanelStatus;
}

SysPanelStatus InputMethodAbility::GetSysPanelStatus()
{
    std::lock_guard<std::mutex> lock(sysPanelStatusLock_);
    return sysPanelStatus_;
}

int32_t InputMethodAbility::GetSoftKeyboardInfo(BoundImeInfo &imeInfo)
{
    auto panel = GetSoftKeyboardPanel();
    if (panel == nullptr) {
        imeInfo.status = InputWindowStatus::HIDE;
        return ErrorCode::NO_ERROR;
    }
    imeInfo.panelFlag = panel->GetPanelFlag();
    imeInfo.status = panel->IsShowing() ? InputWindowStatus::SHOW : InputWindowStatus::HIDE;
    imeInfo.displayId = ImfCommonConst::DEFAULT_DISPLAY_ID;
    if (imeInfo.status == InputWindowStatus::HIDE) {
        IMSA_HILOGD("hide status: %{public}s.", imeInfo.ToString().c_str());
        return ErrorCode::NO_ERROR;
    }
    auto ret = panel->GetDisplayId(imeInfo.displayId);
    if (ret != ErrorCode::NO_ERROR) {
        imeInfo.displayId = GetInputAttribute().callingDisplayId;
    }
    IMSA_HILOGD("show status: %{public}s.", imeInfo.ToString().c_str());
    return ErrorCode::NO_ERROR;
}
} // namespace MiscServices
} // namespace OHOS