/*
 * Copyright (C) 2021 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_system_ability.h"

#include <cinttypes>

#include "ability_manager_client.h"
#include "app_mgr_adapter.h"
#include "combination_key.h"
#ifdef IMF_RESTORE_IN_HIGH_CPU_USAGE
#include "cpu_collector_client.h"
#endif
#include "display_adapter.h"
#include "full_ime_info_manager.h"
#include "im_common_event_manager.h"
#include "ime_enabled_info_manager.h"
#include "ime_event_listener_manager.h"
#include "imsa_hisysevent_reporter.h"
#include "input_manager.h"
#include "inputmethod_message_handler.h"
#include "ipc_skeleton.h"
#include "iservice_registry.h"
#include "itypes_util.h"
#include "mem_mgr_client.h"
#include "numkey_apps_manager.h"
#include "os_account_adapter.h"
#include "samgr_adapter.h"
#include "scene_board_judgement.h"
#include "securec.h"
#include "settings_data_utils.h"
#include "system_ability_definition.h"
#include "unordered_map"
#include "variant"
#include "window_monitors_manager.h"
#ifdef IMF_SCREENLOCK_MGR_ENABLE
#include "screenlock_manager.h"
#endif
#include "system_param_adapter.h"
#include "wms_connection_observer.h"
#include "xcollie/xcollie.h"
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
#include "on_demand_start_stop_sa.h"
#endif
#include "ime_state_manager_factory.h"
#include "imf_hook_manager.h"
#include "imf_module_manager.h"
#include "input_method_tools.h"
#include "window_adapter.h"
#include "os_account_manager.h"
#include "res_sched_adapter.h"

 namespace OHOS {
namespace MiscServices {
using namespace MessageID;
using namespace AppExecFwk;
using namespace Security::AccessToken;
using namespace std::chrono;
using namespace HiviewDFX;
using namespace AccountSA;
constexpr uint32_t FATAL_TIMEOUT = 30;    // 30s
constexpr int64_t WARNING_TIMEOUT = 5000; // 5s
constexpr uint32_t MAX_RETRIES = 3;
constexpr uint32_t INTERVALMS_RETRY = 2000; // 2s
REGISTER_SYSTEM_ABILITY_BY_ID(InputMethodSystemAbility, INPUT_METHOD_SYSTEM_ABILITY_ID, true);
constexpr std::int32_t INIT_INTERVAL = 10000L;
constexpr std::int32_t WMS_RETRY_INTERVAL = 60000;  // 1min
constexpr const char *UNDEFINED = "undefined";
static const char *PERMISSION_CONNECT_IME_ABILITY = "ohos.permission.CONNECT_IME_ABILITY";
std::shared_ptr<AppExecFwk::EventHandler> InputMethodSystemAbility::serviceHandler_;
constexpr uint32_t START_SA_TIMEOUT = 6; // 6s
constexpr const char *SELECT_DIALOG_ACTION = "action.system.inputmethodchoose";
constexpr const char *SELECT_DIALOG_HAP = "com.ohos.inputmethodchoosedialog";
constexpr const char *SELECT_DIALOG_ABILITY = "InputMethod";
constexpr const char *IME_MIRROR_CAP_NAME = "ime_mirror";
constexpr uint64_t IMF_HILOG_DOMAIN = 0xD001C10;
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
constexpr const char *UNLOAD_SA_TASK = "unloadInputMethodSaTask";
constexpr int64_t DELAY_UNLOAD_SA_TIME = 20000; // 20s
constexpr int32_t REFUSE_UNLOAD_DELAY_TIME = 1000; // 1s
#endif
const constexpr char *IMMERSIVE_EFFECT_CAP_NAME = "immersive_effect";
const constexpr char *SYSTEM_PANEL_CAP_NAME = "system_panel";
#ifdef IMF_RESTORE_IN_HIGH_CPU_USAGE
const constexpr double PERCENTAGE_MULTIPLIER = 100.0;
const constexpr int32_t CPU_USAGE_HIGH_PERCENT = 70;
#endif
InputMethodSystemAbility::InputMethodSystemAbility(int32_t systemAbilityId, bool runOnCreate)
    : SystemAbility(systemAbilityId, runOnCreate), state_(ServiceRunningState::STATE_NOT_START)
{
}

InputMethodSystemAbility::InputMethodSystemAbility() : state_(ServiceRunningState::STATE_NOT_START)
{
}

InputMethodSystemAbility::~InputMethodSystemAbility()
{
    stop_ = true;
    Message *msg = new (std::nothrow) Message(MessageID::MSG_ID_QUIT_WORKER_THREAD, nullptr);
    if (msg == nullptr) {
        IMSA_HILOGE("new Message failed");
        return;
    }
    auto handler = MessageHandler::Instance();
    if (handler == nullptr) {
        IMSA_HILOGE("handler is nullptr");
        delete msg;
        msg = nullptr;
        return;
    }
    handler->SendMessage(msg);
    if (workThreadHandler.joinable()) {
        workThreadHandler.join();
    }
}

#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
int64_t InputMethodSystemAbility::GetTickCount()
{
    auto now = std::chrono::steady_clock::now();
    auto durationSinceEpoch = now.time_since_epoch();
    return std::chrono::duration_cast<std::chrono::milliseconds>(durationSinceEpoch).count();
}
void InputMethodSystemAbility::ResetDelayUnloadTask(uint32_t code)
{
    auto task = [this]() {
        IMSA_HILOGI("start unload task");
        if (IsImeInUse()) {
            IMSA_HILOGI("ime in use");
            return;
        }
        auto onDemandStartStopSa = std::make_shared<OnDemandStartStopSa>();
        onDemandStartStopSa->UnloadInputMethodSystemAbility();
    };

    static std::mutex lastPostTimeLock;
    std::lock_guard<std::mutex> lock(lastPostTimeLock);
    static int64_t lastPostTime = 0;
    if (code == static_cast<uint32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_RELEASE_INPUT) ||
        code == static_cast<uint32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_REQUEST_HIDE_INPUT)) {
        if (lastPostTime != 0 && (GetTickCount() - lastPostTime) < DELAY_UNLOAD_SA_TIME) {
            IMSA_HILOGD("no need post unload task repeat");
            return;
        }
    }

    if (serviceHandler_ == nullptr) {
        IMSA_HILOGE("serviceHandler_ is nullptr code:%{public}u", code);
        return;
    }

    serviceHandler_->RemoveTask(std::string(UNLOAD_SA_TASK));
    IMSA_HILOGD("post unload task");
    lastPostTime = GetTickCount();
    bool ret = serviceHandler_->PostTask(task, std::string(UNLOAD_SA_TASK), DELAY_UNLOAD_SA_TIME);
    if (!ret) {
        IMSA_HILOGE("post unload task fail code:%{public}u", code);
    }
}
bool InputMethodSystemAbility::IsImeInUse()
{
    auto userSessions = UserSessionManager::GetInstance().GetUserSessions();
    for (const auto &userSession : userSessions) {
        auto session = userSession.second;
        if (session != nullptr && session->IsImeInUse()) {
            return true;
        }
    }
    return false;
}
#endif

int32_t InputMethodSystemAbility::OnRemoteRequest(
    uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    OnDemandStartStopSa::IncreaseProcessingIpcCnt();
#endif
    if (code != static_cast<uint32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_RELEASE_INPUT)) {
        IMSA_HILOGI("IMSA, code = %{public}u, callingPid/Uid/timestamp: %{public}d/%{public}d/%{public}lld", code,
            IPCSkeleton::GetCallingPid(), IPCSkeleton::GetCallingUid(),
            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
                .count());
    }
    auto id = XCollie::GetInstance().SetTimer("IMSA_API[" + std::to_string(code) + "]", FATAL_TIMEOUT, nullptr,
    nullptr, XCOLLIE_FLAG_DEFAULT);
    int64_t startPoint = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    auto ret = InputMethodSystemAbilityStub::OnRemoteRequest(code, data, reply, option);
    int64_t costTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count() - startPoint;
    // log warning when timeout 5s
    if (costTime > WARNING_TIMEOUT) {
    IMSA_HILOGW("code: %{public}d, pid: %{public}d, uid: %{public}d, cost: %{public}" PRId64 "", code,
        IPCSkeleton::GetCallingPid(), IPCSkeleton::GetCallingUid(), costTime);
    }
    XCollie::GetInstance().CancelTimer(id);
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    OnDemandStartStopSa::DecreaseProcessingIpcCnt();
    ResetDelayUnloadTask(code);
#endif
    return ret;
}
// LCOV_EXCL_START
void InputMethodSystemAbility::OnStart()
{
    IMSA_HILOGI("InputMethodSystemAbility::OnStart start.");
    if (!InputMethodSysEvent::GetInstance().StartTimerForReport()) {
        IMSA_HILOGE("start sysevent timer failed!");
    }
    if (state_ == ServiceRunningState::STATE_RUNNING) {
        IMSA_HILOGI("imsa service is already running.");
        return;
    }
    auto id = HiviewDFX::XCollie::GetInstance().SetTimer(
        "IMSA OnStart timeout", START_SA_TIMEOUT, nullptr, nullptr, HiviewDFX::XCOLLIE_FLAG_DEFAULT);
    InitServiceHandler();
    Initialize();
    int32_t ret = Init();
    if (ret != ErrorCode::NO_ERROR) {
        InputMethodSysEvent::GetInstance().ServiceFaultReporter("imf", ret, ImfCommonConst::DEFAULT_USER_ID);
        auto callback = [=]() { Init(); };
        if (serviceHandler_ == nullptr) {
            IMSA_HILOGE("serviceHandler_ is nullptr!");
        } else {
            serviceHandler_->PostTask(callback, INIT_INTERVAL);
        }
        IMSA_HILOGE("init failed. try again 10s later!");
    }
    HiviewDFX::XCollie::GetInstance().CancelTimer(id);
    InitHiTrace();
    InputMethodSyncTrace tracer("InputMethodController Attach trace.");
    InputmethodDump::GetInstance().AddDumpAllMethod([this](int fd) { this->DumpAllMethod(fd); });
    IMSA_HILOGI("start imsa service success.");
}

bool InputMethodSystemAbility::IsValidBundleName(const std::string &bundleName)
{
    if (bundleName.empty()) {
        IMSA_HILOGE("bundleName is empty.");
        return false;
    }
    std::vector<Property> props;
    auto ret = ListInputMethod(InputMethodStatus::ALL, props, ImfCommonConst::DEFAULT_USER_ID);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("ListInputMethod failed, ret=%{public}d", ret);
        return false;
    }

    return std::any_of(props.begin(), props.end(), [&bundleName](const auto &prop) {
        return prop.name == bundleName;
    });
}

std::string InputMethodSystemAbility::GetRestoreBundleName(MessageParcel &data)
{
    std::string jsonString = data.ReadString();
    if (jsonString.empty()) {
        IMSA_HILOGE("jsonString is empty.");
        return "";
    }
    IMSA_HILOGI("restore jsonString=%{public}s", jsonString.c_str());

    cJSON *root = cJSON_Parse(jsonString.c_str());
    if (root == NULL) {
        IMSA_HILOGE("cJSON_Parse fail");
        return "";
    }
    std::string bundleName = "";
    cJSON *item = NULL;
    cJSON_ArrayForEach(item, root)
    {
        cJSON *type = cJSON_GetObjectItem(item, "type");
        cJSON *detail = cJSON_GetObjectItem(item, "detail");
        if (type == NULL || detail == NULL || type->valuestring == NULL || detail->valuestring == NULL) {
            IMSA_HILOGE("type or detail is null");
            continue;
        }

        if (strcmp(type->valuestring, "default_input_method") == 0) {
            bundleName = std::string(detail->valuestring);
            break;
        }
    }
    cJSON_Delete(root);
    return bundleName;
}

int32_t InputMethodSystemAbility::RestoreInputmethod(std::string &bundleName)
{
    Property propertyData;
    GetCurrentInputMethod(GetCallingUserId(), propertyData);
    auto prop = std::make_shared<Property>(propertyData);
    std::string currentInputMethod = prop->name;
    if (currentInputMethod == bundleName) {
        IMSA_HILOGW("currentInputMethod=%{public}s, has been set", currentInputMethod.c_str());
        return ErrorCode::NO_ERROR;
    }

    int32_t userId = GetCallingUserId();
    auto defaultIme = ImeInfoInquirer::GetInstance().GetDefaultIme();
    if (defaultIme.bundleName != bundleName) {
        auto result = EnableIme(userId, bundleName);
        if (result != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("EnableIme failed");
            return ErrorCode::ERROR_ENABLE_IME;
        }
    }

    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("session[ userId=%{public}d ] is nullptr", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    SwitchInfo switchInfo = { std::chrono::system_clock::now(), bundleName, "" };
    switchInfo.timestamp = std::chrono::system_clock::now();
    session->GetSwitchQueue().Push(switchInfo);
    auto ret = OnSwitchInputMethod(userId, switchInfo, SwitchTrigger::IMSA);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("SwitchInputMethod failed, ret=%{public}d.", ret);
        return ret;
    }
    IMSA_HILOGI("restore success");
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnExtension(const std::string &extension, MessageParcel &data, MessageParcel &reply)
{
    IMSA_HILOGI("extension=%{public}s", extension.c_str());
    if (extension == "restore") {
        int32_t fd = data.ReadFileDescriptor();
        if (fd >= 0) {
            fdsan_exchange_owner_tag(fd, 0, IMF_HILOG_DOMAIN);
            fdsan_close_with_tag(fd, IMF_HILOG_DOMAIN);
        }
        std::string bundleName = GetRestoreBundleName(data);
        if (!IsValidBundleName(bundleName)) {
            IMSA_HILOGE("bundleName=%{public}s is invalid", bundleName.c_str());
            return ErrorCode::ERROR_BAD_PARAMETERS;
        }

        return RestoreInputmethod(bundleName);
    }
    return 0;
}
// LCOV_EXCL_STOP
int InputMethodSystemAbility::Dump(int fd, const std::vector<std::u16string> &args)
{
    IMSA_HILOGD("InputMethodSystemAbility::Dump start.");
    std::vector<std::string> argsStr;
    for (auto item : args) {
        argsStr.emplace_back(Str16ToStr8(item));
    }
    InputmethodDump::GetInstance().Dump(fd, argsStr);
    return ERR_OK;
}

void InputMethodSystemAbility::DumpAllMethod(int fd)
{
    IMSA_HILOGD("InputMethodSystemAbility::DumpAllMethod start.");
    auto ids = OsAccountAdapter::QueryActiveOsAccountIds();
    if (ids.empty()) {
        dprintf(fd, "\n - InputMethodSystemAbility::DumpAllMethod get Active Id failed.\n");
        return;
    }
    dprintf(fd, "\n - DumpAllMethod get Active Id succeed,count=%zu,", ids.size());
    for (auto id : ids) {
        const auto &params = ImeInfoInquirer::GetInstance().GetDumpInfo(id);
        if (params.empty()) {
            IMSA_HILOGD("userId: %{public}d the IME properties is empty.", id);
            dprintf(fd, "\n - The IME properties about the Active Id %d is empty.\n", id);
            continue;
        }
        dprintf(fd, "\n - The Active Id:%d get input method:\n%s\n", id, params.c_str());
    }
    IMSA_HILOGD("InputMethodSystemAbility::DumpAllMethod end.");
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::Init()
{
    IMSA_HILOGI("publish start");
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    ImeInfoInquirer::GetInstance().InitSystemConfig();
    ImeInfoInquirer::GetInstance().InitProductConfig();
    bool isSuccess = Publish(this);
    if (!isSuccess) {
        IMSA_HILOGE("publish failed");
        return -1;
    }
    state_ = ServiceRunningState::STATE_RUNNING;
    ResetDelayUnloadTask(static_cast<uint32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_RELEASE_INPUT));
    IMSA_HILOGI("publish success");
#else
    bool isSuccess = Publish(this);
    if (!isSuccess) {
        IMSA_HILOGE("publish failed");
        return -1;
    }
    IMSA_HILOGI("publish success");
    state_ = ServiceRunningState::STATE_RUNNING;
    ImeInfoInquirer::GetInstance().InitSystemConfig();
    ImeInfoInquirer::GetInstance().InitProductConfig();
    ImeInfoInquirer::GetInstance().InitDynamicStartImeCfg();
    ImeStateManagerFactory::GetInstance().SetDynamicStartIme(ImeInfoInquirer::GetInstance().IsDynamicStartIme());
#endif
    InitMonitors();
    return ErrorCode::NO_ERROR;
}

void InputMethodSystemAbility::InitUserInfo(int32_t userId, uint64_t displayId)
{
    UserSessionManager::GetInstance().AddUserSession(userId);
    uint64_t displayGroupId = ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID;
    int32_t ret = WindowAdapter::GetInstance().GetDisplayGroupIdWithRetry(displayId, userId, displayGroupId);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetDisplayGroupIdWithRetry failed, ret: %{public}d", ret);
    }
    if (ret == ErrorCode::NO_ERROR && displayGroupId == ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID) {
        NumkeyAppsManager::GetInstance().OnUserSwitched(userId);
    }
}

void InputMethodSystemAbility::UpdateUserInfo(int32_t userId, uint64_t displayId)
{
    IMSA_HILOGI("display: %{public}" PRIu64 ", userId switch to %{public}d.", displayId, userId);
    // update user-separated data
    FullImeInfoManager::GetInstance().Switch(userId, displayId);
    UserSessionManager::GetInstance().AddUserSession(userId);
    uint64_t displayGroupId = ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID;
    int32_t ret = WindowAdapter::GetInstance().GetDisplayGroupIdWithRetry(displayId, userId, displayGroupId);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetDisplayGroupIdWithRetry failed, ret: %{public}d", ret);
    }
    if (ret == ErrorCode::NO_ERROR && displayGroupId == ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID) {
        NumkeyAppsManager::GetInstance().OnUserSwitched(userId);
    }
}

int32_t InputMethodSystemAbility::OnIdle(const SystemAbilityOnDemandReason &idleReason)
{
    IMSA_HILOGI("OnIdle start.");
    (void)idleReason;
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    if (OnDemandStartStopSa::IsSaBusy() || IsImeInUse()) {
        IMSA_HILOGW("sa is busy, refuse stop imsa.");
        return REFUSE_UNLOAD_DELAY_TIME;
    }
#endif
    return 0;
}

void InputMethodSystemAbility::OnStop()
{
    IMSA_HILOGI("OnStop start.");
    ImeStateManager::SetEventHandler(nullptr);
    UserSessionManager::GetInstance().SetEventHandler(nullptr);
    ImeEnabledInfoManager::GetInstance().SetEventHandler(nullptr);
    serviceHandler_ = nullptr;
    state_ = ServiceRunningState::STATE_NOT_START;
    Memory::MemMgrClient::GetInstance().NotifyProcessStatus(getpid(), 1, 0, INPUT_METHOD_SYSTEM_ABILITY_ID);
    NumkeyAppsManager::GetInstance().Release();
    SettingsDataUtils::GetInstance().Release();
    ImfModuleMgr::GetInstance().Destroy(ImfModuleMgr::IMF_EXT_MODULE_PATH);
}
// LCOV_EXCL_STOP
void InputMethodSystemAbility::InitServiceHandler()
{
    IMSA_HILOGI("InitServiceHandler start.");
    if (serviceHandler_ != nullptr) {
        IMSA_HILOGE("InputMethodSystemAbility already init!");
        return;
    }
    std::shared_ptr<AppExecFwk::EventRunner> runner = AppExecFwk::EventRunner::Create("OS_InputMethodSystemAbility");
    serviceHandler_ = std::make_shared<AppExecFwk::EventHandler>(runner);
    ImeStateManager::SetEventHandler(serviceHandler_);
    ImeEnabledInfoManager::GetInstance().SetEventHandler(serviceHandler_);
    IMSA_HILOGI("InitServiceHandler succeeded.");
}

/**
 * Initialization of Input method management service
 * \n It's called after the service starts, before any transaction.
 */
void InputMethodSystemAbility::Initialize()
{
    IMSA_HILOGI("InputMethodSystemAbility::Initialize.");
    // init work thread to handle the messages
    workThreadHandler = std::thread([this] { this->WorkThread(); });
    identityChecker_ = std::make_shared<IdentityCheckerImpl>();
    UserSessionManager::GetInstance().SetEventHandler(serviceHandler_);
    UserSessionManager::GetInstance().AddUserSession(ImfCommonConst::START_USER_ID);
    IMSA_HILOGI("start get scene board enable status");
    ImeEnabledInfoManager::GetInstance().SetCurrentImeStatusChangedHandler(
        [this](int32_t userId, const std::string &bundleName, EnabledStatus newStatus) {
            OnCurrentImeStatusChanged(userId, bundleName, newStatus);
        });
    isScbEnable_.store(Rosen::SceneBoardJudgement::IsSceneBoardEnabled());
    IMSA_HILOGI("Initialize end");
}
// LCOV_EXCL_START
void InputMethodSystemAbility::ResetAllImes()
{
    RestartAllForegroundImes();
    StopAllBackgroundImes();
}

void InputMethodSystemAbility::RestartAllForegroundImes()
{
#ifdef IMF_ON_DEMAND_START_STOP_SA_ENABLE
    IMSA_HILOGD("dynamic start sa, no need");
#else
    if (ImeStateManagerFactory::GetInstance().GetDynamicStartIme()) {
        IMSA_HILOGD("dynamic start ime, no need");
        return;
    }
    auto accounts = OsAccountAdapter::GetForegroundOsAccountIds();
    for (auto account : accounts) {
        auto session = UserSessionManager::GetInstance().GetUserSession(account);
        if (session == nullptr) {
            UserSessionManager::GetInstance().AddUserSession(account);
            session = UserSessionManager::GetInstance().GetUserSession(account);
        }
        if (session != nullptr) {
            session->AddRestartIme();
        }
    }
#endif
}

void InputMethodSystemAbility::StopAllBackgroundImes()
{
    auto task = [this]() {
        auto sessions = UserSessionManager::GetInstance().GetUserSessions();
        for (const auto &tempSession : sessions) {
            if (!OsAccountAdapter::IsOsAccountForeground(tempSession.first)) {
                tempSession.second->StopCurrentIme();
            }
        }
    };
    if (serviceHandler_ == nullptr) {
        return;
    }
    serviceHandler_->PostTask(task, __FUNCTION__, 0, AppExecFwk::EventQueue::Priority::IMMEDIATE);
}

std::shared_ptr<PerUserSession> InputMethodSystemAbility::GetSessionFromMsg(const Message *msg)
{
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("Aborted! Message is nullptr!");
        return nullptr;
    }
    auto userId = msg->msgContent_->ReadInt32();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return nullptr;
    }
    return session;
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::PrepareForOperateKeyboard(
    std::shared_ptr<PerUserSession> &session, uint32_t windowId, const sptr<IRemoteObject> &abilityToken)
{
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto pid = IPCSkeleton::GetCallingPid();
    auto userId = GetCallingUserId();
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    auto focusedRet = identityChecker_->IsFocused(pid, tokenId, userId, windowId, abilityToken);
    if (focusedRet.first) {
        return ErrorCode::NO_ERROR;
    }
    auto isBroker = identityChecker_->IsBroker(tokenId);
    if (!isBroker) {
        return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
    }
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::SwitchByCondition(
    const Condition &condition, const std::shared_ptr<ImeInfo> &info, int32_t userId)
{
    if (info == nullptr) {
        IMSA_HILOGE("info is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto target = ImeInfoInquirer::GetInstance().FindTargetSubtypeByCondition(info->subProps, condition);
    if (target == nullptr) {
        IMSA_HILOGE("target is empty!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    SwitchInfo switchInfo = { std::chrono::system_clock::now(), target->name, target->id };
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    session->GetSwitchQueue().Push(switchInfo);
    return OnSwitchInputMethod(userId, switchInfo, SwitchTrigger::IMSA);
}
// LCOV_EXCL_STOP
void InputMethodSystemAbility::SubscribeCommonEvent()
{
    sptr<ImCommonEventManager> imCommonEventManager = ImCommonEventManager::GetInstance();
    bool isSuccess = imCommonEventManager->SubscribeEvent();
    if (isSuccess) {
        IMSA_HILOGI("initialize subscribe service event success.");
        return;
    }

    IMSA_HILOGE("failed, try again 10s later!");
    auto callback = [this]() { SubscribeCommonEvent(); };
    serviceHandler_->PostTask(callback, INIT_INTERVAL);
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::PrepareInput(
    int32_t userId, InputClientInfo &clientInfo, const FocusedInfo &focusedInfo)
{
    InputMethodSyncTrace tracer("InputMethodSystemAbility PrepareInput");
    auto ret = GenerateClientInfo(userId, clientInfo, focusedInfo);
    if (ret != ErrorCode::NO_ERROR) {
        return ret;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return session->OnPrepareInput(clientInfo);
}

int32_t InputMethodSystemAbility::GenerateClientInfo(
    int32_t userId, InputClientInfo &clientInfo, const FocusedInfo &focusedInfo)
{
    if (clientInfo.client == nullptr || clientInfo.channel == nullptr) {
        IMSA_HILOGE("client or channel is nullptr!");
        return ErrorCode::ERROR_IMSA_NULLPTR;
    }
    auto deathRecipient = new (std::nothrow) InputDeathRecipient();
    if (deathRecipient == nullptr) {
        IMSA_HILOGE("failed to new deathRecipient!");
        return ErrorCode::ERROR_IMSA_MALLOC_FAILED;
    }
    clientInfo.pid = IPCSkeleton::GetCallingPid();
    clientInfo.uid = IPCSkeleton::GetCallingUid();
    clientInfo.userID = userId;
    clientInfo.deathRecipient = deathRecipient;
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (focusedInfo.uiExtensionHostPid != ImfCommonConst::INVALID_PID) {
        clientInfo.uiExtensionTokenId = tokenId;
        clientInfo.uiExtensionHostPid = focusedInfo.uiExtensionHostPid;
    } else {
        clientInfo.uiExtensionTokenId = ImfCommonConst::IMF_INVALID_TOKENID;
        clientInfo.uiExtensionHostPid = ImfCommonConst::INVALID_PID;
    }
    clientInfo.config.inputAttribute.bundleName = identityChecker_->GetBundleNameByToken(tokenId);
    auto callingDisplayId = identityChecker_->GetDisplayIdByWindowId(clientInfo.config.windowId, userId);
    clientInfo.config.privateCommand.insert_or_assign(
        "displayId", PrivateDataValue(static_cast<int32_t>(callingDisplayId)));
    clientInfo.name = ImfHiSysEventUtil::GetAppName(tokenId);
    clientInfo.clientGroupId = focusedInfo.displayGroupId;
    clientInfo.config.inputAttribute.editorWindowId = focusedInfo.windowId;
    clientInfo.config.inputAttribute.editorDisplayId = focusedInfo.displayId;
    clientInfo.config.inputAttribute.windowId = focusedInfo.keyboardWindowId;
    clientInfo.config.inputAttribute.callingDisplayId = focusedInfo.keyboardDisplayId;
    clientInfo.config.inputAttribute.displayGroupId = focusedInfo.keyboardDisplayGroupId;
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session != nullptr) {
        clientInfo.config.inputAttribute.needAutoInputNumkey =
            session->IsNumkeyAutoInputApp(clientInfo.config.inputAttribute.bundleName);
    }
    IMSA_HILOGD("result:%{public}s,wid:%{public}d", clientInfo.config.inputAttribute.ToString().c_str(),
        clientInfo.config.windowId);
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::ReleaseInput(
    const sptr<IInputClient> &client, uint32_t sessionId, int32_t clientSessionId)
{
    if (client == nullptr) {
        IMSA_HILOGE("client is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->OnReleaseInput(client, sessionId, clientSessionId);
}

void InputMethodSystemAbility::IncreaseAttachCount()
{
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("get session failed:%{public}d", userId);
        return;
    }

    session->IncreaseAttachCount();
}

void InputMethodSystemAbility::DecreaseAttachCount()
{
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("get session failed:%{public}d", userId);
        return;
    }

    session->DecreaseAttachCount();
}

ErrCode InputMethodSystemAbility::StartInput(const InputClientInfoInner &inputClientInfoInner,
    std::vector<sptr<IRemoteObject>> &agents, std::vector<BindImeInfo> &imeInfos)
{
    AttachStateGuard guard(*this);
    auto userId = GetCallingUserId();
    InputClientInfo inputClientInfo = InputMethodTools::GetInstance().InnerToInputClientInfo(inputClientInfoInner);
    bool failedByUnavailableIme = false;
    auto ret = StartInputInner(inputClientInfo, agents, imeInfos, failedByUnavailableIme);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session != nullptr) {
        session->SetAttachFailedByUnavailableImeFlag(failedByUnavailableIme);
    }
    std::string bundleName = "";
    if (!imeInfos.empty()) {
        bundleName = imeInfos[0].bundleName;
    } else {
        bundleName = GetCurrentImeInfoForHiSysEvent(GetCallingUserId()).second;
    }
    IMSA_HILOGD("HiSysEvent report start!");
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetPeerName(ImfHiSysEventUtil::GetAppName(IPCSkeleton::GetCallingTokenID()))
                        .SetPeerPid(IPCSkeleton::GetCallingPid())
                        .SetPeerUserId(userId)
                        .SetClientType(inputClientInfo.type)
                        .SetInputPattern(inputClientInfo.attribute.inputPattern)
                        .SetIsShowKeyboard(inputClientInfo.isShowKeyboard)
                        .SetImeName(bundleName)
                        .SetErrCode(ret)
                        .Build();
    ImsaHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_ATTACH, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", ret);
    return ret;
}

int32_t InputMethodSystemAbility::StartInputInner(InputClientInfo &inputClientInfo,
    std::vector<sptr<IRemoteObject>> &agents, std::vector<BindImeInfo> &imeInfos, bool &failedByUnavailableIme)
{
    failedByUnavailableIme = false;
    auto userId = GetCallingUserId();
    auto pid = IPCSkeleton::GetCallingPid();
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto checkRet =
        IsFocusedOrBroker(pid, tokenId, userId, inputClientInfo.config.windowId, inputClientInfo.config.abilityToken);
    if (!checkRet.first) {
        return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    auto displayId = checkRet.second.displayId;
    if (session->GetCurrentClientPid(displayId) != IPCSkeleton::GetCallingPid() &&
        session->GetInactiveClientPid(displayId) != IPCSkeleton::GetCallingPid()) {
        // notify inputStart when caller pid different from both current client and inactive client
        inputClientInfo.isNotifyInputStart = true;
    }
    if (session->CheckPwdInputPatternConv(inputClientInfo, displayId)) {
        inputClientInfo.needHide = true;
        inputClientInfo.isNotifyInputStart = true;
    }
    int32_t ret = PrepareInput(userId, inputClientInfo, checkRet.second);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to PrepareInput!");
        return ret;
    }
    auto imeToBind = session->GetReadyImeDataToBind(displayId);
    if (imeToBind == nullptr || imeToBind->IsRealIme()) {
        if (imeToBind == nullptr) {
            InputTypeManager::GetInstance().Set(false);
        }
        ret = EnsureImeAvailable(userId, inputClientInfo);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("%{public}d failed to EnsureImeAvailable!", userId);
            failedByUnavailableIme = true;
            return ret;
        }
    }
    return session->OnStartInput(inputClientInfo, agents, imeInfos);
}

std::pair<bool, FocusedInfo> InputMethodSystemAbility::IsFocusedOrBroker(int64_t callingPid, uint32_t callingTokenId,
    int32_t userId, uint32_t windowId, const sptr<IRemoteObject> &abilityToken)
{
    auto focusedRet = identityChecker_->IsFocused(callingPid, callingTokenId, userId, windowId, abilityToken);
    if (focusedRet.first) {
        return focusedRet;
    }
    return identityChecker_->CheckBroker(callingTokenId, userId);
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::EnsureImeAvailable(int32_t userId, InputClientInfo &inputClientInfo)
{
    IMSA_HILOGI("SecurityImeFlag: %{public}d, IsSameTextInput: %{public}d, IsStarted: %{public}d.",
        inputClientInfo.config.inputAttribute.IsSecurityImeFlag(),
        !inputClientInfo.isNotifyInputStart,
        InputTypeManager::GetInstance().IsStarted());
    if (inputClientInfo.config.inputAttribute.IsSecurityImeFlag()) {
        return StartSecurityIme(userId, inputClientInfo);
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    if (!inputClientInfo.isNotifyInputStart && InputTypeManager::GetInstance().IsStarted()) {
        IMSA_HILOGD("NormalFlag, same textField, input type started, not deal.");
        return ErrorCode::NO_ERROR;
    }
    if (inputClientInfo.isNotifyInputStart && InputTypeManager::GetInstance().IsStarted()) {
        IMSA_HILOGD("NormalFlag, diff textField, input type started, restore.");
        session->RestoreCurrentImeSubType();
    }
#ifdef IMF_SCREENLOCK_MGR_ENABLE
    if (session->IsDeviceLockAndScreenLocked()) {
        std::string ime;
        if (GetScreenLockIme(userId, ime) != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("not ime screenlocked");
            return ErrorCode::ERROR_IMSA_IME_TO_START_NULLPTR;
        }
        ImeEnabledInfoManager::GetInstance().SetTmpIme(userId, ime);
        return session->StartUserSpecifiedIme();
    } else {
        ImeEnabledInfoManager::GetInstance().SetTmpIme(userId, "");
    }
#endif
    IMSA_HILOGD("Screen is unLocked!");
    if (session->IsPreconfiguredDefaultImeSpecified(inputClientInfo)) {
        auto [ret, status] = session->StartPreconfiguredDefaultIme();
        return ret;
    }
    return session->StartUserSpecifiedIme();
}

ErrCode InputMethodSystemAbility::IsRestrictedDefaultImeByDisplay(uint64_t displayId, bool &resultValue)
{
    resultValue = ImeInfoInquirer::GetInstance().IsRestrictedDefaultImeByDisplay(displayId);
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::ShowInputInner(
    sptr<IInputClient> client, uint32_t windowId, int32_t requestKeyboardReason)
{
    std::shared_ptr<PerUserSession> session = nullptr;
    auto result = PrepareForOperateKeyboard(session, windowId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("prepare failed:%{public}d.", result);
        return result;
    }
    if (client == nullptr) {
        IMSA_HILOGE("client is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return session->OnShowInput(client, requestKeyboardReason);
}

ErrCode InputMethodSystemAbility::HideInput(const sptr<IInputClient> &client, uint32_t windowId)
{
    std::shared_ptr<PerUserSession> session = nullptr;
    auto result = PrepareForOperateKeyboard(session, windowId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("prepare failed:%{public}d.", result);
        return result;
    }
    if (client == nullptr) {
        IMSA_HILOGE("client is nullptr!");
        return ErrorCode::ERROR_CLIENT_NULL_POINTER;
    }
    return session->OnHideInput(client);
}

ErrCode InputMethodSystemAbility::StopInputSession(uint32_t windowId)
{
    auto pid = IPCSkeleton::GetCallingPid();
    std::shared_ptr<PerUserSession> session = nullptr;
    auto result = PrepareForOperateKeyboard(session, windowId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("prepare failed:%{public}d.", result);
        return result;
    }
    auto [clientGroup, clientInfo] = session->GetClientBySelfPidOrHostPid(pid);
    if (clientInfo == nullptr) {
        IMSA_HILOGE("client group not found");
        return ErrorCode::ERROR_CLIENT_NOT_FOUND;
    }
    return session->OnHideCurrentInput(clientInfo->clientGroupId);
}

ErrCode InputMethodSystemAbility::RequestHideInput(uint32_t windowId, uint64_t displayId, bool isFocusTriggered,
    int32_t userId)
{
    IMSA_HILOGI("isFocusTriggered/windowId/displayId/userId:%{public}d/%{public}d/%{public}" PRIu64 "/%{public}d.",
        isFocusTriggered, windowId, displayId, userId);
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto pid = IPCSkeleton::GetCallingPid();
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(outputUserId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", outputUserId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto [isFocused, focusedInfo] = identityChecker_->IsFocused(pid, tokenId, outputUserId, windowId);
    std::string callerBundleName;
    if (isFocused) {
        IMSA_HILOGD("caller focused, bundleName: %{public}s", callerBundleName.c_str());
        callerBundleName = identityChecker_->GetBundleNameByToken(tokenId);
    } else {
        if (isFocusTriggered) {
            IMSA_HILOGD("not focused");
            return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
        }
        if (!identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
            IMSA_HILOGD("permission denied");
            return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
        }
    }
    return session->OnRequestHideInput(
        WindowAdapter::GetDisplayIdWithCorrect(windowId, displayId, outputUserId), callerBundleName);
}

ErrCode InputMethodSystemAbility::SetCoreAndAgent(const sptr<IInputMethodCore> &core, const sptr<IRemoteObject> &agent)
{
    IMSA_HILOGD("InputMethodSystemAbility start.");
    auto userId = GetCallingUserId();
    auto tokenId = GetCallingTokenID();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!IsCurrentIme(userId, tokenId)) {
        IMSA_HILOGE("not current ime, userId:%{public}d", userId);
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    return session->OnSetCoreAndAgent(core, agent);
}
// LCOV_EXCL_START
ErrCode InputMethodSystemAbility::RegisterProxyIme(
    uint64_t displayId, const sptr<IInputMethodCore> &core, const sptr<IRemoteObject> &agent)
{
    auto uid = IPCSkeleton::GetCallingUid();
    if (uid == ImfCommonConst::AI_PROXY_IME && !ImeInfoInquirer::GetInstance().IsEnableAppAgent()) {
        IMSA_HILOGE("current device does not support app agent");
        return ErrorCode::ERROR_DEVICE_UNSUPPORTED;
    }
    if (!identityChecker_->IsValidVirtualIme(uid)) {
        IMSA_HILOGE("not proxy sa");
        return ErrorCode::ERROR_NOT_AI_APP_IME;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->OnRegisterProxyIme(displayId, core, agent, IPCSkeleton::GetCallingPid(), uid);
}

ErrCode InputMethodSystemAbility::UnregisterProxyIme(uint64_t displayId, int32_t type)
{
    if (type < static_cast<int32_t>(UnRegisteredType::BEGIN) || type > static_cast<int32_t>(UnRegisteredType::END)) {
        IMSA_HILOGE("Invalid type parameter: %{public}d, out of range.", type);
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    auto uid = IPCSkeleton::GetCallingUid();
    if (uid == ImfCommonConst::AI_PROXY_IME && !ImeInfoInquirer::GetInstance().IsEnableAppAgent()) {
        IMSA_HILOGE("current device does not support app agent");
        return ErrorCode::ERROR_DEVICE_UNSUPPORTED;
    }
    if (!identityChecker_->IsValidVirtualIme(uid)) {
        IMSA_HILOGE("not agent sa");
        return ErrorCode::ERROR_NOT_AI_APP_IME;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->OnUnregisterProxyIme(displayId, IPCSkeleton::GetCallingPid(),
        static_cast<UnRegisteredType>(type));
}

ErrCode InputMethodSystemAbility::BindImeMirror(const sptr<IInputMethodCore> &core, const sptr<IRemoteObject> &agent)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }

    if (!ImeInfoInquirer::GetInstance().IsCapacitySupport(IME_MIRROR_CAP_NAME)) {
        IMSA_HILOGE("ime_mirror is not supported");
        return ErrorCode::ERROR_DEVICE_UNSUPPORTED;
    }

    if (!identityChecker_->IsValidVirtualIme(IPCSkeleton::GetCallingUid())) {
        IMSA_HILOGE("not agent sa");
        return ErrorCode::ERROR_NOT_AI_APP_IME;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return session->OnBindImeMirror(core, agent);
}

ErrCode InputMethodSystemAbility::UnbindImeMirror()
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!ImeInfoInquirer::GetInstance().IsCapacitySupport(IME_MIRROR_CAP_NAME)) {
        IMSA_HILOGE("ime_mirror is not supported");
        return ErrorCode::ERROR_DEVICE_UNSUPPORTED;
    }
    if (!identityChecker_->IsValidVirtualIme(IPCSkeleton::GetCallingUid())) {
        IMSA_HILOGE("not agent sa");
        return ErrorCode::ERROR_NOT_AI_APP_IME;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->OnUnbindImeMirror();
}
// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::InitConnect()
{
    IMSA_HILOGD("InputMethodSystemAbility init connect.");
    auto userId = GetCallingUserId();
    auto tokenId = GetCallingTokenID();
    auto pid = IPCSkeleton::GetCallingPid();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!IsCurrentIme(userId, tokenId)) {
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    return session->InitConnect(pid);
}

ErrCode InputMethodSystemAbility::HideCurrentInput()
{
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto ret = identityChecker_->IsBroker(tokenId);
    if (!ret) {
        auto hasPermission = identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY));
        if (!hasPermission) {
            return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
        }
    }
    return session->OnHideCurrentInput(ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID);
}

ErrCode InputMethodSystemAbility::HideCurrentInput(uint64_t displayId)
{
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    if (!WindowAdapter::GetInstance().IsDisplayIdExist(displayId, userId)) {
        IMSA_HILOGE("displayId:%{public}" PRIu64 " not exist!", displayId);
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (identityChecker_->IsBroker(tokenId)) {
        return session->OnHideCurrentInputInTargetDisplay(displayId);
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    if (!identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
        IMSA_HILOGE("not has connect ime ability permission!");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    return session->OnHideCurrentInputInTargetDisplay(displayId);
}

ErrCode InputMethodSystemAbility::ShowCurrentInputInner()
{
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    auto ret = identityChecker_->IsBroker(tokenId);
    if (!ret) {
        auto hasPermission = identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY));
        if (!hasPermission) {
            return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
        }
    }
    return session->OnShowCurrentInput(ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID);
}

int32_t InputMethodSystemAbility::ShowCurrentInputInner(uint64_t displayId)
{
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    if (!WindowAdapter::GetInstance().IsDisplayIdExist(displayId, userId)) {
        IMSA_HILOGE("displayId:%{public}" PRIu64 " not exist!", displayId);
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (identityChecker_->IsBroker(tokenId)) {
        return session->OnShowCurrentInputInTargetDisplay(displayId);
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    if (!identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
        IMSA_HILOGE("not has connect ime ability permission!");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    return session->OnShowCurrentInputInTargetDisplay(displayId);
}

ErrCode InputMethodSystemAbility::PanelStatusChange(uint32_t status, const ImeWindowInfo &info)
{
    auto userId = GetCallingUserId();
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (!IsCurrentIme(userId, tokenId)) {
        IMSA_HILOGE("not current ime!");
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    auto commonEventManager = ImCommonEventManager::GetInstance();
    if (commonEventManager != nullptr) {
        auto ret = commonEventManager->PublishPanelStatusChangeEvent(
            userId, static_cast<InputWindowStatus>(status), info);
        IMSA_HILOGD("public panel status change event: %{public}d", ret);
    }
    ResSchedAdapter::NotifyPanelStatus(static_cast<InputWindowStatus>(status) == InputWindowStatus::SHOW);
    return ImeEventListenerManager::GetInstance().NotifyPanelStatusChange(
        userId, static_cast<InputWindowStatus>(status), info);
}

ErrCode InputMethodSystemAbility::NotifyInputStart(const InputStartInfo &inputStartInfo)
{
    IMSA_HILOGD("IMSA enter!");
    auto userId = GetCallingUserId();
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (!IsCurrentIme(userId, tokenId)) {
        IMSA_HILOGE("not current ime!");
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    auto finalInfo = inputStartInfo;
    finalInfo.userId = userId;
    return ImeEventListenerManager::GetInstance().NotifyInputStart(userId, finalInfo);
}

ErrCode InputMethodSystemAbility::NotifyInputStop(const InputStopInfo &inputStopInfo)
{
    IMSA_HILOGD("IMSA enter!");
    auto userId = GetCallingUserId();
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (!IsCurrentIme(userId, tokenId)) {
        IMSA_HILOGE("not current ime!");
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    auto finalInfo = inputStopInfo;
    finalInfo.userId = userId;
    return ImeEventListenerManager::GetInstance().NotifyInputStop(userId, finalInfo);
}

ErrCode InputMethodSystemAbility::NotifySoftKeyBoardInfoChanged(
    const BoundImeInfo &oldImeInfo, const BoundImeInfo &newImeInfo)
{
    auto userId = GetCallingUserId();
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (!IsCurrentIme(userId, tokenId)) {
        IMSA_HILOGE("not current ime!");
        return ErrorCode::ERROR_NOT_CURRENT_IME;
    }
    return ImeEventListenerManager::GetInstance().NotifySoftKeyBoardInfoChanged(userId, oldImeInfo, newImeInfo);
}

ErrCode InputMethodSystemAbility::GetSoftKeyboardInfo(int32_t userId, BoundImeInfo &imeInfo)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())
        && !identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(outputUserId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", outputUserId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    auto ret = session->GetSoftKeyboardInfo(imeInfo);
    IMSA_HILOGD("userId/imeInfo:%{public}d/%{public}s!", userId, imeInfo.ToString().c_str());
    return ret;
}

ErrCode InputMethodSystemAbility::UpdateListenEventFlag(const InputClientInfoInner &clientInfoInner, uint32_t eventFlag)
{
    InputClientInfo clientInfo = InputMethodTools::GetInstance().InnerToInputClientInfo(clientInfoInner);
    IMSA_HILOGD("finalEventFlag: %{public}u, eventFlag: %{public}u.", clientInfo.eventFlag, eventFlag);
    if (EventStatusManager::IsImeHideOn(eventFlag) || EventStatusManager::IsImeShowOn(eventFlag) ||
        EventStatusManager::IsInputStatusChangedOn(eventFlag) ||
        EventStatusManager::IsSoftKeyboardInfoChangedOn(eventFlag)) {
        if (identityChecker_ == nullptr) {
            IMSA_HILOGE("identityChecker_ is nullptr!");
            return ErrorCode::ERROR_NULL_POINTER;
        }
        if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID()) &&
            !identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID())) {
            IMSA_HILOGE("not system application!");
            return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
        }
    }
    auto userId = OsAccountAdapter::GetOsAccountLocalIdFromUid(IPCSkeleton::GetCallingUid());
    return ImeEventListenerManager::GetInstance().UpdateListenerInfo(
        userId, { clientInfo.eventFlag, clientInfo.client, IPCSkeleton::GetCallingPid() });
}
// LCOV_EXCL_START
ErrCode InputMethodSystemAbility::SetCallingWindow(uint32_t windowId, const sptr<IInputClient> &client)
{
    IMSA_HILOGD("IMF SA setCallingWindow enter");
    auto pid = IPCSkeleton::GetCallingPid();
    AccessTokenID tokenId = IPCSkeleton::GetCallingTokenID();
    auto userId = GetCallingUserId();
    auto checkRet = IsFocusedOrBroker(pid, tokenId, userId, windowId);
    if (!checkRet.first) {
        return ErrorCode::ERROR_CLIENT_NOT_FOCUSED;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return session->OnSetCallingWindow(checkRet.second, client, windowId);
}

ErrCode InputMethodSystemAbility::GetInputStartInfo(InputStartInfo &inputStartInfo)
{
    if (!identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID())) {
        IMSA_HILOGE("not native sa!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    auto userId = GetCallingUserId();
    auto userSession = UserSessionManager::GetInstance().GetUserSession(userId);
    if (userSession == nullptr) {
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return userSession->GetInputStartInfo(inputStartInfo);
}
// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::IsCurrentIme(bool& resultValue)
{
    auto userId = GetCallingUserId();
    auto tokenId = GetCallingTokenID();
    resultValue = IsCurrentIme(userId, tokenId);
    return ERR_OK;
}

ErrCode InputMethodSystemAbility::IsInputTypeSupported(int32_t type, bool &resultValue)
{
    resultValue = InputTypeManager::GetInstance().IsSupported(static_cast<InputType>(type));
    return ERR_OK;
}

ErrCode InputMethodSystemAbility::StartInputType(int32_t type, bool isPersistence)
{
    return StartInputType(GetCallingUserId(), static_cast<InputType>(type), isPersistence);
}
// LCOV_EXCL_START
ErrCode InputMethodSystemAbility::StartInputTypeAsync(int32_t type, bool isPersistence)
{
    return StartInputType(GetCallingUserId(), static_cast<InputType>(type), isPersistence);
}

ErrCode InputMethodSystemAbility::ExitCurrentInputType()
{
    auto userId = GetCallingUserId();
    auto ret = IsDefaultImeFromTokenId(userId, IPCSkeleton::GetCallingTokenID());
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("not default ime!");
        return ErrorCode::ERROR_NOT_DEFAULT_IME;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    InputTypeManager::GetInstance().Set(false);
    return session->StartCurrentIme();
}

ErrCode InputMethodSystemAbility::IsDefaultIme()
{
    return IsDefaultImeFromTokenId(GetCallingUserId(), IPCSkeleton::GetCallingTokenID());
}

ErrCode InputMethodSystemAbility::IsSystemApp(bool& resultValue)
{
    resultValue = identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID());
    return ERR_OK;
}

ErrCode InputMethodSystemAbility::IsCapacitySupport(int32_t capacity, bool &isSupport)
{
    IMSA_HILOGI("capacity:%{public}d", capacity);
    if (capacity < 0 || capacity >= static_cast<int32_t>(CapacityType::END)) {
        IMSA_HILOGE("capacity is invalid!");
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }
    if (capacity == static_cast<int32_t>(CapacityType::IMMERSIVE_EFFECT)) {
        isSupport = ImeInfoInquirer::GetInstance().IsCapacitySupport(IMMERSIVE_EFFECT_CAP_NAME);
    }
    if (capacity == static_cast<int32_t>(CapacityType::SYSTEM_PANEL)) {
        isSupport = ImeInfoInquirer::GetInstance().IsCapacitySupport(SYSTEM_PANEL_CAP_NAME);
    }
    if (capacity == static_cast<int32_t>(CapacityType::DISABLE_IMMERSIVE_MODE)) {
        isSupport = ImeInfoInquirer::GetInstance().IsDisableImmersiveMode();
    }
    if (capacity == static_cast<int32_t>(CapacityType::SUPPORT_PC_MODE)) {
        isSupport = ImeInfoInquirer::GetInstance().IsSupportPcMode();
    }
    if (capacity == static_cast<int32_t>(CapacityType::DISABLE_PC_MODE_IMMERSIVE_MODE)) {
        isSupport = ImeInfoInquirer::GetInstance().IsDisablePcModeImmersiveMode();
    }
    if (capacity == static_cast<int32_t>(CapacityType::IS_PC_MODE)) {
        isSupport = ImeInfoInquirer::GetInstance().IsPcMode();
    }
    return ERR_OK;
}

int32_t InputMethodSystemAbility::IsDefaultImeFromTokenId(int32_t userId, uint32_t tokenId)
{
    auto prop = std::make_shared<Property>();
    auto ret = ImeInfoInquirer::GetInstance().GetDefaultInputMethod(userId, prop, true);
    if (ret != ErrorCode::NO_ERROR || prop == nullptr) {
        IMSA_HILOGE("failed to get default ime!");
        return ErrorCode::ERROR_PERSIST_CONFIG;
    }
    if (!identityChecker_->IsBundleNameValid(tokenId, prop->name)) {
        return ErrorCode::ERROR_NOT_DEFAULT_IME;
    }
    return ErrorCode::NO_ERROR;
}

ErrCode InputMethodSystemAbility::IsCurrentImeByPid(int32_t pid, bool& resultValue, int32_t userId)
{
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID()) &&
        !identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID())) {
        IMSA_HILOGE("not system application or system ability!");
        resultValue = false;
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(outputUserId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", outputUserId);
        resultValue = false;
        return ErrorCode::ERROR_NULL_POINTER;
    }
    resultValue = session->IsCurrentImeByPid(pid);
    return ERR_OK;
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::IsPanelShown(uint64_t displayId, const PanelInfo &panelInfo, bool &isShown)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    int32_t outputUserId = -1;
    auto errorCode = AccountSA::OsAccountManager::GetForegroundOsAccountLocalId(displayId, outputUserId);
    if (errorCode != 0) {
        IMSA_HILOGE("GetForegroundOsAccountLocalId failed, displayId:%{public}" PRIu64 ", errorCode:%{public}d",
            displayId, errorCode);
        return ErrorCode::ERROR_ACCOUNT_LOCALID_FAILED;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(outputUserId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", outputUserId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return session->IsPanelShown(displayId, panelInfo, isShown);
}

int32_t InputMethodSystemAbility::IsPanelShown(const PanelInfo &panelInfo, bool &isShown)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    auto userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    return session->IsPanelShown(panelInfo, isShown);
}

int32_t InputMethodSystemAbility::DisplayOptionalInputMethod()
{
    IMSA_HILOGD("InputMethodSystemAbility start.");
    return OnDisplayOptionalInputMethod();
}

ErrCode InputMethodSystemAbility::SwitchInputMethod(const std::string &bundleName,
    const std::string &subName, uint32_t trigger, int32_t userId)
{
    // IMSA not check permission, add this verify for prevent counterfeit
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (static_cast<SwitchTrigger>(trigger) == SwitchTrigger::IMSA) {
        IMSA_HILOGW("caller counterfeit!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    return SwitchInputMethodInner(outputUserId, bundleName, subName, static_cast<SwitchTrigger>(trigger));
}
// LCOV_EXCL_START
ErrCode InputMethodSystemAbility::EnableIme(
    const std::string &bundleName, const std::string &extensionName, int32_t status, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto ret = CheckEnableAndSwitchPermission();
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("permission check failed!");
        return ret;
    }
    return EnableIme(outputUserId, bundleName, extensionName, static_cast<EnabledStatus>(status));
}

int32_t InputMethodSystemAbility::EnableIme(
    int32_t userId, const std::string &bundleName, const std::string &extensionName, EnabledStatus status)
{
    return ImeEnabledInfoManager::GetInstance().Update(
        userId, bundleName, extensionName, static_cast<EnabledStatus>(status));
}

int32_t InputMethodSystemAbility::StartSwitch(int32_t userId, const SwitchInfo &switchInfo,
    const std::shared_ptr<PerUserSession> &session)
{
    if (session == nullptr) {
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGI("start switch %{public}s|%{public}s.", switchInfo.bundleName.c_str(), switchInfo.subName.c_str());
    auto info = ImeInfoInquirer::GetInstance().GetImeInfo(userId, switchInfo.bundleName, switchInfo.subName);
    if (info == nullptr) {
        return ErrorCode::ERROR_IMSA_GET_IME_INFO_FAILED;
    }
    InputTypeManager::GetInstance().Set(false);
    int32_t ret = ErrorCode::NO_ERROR;
    {
        InputMethodSyncTrace tracer("InputMethodSystemAbility_OnSwitchInputMethod");
        std::string targetImeName = info->prop.name + "/" + info->prop.id;
        if (!switchInfo.isTmpImeSwitchSubtype) {
            ret = ImeEnabledInfoManager::GetInstance().SetCurrentIme(userId, targetImeName, switchInfo.subName, true);
            if (ret != ErrorCode::NO_ERROR) {
                IMSA_HILOGW("set %{public}d/%{public}s current ime failed.", userId, targetImeName.c_str());
                return ret;
            }
            session->NotifyImeChangedToClients();
        }
        GetValidSubtype(switchInfo.subName, info);
        if (session->IsImeSwitchForbidden()) {
            /* The following process is designed to prevent the failure of switching between uppercase(chinese)
             * and lowercase(english) via shortcut keys or the pc status bar */
            auto imeData = session->GetRealImeData();
            if (imeData != nullptr && imeData->ime.first == switchInfo.bundleName) {
                IMSA_HILOGD("subtype switch in special scene:%{public}s.", info->subProp.id.c_str());
                session->SwitchSubtype(info->subProp);
            }
            return ret;
        }
        auto targetIme = std::make_shared<ImeNativeCfg>(
            ImeNativeCfg{ targetImeName, info->prop.name, switchInfo.subName, info->prop.id });
        ret = session->StartIme(targetIme);
        if (ret != ErrorCode::NO_ERROR) {
            InputMethodSysEvent::GetInstance().InputmethodFaultReporter(
                ret, switchInfo.bundleName, "switch input method failed!", userId);
            return ret;
        }
        ret = session->SwitchSubtype(info->subProp);
    }
    ret = info->isSpecificSubName ? ret : ErrorCode::NO_ERROR;
    if (ret != ErrorCode::NO_ERROR) {
        InputMethodSysEvent::GetInstance().InputmethodFaultReporter(
            ret, switchInfo.bundleName, "switch input method subtype failed!", userId);
    }
    return ret;
}
// LCOV_EXCL_START
bool InputMethodSystemAbility::IsTmpIme(int32_t userId, uint32_t tokenId)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("user:%{public}d session is nullptr!", userId);
        return false;
    }
    auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetUserCfgIme(userId);
    if (currentImeCfg.bundleName.empty()) {
        IMSA_HILOGE("user:%{public}d has no default ime.", userId);
        return false;
    }
    auto imeData = session->GetRealImeData();
    if (imeData == nullptr) {
        IMSA_HILOGE("user:%{public}d has no running ime.", userId);
        return false;
    }
    auto bundleName = FullImeInfoManager::GetInstance().Get(userId, tokenId);
    if (bundleName.empty()) {
        bundleName = identityChecker_->GetBundleNameByToken(tokenId);
        IMSA_HILOGW("%{public}d/%{public}d/%{public}s not find in cache.", userId, tokenId, bundleName.c_str());
    }
    return !currentImeCfg.bundleName.empty() && !bundleName.empty() &&
        imeData->ime.first != currentImeCfg.bundleName && imeData->ime.first == bundleName;
}

bool InputMethodSystemAbility::IsTmpImeSwitchSubtype(int32_t userId, uint32_t tokenId, const SwitchInfo &switchInfo)
{
    if (!IsTmpIme(userId, tokenId)) {
        IMSA_HILOGD("user:%{public}d tokenId:%{public}d not tmp ime.", userId, tokenId);
        return false;
    }
    auto bundleName = FullImeInfoManager::GetInstance().Get(userId, tokenId);
    if (bundleName.empty()) {
        bundleName = identityChecker_->GetBundleNameByToken(tokenId);
        IMSA_HILOGW("%{public}d/%{public}d/%{public}s not find in cache.", userId, tokenId, bundleName.c_str());
    }
    bool ret = !bundleName.empty() && bundleName == switchInfo.bundleName;
    IMSA_HILOGD("%{public}s/%{public}d switch.", switchInfo.bundleName.c_str(), ret);
    return ret;
}

int32_t InputMethodSystemAbility::SwitchInputMethodInner(int32_t userId, const std::string &bundleName,
    const std::string &subName, SwitchTrigger trigger)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    auto tokenId = GetCallingTokenID();
    SwitchInfo switchInfo = { std::chrono::system_clock::now(), bundleName, subName };
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    EnabledStatus status = EnabledStatus::DISABLED;
    auto ret = ImeEnabledInfoManager::GetInstance().GetEnabledState(userId, bundleName, status);
    if (ret != ErrorCode::NO_ERROR || status == EnabledStatus::DISABLED) {
        IMSA_HILOGW("ime %{public}s not enable, stopped!", bundleName.c_str());
        return ErrorCode::ERROR_ENABLE_IME;
    }
    auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentImeCfg == nullptr) {
        IMSA_HILOGE("Failed to get current ime config");
        return ErrorCode::ERROR_IMSA_GET_IME_INFO_FAILED;
    }
    if (switchInfo.subName.empty() && switchInfo.bundleName == currentImeCfg->bundleName) {
        switchInfo.subName = currentImeCfg->subName;
    }
    switchInfo.timestamp = std::chrono::system_clock::now();
    switchInfo.isTmpImeSwitchSubtype = IsTmpImeSwitchSubtype(userId, tokenId, switchInfo);
    session->GetSwitchQueue().Push(switchInfo);
    return InputTypeManager::GetInstance().IsInputType({ bundleName, subName })
               ? OnStartInputType(userId, switchInfo, true)
               : OnSwitchInputMethod(userId, switchInfo, trigger);
}

int32_t InputMethodSystemAbility::OnSwitchInputMethod(int32_t userId, const SwitchInfo &switchInfo,
    SwitchTrigger trigger)
{
    InputMethodSysEvent::GetInstance().RecordEvent(IMEBehaviour::CHANGE_IME);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!session->GetSwitchQueue().IsReady(switchInfo)) {
        IMSA_HILOGD("start wait.");
        session->GetSwitchQueue().Wait(switchInfo);
    }
    int32_t ret = CheckSwitchPermission(userId, switchInfo, trigger);
    if (ret != ErrorCode::NO_ERROR) {
        InputMethodSysEvent::GetInstance().InputmethodFaultReporter(ErrorCode::ERROR_STATUS_PERMISSION_DENIED,
            switchInfo.bundleName, "switch input method failed!", userId);
        session->GetSwitchQueue().Pop();
        return ret;
    }
    ret = StartSwitch(userId, switchInfo, session);
    session->GetSwitchQueue().Pop();
    return ret;
}

void InputMethodSystemAbility::GetValidSubtype(const std::string &subName, const std::shared_ptr<ImeInfo> &info)
{
    if (info == nullptr) {
        IMSA_HILOGE("info is nullptr!");
        return;
    }
    if (subName.empty()) {
        IMSA_HILOGW("undefined subtype");
        info->subProp.id = UNDEFINED;
        info->subProp.name = UNDEFINED;
    }
}

int32_t InputMethodSystemAbility::OnStartInputType(int32_t userId, const SwitchInfo &switchInfo,
    bool isCheckPermission, bool isPersistence)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    if (!session->GetSwitchQueue().IsReady(switchInfo)) {
        IMSA_HILOGD("start wait.");
        session->GetSwitchQueue().Wait(switchInfo);
    }
    IMSA_HILOGD("start switch %{public}s|%{public}s.", switchInfo.bundleName.c_str(), switchInfo.subName.c_str());
    if (isCheckPermission && !IsStartInputTypePermitted(userId)) {
        IMSA_HILOGE("not permitted to start input type!");
        session->GetSwitchQueue().Pop();
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    if (!IsNeedSwitch(userId, switchInfo.bundleName, switchInfo.subName)) {
        IMSA_HILOGI("no need to switch.");
        session->GetSwitchQueue().Pop();
        return ErrorCode::NO_ERROR;
    }
    int32_t ret = SwitchInputType(userId, switchInfo, isPersistence);
    session->GetSwitchQueue().Pop();
    return ret;
}

bool InputMethodSystemAbility::IsNeedSwitch(int32_t userId, const std::string &bundleName,
    const std::string &subName)
{
    if (InputTypeManager::GetInstance().IsStarted()) {
        ImeIdentification target = { bundleName, subName };
        return !(target == InputTypeManager::GetInstance().GetCurrentIme());
    }
    auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentImeCfg == nullptr) {
        return true;
    }
    IMSA_HILOGI("currentIme: %{public}s/%{public}s, targetIme: %{public}s/%{public}s.",
        currentImeCfg->bundleName.c_str(), currentImeCfg->subName.c_str(), bundleName.c_str(), subName.c_str());
    if ((subName.empty() && bundleName == currentImeCfg->bundleName) ||
        (!subName.empty() && subName == currentImeCfg->subName && currentImeCfg->bundleName == bundleName)) {
        IMSA_HILOGI("no need to switch");
        return false;
    }
    return true;
}

int32_t InputMethodSystemAbility::SwitchInputType(int32_t userId, const SwitchInfo &switchInfo, bool isPersistence)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    auto targetIme = session->GetImeNativeCfg(userId, switchInfo.bundleName, switchInfo.subName);
    if (targetIme == nullptr) {
        IMSA_HILOGE("targetIme is nullptr!");
        return ErrorCode::ERROR_IMSA_GET_IME_INFO_FAILED;
    }
    auto ret = session->StartIme(targetIme);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("start input method failed!");
        return ret;
    }

    ImeIdentification ime = { switchInfo.bundleName, switchInfo.subName };
    bool isVoiceKbIme = InputTypeManager::GetInstance().IsVoiceKbIme(ime);
    // Use SendPrivateCommand to notify when start voice keyboard temporarily.
    if (isVoiceKbIme && !isPersistence) {
        ret = session->SendVoicePrivateCommand(isPersistence);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("send voice private command failed!");
            return ret;
        }
    } else {
        SubProperty prop;
        prop.name = switchInfo.bundleName;
        prop.id = switchInfo.subName;
        ret = session->SwitchSubtype(prop);
        if (ret != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("switch subtype failed!");
            return ret;
        }
    }
    InputTypeManager::GetInstance().Set(true, { switchInfo.bundleName, switchInfo.subName });
    session->SetInputType(nullptr);
    return ErrorCode::NO_ERROR;
}

// Deprecated because of no permission check, kept for compatibility
int32_t InputMethodSystemAbility::HideCurrentInputDeprecated(uint32_t windowId)
{
    auto pid = IPCSkeleton::GetCallingPid();
    std::shared_ptr<PerUserSession> session = nullptr;
    auto result = PrepareForOperateKeyboard(session, windowId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("prepare failed:%{public}d.", result);
        return result;
    }
    auto [clientGroup, clientInfo] = session->GetClientBySelfPid(pid);
    if (clientInfo == nullptr) {
        IMSA_HILOGE("client group not found");
        return ErrorCode::ERROR_CLIENT_NOT_FOUND;
    }
    return session->OnHideCurrentInput(clientInfo->clientGroupId);
}

int32_t InputMethodSystemAbility::ShowCurrentInputDeprecated(uint32_t windowId)
{
    auto pid = IPCSkeleton::GetCallingPid();
    std::shared_ptr<PerUserSession> session = nullptr;
    auto result = PrepareForOperateKeyboard(session, windowId);
    if (result != ErrorCode::NO_ERROR) {
        return result;
    }
    auto [clientGroup, clientInfo] = session->GetClientBySelfPid(pid);
    if (clientInfo == nullptr) {
        IMSA_HILOGE("client group not found");
        return ErrorCode::ERROR_CLIENT_NOT_FOUND;
    }
    return session->OnShowCurrentInput(clientInfo->clientGroupId);
}
// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::GetCurrentInputMethod(int32_t userId, Property& resultValue)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto prop = ImeInfoInquirer::GetInstance().GetCurrentInputMethod(outputUserId);
    if (prop == nullptr) {
        IMSA_HILOGE("prop is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    resultValue = *prop;
    return ERR_OK;
}
// LCOV_EXCL_START
ErrCode InputMethodSystemAbility::IsKeyboardCallingProcess(
    int32_t pid, uint32_t windowId, bool &isKeyboardCallingProcess)
{
    int32_t userId = GetCallingUserId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    isKeyboardCallingProcess = session->IsKeyboardCallingProcess(pid, windowId);
    return ERR_OK;
}

ErrCode InputMethodSystemAbility::IsDefaultImeSet(bool& resultValue, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    resultValue = ImeInfoInquirer::GetInstance().IsDefaultImeSet(outputUserId);
    return ERR_OK;
}
// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::GetCurrentInputMethodSubtype(SubProperty& resultValue, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto prop = ImeInfoInquirer::GetInstance().GetCurrentSubtype(outputUserId);
    if (prop == nullptr) {
        IMSA_HILOGE("prop is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    resultValue = *prop;
    return ERR_OK;
}

ErrCode InputMethodSystemAbility::GetDefaultInputMethod(Property &prop, bool isBrief, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    std::shared_ptr<Property> property = std::make_shared<Property>(prop);
    auto ret = ImeInfoInquirer::GetInstance().GetDefaultInputMethod(outputUserId, property, isBrief);
    if (property != nullptr && ret == ErrorCode::NO_ERROR) {
        prop = *property;
    }
    return ret;
}

ErrCode InputMethodSystemAbility::GetInputMethodConfig(ElementName &inputMethodConfig, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    return ImeInfoInquirer::GetInstance().GetInputMethodConfig(outputUserId, inputMethodConfig);
}

ErrCode InputMethodSystemAbility::ListInputMethod(uint32_t status, std::vector<Property> &props, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    return ImeInfoInquirer::GetInstance().ListInputMethod(outputUserId,
        static_cast<InputMethodStatus>(status), props);
}

ErrCode InputMethodSystemAbility::ListCurrentInputMethodSubtype(std::vector<SubProperty> &subProps, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    return ImeInfoInquirer::GetInstance().ListCurrentInputMethodSubtype(outputUserId, subProps);
}

int32_t InputMethodSystemAbility::ListInputMethodSubtype(const std::string &bundleName,
    std::vector<SubProperty> &subProps, int32_t userId)
{
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    return ImeInfoInquirer::GetInstance().ListInputMethodSubtype(outputUserId, bundleName, subProps);
}

/**
 * Work Thread of input method management service
 * \n Remote commands which may change the state or data in the service will be handled sequentially in this thread.
 */
// LCOV_EXCL_START
void InputMethodSystemAbility::WorkThread()
{
    pthread_setname_np(pthread_self(), "OS_IMSAWorkThread");
    while (!stop_) {
        Message *msg = MessageHandler::Instance()->GetMessage();
        if (msg == nullptr) {
            IMSA_HILOGE("msg is nullptr!");
            break;
        }
        switch (msg->msgId_) {
            case MSG_ID_USER_SWITCHED: {
                OnUserSwitched(msg);
                break;
            }
            case MSG_ID_USER_REMOVED: {
                OnUserRemoved(msg);
                break;
            }
            case MSG_ID_USER_STOPPED: {
                OnUserStop(msg);
                break;
            }
            case MSG_ID_HIDE_KEYBOARD_SELF: {
                OnHideKeyboardSelf(msg);
                break;
            }
            case MSG_ID_BUNDLE_SCAN_FINISHED: {
                HandleBundleScanFinished();
                break;
            }
            case MSG_ID_DATA_SHARE_READY: {
                HandleDataShareReady();
                break;
            }
            case MSG_ID_PACKAGE_ADDED:
            case MSG_ID_PACKAGE_CHANGED:
            case MSG_ID_PACKAGE_REMOVED: {
                HandlePackageEvent(msg);
                break;
            }
            case MSG_ID_SYS_LANGUAGE_CHANGED:
            case MSG_ID_BUNDLE_RESOURCES_CHANGED: {
                FullImeInfoManager::GetInstance().Update();
                break;
            }
            case MSG_ID_BOOT_COMPLETED: {
                FullImeInfoManager::GetInstance().Init();
                break;
            }
            case MSG_ID_OS_ACCOUNT_STARTED: {
                HandleOsAccountStarted();
                break;
            }
            case MSG_ID_SCREEN_UNLOCK: {
                OnScreenUnlock(msg);
                break;
            }
            case MSG_ID_SCREEN_LOCK: {
                OnScreenLock(msg);
                break;
            }
            case MSG_ID_REGULAR_UPDATE_IME_INFO: {
                FullImeInfoManager::GetInstance().RegularInit();
                break;
            }
            case MSG_ID_UPDATE_LARGE_MEMORY_STATE: {
                int32_t ret = HandleUpdateLargeMemoryState(msg);
                if (ret != ErrorCode::NO_ERROR) {
                    IMSA_HILOGE("update large memory state failed %{public}d", ret);
                }
                break;
            }
            case MSG_ID_SYS_MEMORY_CHANGED: {
                OnSysMemChanged();
                break;
            }
            case MSG_ID_WMS_STARTED: {
                HandleWmsStarted();
                break;
            }
            case MSG_ID_TRIGGER_MAKE_SYS_IME_IMAGE: {
                OnMakeSysImeImage();
                break;
            }
            case MSG_ID_SYS_IME_IMAGE_CREATED: {
                OnSysImeImageCreated(msg);
                break;
            }
            default: {
                IMSA_HILOGD("the message is %{public}d.", msg->msgId_);
                break;
            }
        }
        delete msg;
        msg = nullptr;
    }
}

/**
 * Called when a user is started. (EVENT_USER_STARTED is received)
 * \n Run in work thread of input method management service
 * \param msg the parameters are saved in msg->msgContent_
 * \return ErrorCode
 */
int32_t InputMethodSystemAbility::OnUserSwitched(const Message *msg)
{
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("message is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t fromUserId = 0;
    int32_t toUserId = 0;
    uint64_t displayId = 0;
    MessageParcel *data = msg->msgContent_;
    if (!ITypesUtil::Unmarshal(*data, fromUserId, toUserId, displayId)) {
        IMSA_HILOGE("failed to read message parcel");
        return ErrorCode::ERROR_EX_PARCELABLE;
    }
    IMSA_HILOGI("displayId: %{public}" PRIu64 " from %{public}d to %{public}d", displayId, fromUserId, toUserId);
    HandleUserSwitchedOut(fromUserId, displayId);
    HandleUserSwitchedIn(toUserId, displayId);
#ifdef SCENE_BOARD_ENABLE
    // if scb is enabled, restart ime when receive wmsConnected.
    return ErrorCode::NO_ERROR;
#endif
    // if scb is not enabled, restart ime when every user switched
    StartNewUserIme(toUserId);
    return ErrorCode::NO_ERROR;
}

void InputMethodSystemAbility::HandleUserSwitchedOut(int32_t userId, uint64_t displayId)
{
#ifndef SCENE_BOARD_ENABLE
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session != nullptr) {
        session->RemoveAllCurrentClient();
    }
#endif
}

void InputMethodSystemAbility::HandleUserSwitchedIn(int32_t userId, uint64_t displayId)
{
    UpdateUserInfo(userId, displayId);
    if (WindowMonitorsManager::GetInstance().IsInited(userId)) {
        IMSA_HILOGD("user %{public}d has been inited", userId);
    } else {
        IMSA_HILOGI("user %{public}d has not been inited, init now", userId);
        if (InitWindowMonitors(userId)) {
            WindowMonitorsManager::GetInstance().SetInited(userId);
        }
    }
}

int32_t InputMethodSystemAbility::OnUserRemoved(const Message *msg)
{
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("Aborted! Message is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t userId = 0;
    MessageParcel *data = msg->msgContent_;
    if (!ITypesUtil::Unmarshal(*data, userId)) {
        IMSA_HILOGE("failed to read parcel");
        return ErrorCode::ERROR_EX_PARCELABLE;
    }
    IMSA_HILOGI("userId: %{public}d", userId);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session != nullptr) {
        session->StopCurrentIme();
        UserSessionManager::GetInstance().RemoveUserSession(userId);
    }
    FullImeInfoManager::GetInstance().Delete(userId);
    NumkeyAppsManager::GetInstance().OnUserRemoved(userId);
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnUserStop(const Message *msg)
{
    auto session = GetSessionFromMsg(msg);
    if (session == nullptr) {
        return ErrorCode::ERROR_NULL_POINTER;
    }
    session->StopCurrentIme();
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnHideKeyboardSelf(const Message *msg)
{
    auto session = GetSessionFromMsg(msg);
    if (session == nullptr) {
        return ErrorCode::ERROR_NULL_POINTER;
    }
    session->OnHideSoftKeyBoardSelf();
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::HandleUpdateLargeMemoryState(const Message *msg)
{
    IMSA_HILOGD("called");
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("Aborted! Message is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    MessageParcel *data = msg->msgContent_;
    int32_t uid = 0;
    int32_t memoryState = 0;
    if (!ITypesUtil::Unmarshal(*data, uid, memoryState) ||
        (memoryState != LargeMemoryState::LARGE_MEMORY_NEED &&
        memoryState != LargeMemoryState::LARGE_MEMORY_NOT_NEED)) {
        IMSA_HILOGE("Failed to read message parcel or invaild param %{public}d!", memoryState);
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    IMSA_HILOGI("memory state %{public}d.", memoryState);
    auto userId = GetUserId(uid);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->UpdateLargeMemorySceneState(memoryState);
}

int32_t InputMethodSystemAbility::HandlePackageEvent(const Message *msg)
{
    MessageParcel *data = msg->msgContent_;
    if (data == nullptr) {
        IMSA_HILOGD("data is nullptr.");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    int32_t userId = 0;
    std::string packageName;
    if (!ITypesUtil::Unmarshal(*data, userId, packageName)) {
        IMSA_HILOGE("Failed to read message parcel!");
        return ErrorCode::ERROR_EX_PARCELABLE;
    }
    if (msg->msgId_ == MSG_ID_PACKAGE_CHANGED) {
        return OnPackageUpdated(userId, packageName);
    }
    if (msg->msgId_ == MSG_ID_PACKAGE_ADDED) {
        auto ret = FullImeInfoManager::GetInstance().Add(userId, packageName);
        if (ret == ErrorCode::NO_ERROR) {
            HandleEDCInputMethodInstall(userId, packageName);
        }
        return ret;
    }
    if (msg->msgId_ == MSG_ID_PACKAGE_REMOVED) {
        return OnPackageRemoved(userId, packageName);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnPackageUpdated(int32_t userId, const std::string &packageName)
{
    int32_t ret = FullImeInfoManager::GetInstance().Update(userId, packageName);
    if (ret != ErrorCode::NO_ERROR) {
        return ret;
    }
    if (!OsAccountAdapter::IsOsAccountForeground(userId)) {
        IMSA_HILOGD("not foreground user");
        return ErrorCode::NO_ERROR;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        UserSessionManager::GetInstance().AddUserSession(userId);
    }
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    session->OnPackageUpdated(packageName);
    return ErrorCode::NO_ERROR;
}

/**
 *  Called when a package is removed.
 *  \n Run in work thread of input method management service
 *  \param msg the parameters are saved in msg->msgContent_
 *  \return ErrorCode::NO_ERROR
 *  \return ErrorCode::ERROR_USER_NOT_UNLOCKED user not unlocked
 *  \return ErrorCode::ERROR_BAD_PARAMETERS bad parameter
 */
int32_t InputMethodSystemAbility::OnPackageRemoved(int32_t userId, const std::string &packageName)
{
    HandleEDCInputMethodRemove(userId, packageName);
    FullImeInfoManager::GetInstance().Delete(userId, packageName);
    return ErrorCode::NO_ERROR;
}

void InputMethodSystemAbility::OnScreenUnlock(const Message *msg)
{
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("message is nullptr");
        return;
    }
    int32_t userId = 0;
    if (!ITypesUtil::Unmarshal(*msg->msgContent_, userId)) {
        IMSA_HILOGE("failed to read message");
        return;
    }
    IMSA_HILOGI("userId: %{public}d", userId);
    if (!OsAccountAdapter::IsOsAccountForeground(userId)) {
        return;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        UserSessionManager::GetInstance().AddUserSession(userId);
    }
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return;
    }
    session->OnScreenUnlock();
}

void InputMethodSystemAbility::OnScreenLock(const Message *msg)
{
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("message is nullptr");
        return;
    }
    int32_t userId = 0;
    if (!ITypesUtil::Unmarshal(*msg->msgContent_, userId)) {
        IMSA_HILOGE("failed to read message");
        return;
    }
    IMSA_HILOGD("userId: %{public}d", userId);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        UserSessionManager::GetInstance().AddUserSession(userId);
    }
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return;
    }
    session->OnScreenLock();
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::OnDisplayOptionalInputMethod()
{
    IMSA_HILOGD("InputMethodSystemAbility::OnDisplayOptionalInputMethod start.");
    AAFwk::Want want;
    want.SetAction(SELECT_DIALOG_ACTION);
    want.SetElementName(SELECT_DIALOG_HAP, SELECT_DIALOG_ABILITY);
    int32_t ret = AAFwk::AbilityManagerClient::GetInstance()->StartAbility(want);
    if (ret != ErrorCode::NO_ERROR && ret != START_SERVICE_ABILITY_ACTIVATING) {
        IMSA_HILOGE("start InputMethod ability failed, err: %{public}d", ret);
        return ErrorCode::ERROR_EX_SERVICE_SPECIFIC;
    }
    IMSA_HILOGI("start InputMethod ability success.");
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::SwitchByCombinationKey(uint32_t state)
{
    IMSA_HILOGD("InputMethodSystemAbility::SwitchByCombinationKey start.");
    auto userId = OsAccountAdapter::GetMainAccountId();
    if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_MODE, state)) {
        IMSA_HILOGI("switch mode.");
        return SwitchMode(userId);
    }
    if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_LANGUAGE, state)) {
        IMSA_HILOGI("switch language.");
        return SwitchLanguage(userId);
    }
    if (CombinationKey::IsMatch(CombinationKeyFunction::SWITCH_IME, state)) {
        IMSA_HILOGI("switch ime.");
        DealSwitchRequest(userId);
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGE("keycode is undefined!");
    return ErrorCode::ERROR_EX_UNSUPPORTED_OPERATION;
}

void InputMethodSystemAbility::DealSwitchRequest(int32_t userId)
{
    {
        std::lock_guard<std::mutex> lock(switchImeMutex_);
        // 0 means current swich ime task count.
        if (switchTaskExecuting_.load()) {
            IMSA_HILOGI("already has switch ime task.");
            ++targetSwitchCount_;
            return;
        } else {
            switchTaskExecuting_.store(true);
            ++targetSwitchCount_;
        }
    }
    auto switchTask = [this, userId]() {
        auto checkSwitchCount = [this]() {
            std::lock_guard<std::mutex> lock(switchImeMutex_);
            if (targetSwitchCount_ > 0) {
                return true;
            }
            switchTaskExecuting_.store(false);
            return false;
        };
        do {
            SwitchType(userId);
        } while (checkSwitchCount());
    };
    if (serviceHandler_ == nullptr) {
        IMSA_HILOGE("serviceHandler_ is nullptr");
        return;
    }
    // 0 means delay time is 0.
    serviceHandler_->PostTask(switchTask, "SwitchImeTask", 0, AppExecFwk::EventQueue::Priority::IMMEDIATE);
}

int32_t InputMethodSystemAbility::SwitchMode(int32_t userId)
{
    auto currentIme = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentIme == nullptr) {
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    auto bundleName = currentIme->bundleName;
    auto subName = currentIme->subName;
    auto info = ImeInfoInquirer::GetInstance().GetImeInfo(userId, bundleName, subName);
    if (info == nullptr) {
        IMSA_HILOGE("current ime is abnormal!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    if (info->isNewIme) {
        IMSA_HILOGD("the switching operation is handed over to ime.");
        return ErrorCode::NO_ERROR;
    }
    auto condition = info->subProp.mode == "upper" ? Condition::LOWER : Condition::UPPER;
    return SwitchByCondition(condition, info, userId);
}

int32_t InputMethodSystemAbility::SwitchLanguage(int32_t userId)
{
    auto currentIme = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentIme == nullptr) {
        return ErrorCode::ERROR_IME_NOT_STARTED;
    }
    auto bundleName = currentIme->bundleName;
    auto subName = currentIme->subName;
    auto info = ImeInfoInquirer::GetInstance().GetImeInfo(userId, bundleName, subName);
    if (info == nullptr) {
        IMSA_HILOGE("current ime is abnormal!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    if (info->isNewIme) {
        IMSA_HILOGD("the switching operation is handed over to ime.");
        return ErrorCode::NO_ERROR;
    }
    if (info->subProp.language != "chinese" && info->subProp.language != "english") {
        return ErrorCode::NO_ERROR;
    }
    auto condition = info->subProp.language == "chinese" ? Condition::ENGLISH : Condition::CHINESE;
    return SwitchByCondition(condition, info, userId);
}

int32_t InputMethodSystemAbility::SwitchType(int32_t userId)
{
    SwitchInfo nextSwitchInfo = { std::chrono::system_clock::now(), "", "" };
    uint32_t cacheCount = 0;
    {
        std::lock_guard<std::mutex> lock(switchImeMutex_);
        cacheCount = targetSwitchCount_.exchange(0);
    }
    int32_t ret =
        ImeInfoInquirer::GetInstance().GetSwitchInfoBySwitchCount(nextSwitchInfo, userId, cacheCount);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("get next SwitchInfo failed, stop switching ime.");
        return ret;
    }
    if (nextSwitchInfo.bundleName.empty()) {
        IMSA_HILOGD("Stay current ime, no need to switch.");
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGD("switch to: %{public}s.", nextSwitchInfo.bundleName.c_str());
    nextSwitchInfo.timestamp = std::chrono::system_clock::now();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    session->GetSwitchQueue().Push(nextSwitchInfo);
    return OnSwitchInputMethod(userId, nextSwitchInfo, SwitchTrigger::IMSA);
}

void InputMethodSystemAbility::InitMonitors()
{
    int32_t ret = InitAccountMonitor();
    IMSA_HILOGI("init account monitor, ret: %{public}d.", ret);
    SubscribeCommonEvent();
    ret = InitMemMgrMonitor();
    IMSA_HILOGI("init MemMgr monitor, ret: %{public}d.", ret);
    ret = InitKeyEventMonitor();
    IMSA_HILOGI("init KeyEvent monitor, ret: %{public}d.", ret);
    ret = InitWmsMonitor();
    IMSA_HILOGI("init wms monitor, ret: %{public}d.", ret);
    ret = InitPasteboardMonitor();
    IMSA_HILOGI("init Pasteboard monitor, ret: %{public}d.", ret);
    InitSystemLanguageMonitor();
    SubscribeAppMgrService();
}

void InputMethodSystemAbility::SubscribeAppMgrService()
{
    auto commonEventMgr = ImCommonEventManager::GetInstance();
    if (commonEventMgr == nullptr) {
        IMSA_HILOGE("commonEventMgr is nullptr.");
        return;
    }
    auto appMgrStartHandler = []() {
        AppMgrAdapter::ResetImageProcessStateObserver();
        AppMgrAdapter::RegisterImageProcessStateObserver();
    };
    commonEventMgr->SubscribeAppMgrService(appMgrStartHandler);
}

bool InputMethodSystemAbility::InitHaMonitor()
{
    if (!ImeInfoInquirer::GetInstance().IsCapacitySupport(SystemConfig::IME_DAU_STATISTICS_CAP_NAME)) {
        IMSA_HILOGD("ime dau statistics cap is not enable.");
        return false;
    }
    SaInfo info;
    if (!ImeInfoInquirer::GetInstance().GetSaInfo(SystemConfig::HA_SERVICE_NAME, info)) {
        IMSA_HILOGE("get ha service info failed.");
        return false;
    }
    auto commonEventMgr = ImCommonEventManager::GetInstance();
    if (commonEventMgr == nullptr) {
        IMSA_HILOGE("commonEventMgr is nullptr.");
        return false;
    }
    return commonEventMgr->SubscribeHaService([]() { ImfHookMgr::GetInstance().OnHaServiceStart(); }, info.id);
}

// LCOV_EXCL_STOP
void InputMethodSystemAbility::HandleDataShareReady()
{
    IMSA_HILOGI("run in.");
    if (ImeInfoInquirer::GetInstance().GetSystemConfig().enableFullExperienceFeature) {
        IMSA_HILOGW("Enter security mode.");
        RegisterSecurityModeObserver();
    }
    if (SettingsDataUtils::GetInstance().IsDataShareReady()) {
        return;
    }
    SettingsDataUtils::GetInstance().NotifyDataShareReady();
    FullImeInfoManager::GetInstance().Init();
    NumkeyAppsManager::GetInstance().Init(OsAccountAdapter::GetMainAccountId());
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::InitAccountMonitor()
{
    IMSA_HILOGI("InputMethodSystemAbility::InitAccountMonitor start.");
    auto imCommonEventManager = ImCommonEventManager::GetInstance();
    if (imCommonEventManager == nullptr) {
        IMSA_HILOGE("imCommonEventManager is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return imCommonEventManager->SubscribeAccountManagerService(
        [this]() { SendMessageToWorkThread(MessageID::MSG_ID_OS_ACCOUNT_STARTED); });
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::InitKeyEventMonitor()
{
    IMSA_HILOGI("InputMethodSystemAbility::InitKeyEventMonitor start.");
    auto handler = [this]() {
        // Check device capslock status and ime cfg corrent, when device power-up.
        HandleImeCfgCapsState(OsAccountAdapter::GetMainAccountId());

        for (int32_t attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            auto switchTrigger = [this](uint32_t keyCode) { return SwitchByCombinationKey(keyCode);};
            int32_t ret = KeyboardEvent::GetInstance().AddKeyEventMonitor(switchTrigger);
            if (ret == ErrorCode::NO_ERROR) {
                IMSA_HILOGI("SubscribeKeyboardEvent add monitor: success.");
                break;
            } else {
                IMSA_HILOGW("SubscribeKeyboardEvent add monitor: failed. attempt: %{public}d, Retrying...", attempt);
                std::this_thread::sleep_for(std::chrono::milliseconds(INTERVALMS_RETRY));
            }
            if (attempt == MAX_RETRIES) {
                IMSA_HILOGE("SubscribeKeyboardEvent add monitor: failed after %{public}d attempts.", MAX_RETRIES);
            }
        }
    };
    auto imCommonEventManager = ImCommonEventManager::GetInstance();
    if (imCommonEventManager == nullptr) {
        IMSA_HILOGE("imCommonEventManager is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    bool ret = imCommonEventManager->SubscribeKeyboardEvent(handler);
    return ret ? ErrorCode::NO_ERROR : ErrorCode::ERROR_SERVICE_START_FAILED;
}

bool InputMethodSystemAbility::InitWmsMonitor()
{
    auto imCommonEventManager = ImCommonEventManager::GetInstance();
    if (imCommonEventManager == nullptr) {
        IMSA_HILOGE("imCommonEventManager is nullptr!");
        return false;
    }
    return imCommonEventManager->SubscribeWindowManagerService(
        [this]() { SendMessageToWorkThread(MessageID::MSG_ID_WMS_STARTED); });
}
// LCOV_EXCL_START
bool InputMethodSystemAbility::InitMemMgrMonitor()
{
    auto imCommonEventManager = ImCommonEventManager::GetInstance();
    if (imCommonEventManager == nullptr) {
        IMSA_HILOGE("imCommonEventManager is nullptr!");
        return false;
    }
    return imCommonEventManager->SubscribeMemMgrService([this]() { HandleMemStarted(); });
}

void InputMethodSystemAbility::HandlePasteboardStarted()
{
    IMSA_HILOGI("pasteboard started");
    auto accountIds = OsAccountAdapter::GetForegroundOsAccountIds();
    for (auto const &accountId : accountIds) {
        auto session = UserSessionManager::GetInstance().GetUserSession(accountId);
        if (session == nullptr) {
            IMSA_HILOGE("%{public}d session is nullptr!", accountId);
            continue;
        }
        auto data = session->GetRealImeData(true);
        if (data == nullptr) {
            IMSA_HILOGE("readyImeData is nullptr.");
            continue;
        }
        if (data->imeStateManager == nullptr) {
            IMSA_HILOGE("imeStateManager is nullptr.");
            continue;
        }
        data->imeStateManager->TemporaryActiveIme();
    }
}

bool InputMethodSystemAbility::InitPasteboardMonitor()
{
    auto commonEventMgr = ImCommonEventManager::GetInstance();
    if (commonEventMgr == nullptr) {
        IMSA_HILOGE("commonEventMgr is nullptr.");
        return false;
    }

    return commonEventMgr->SubscribePasteboardService([this]() {
        HandlePasteboardStarted();
    });
}

void InputMethodSystemAbility::InitSystemLanguageMonitor()
{
    SystemParamAdapter::GetInstance().WatchParam(SystemParamAdapter::SYSTEM_LANGUAGE_KEY);
}

bool InputMethodSystemAbility::InitFocusChangedMonitor(int32_t userId)
{
    auto initFunc = [this, userId]() {
        auto callback = [this](bool isFocused, uint64_t displayId, int32_t pid, int32_t uid) {
            HandleFocusChanged(isFocused, displayId, pid, uid);
        };
        return FocusMonitorManager::GetInstance().RegisterFocusChangedListener(callback, userId);
    };
    auto ret = initFunc();
    if (ret != ErrorCode::NO_ERROR) {
        if (serviceHandler_ != nullptr) {
            serviceHandler_->PostTask(initFunc, WMS_RETRY_INTERVAL);
        }
        return false;
    }
    return true;
}

bool InputMethodSystemAbility::InitWmsConnectionMonitor(int32_t userId)
{
    auto initFunc = [this, userId]() {
        auto callback = [this](bool isConnected, int32_t userId, int32_t screenId, pid_t pid) {
            isConnected ? HandleWmsConnected(userId, screenId, pid) : HandleWmsDisconnected(userId, screenId, pid);
        };
        return WmsConnectionMonitorManager::GetInstance().RegisterWMSConnectionChangedListener(callback, userId);
    };
    int32_t ret = initFunc();
    if (ret != ErrorCode::NO_ERROR) {
        if (serviceHandler_ != nullptr) {
            serviceHandler_->PostTask(initFunc, WMS_RETRY_INTERVAL);
        }
        return false;
    }
    return true;
}
bool InputMethodSystemAbility::InitWindowDisplayChangedMonitor(int32_t userId)
{
    IMSA_HILOGD("enter.");
    auto initFunc = [userId]() {
        auto callback = [](int32_t userId, int32_t windowId, uint64_t displayId) {
            auto session = UserSessionManager::GetInstance().GetUserSession(userId);
            if (session == nullptr) {
                IMSA_HILOGE("window display id changed, user %{public}d session is nullptr!", userId);
                return;
            };
            session->OnWindowDisplayIdChanged(windowId, displayId);
        };
        return WindowAdapter::GetInstance().RegisterWindowDisplayIdChangedListener(callback, userId);
    };
    auto ret = initFunc();
    if (ret != ErrorCode::NO_ERROR) {
        if (serviceHandler_ != nullptr) {
            serviceHandler_->PostTask(initFunc, WMS_RETRY_INTERVAL);
        }
        return false;
    }
    return true;
}

bool InputMethodSystemAbility::InitDisplayGroupMonitor(int32_t userId)
{
    auto ret = WindowAdapter::GetInstance().RegisterAllGroupInfoChangedListener(userId);
    if (ret != ErrorCode::NO_ERROR) {
        auto callback = [userId]() {
            WindowAdapter::GetInstance().StoreAllDisplayGroupInfos(userId);
            WindowAdapter::GetInstance().RegisterAllGroupInfoChangedListener(userId);
        };
        if (serviceHandler_ != nullptr) {
            serviceHandler_->PostTask(callback, WMS_RETRY_INTERVAL);
        }
        return false;
    } else {
        WindowAdapter::GetInstance().StoreAllDisplayGroupInfos(userId);
        return true;
    }
}

void InputMethodSystemAbility::RegisterSecurityModeObserver()
{
    int32_t ret = SettingsDataUtils::GetInstance().CreateAndRegisterObserver(SETTING_URI_PROXY,
        SettingsDataUtils::SECURITY_MODE, [this]() { DataShareCallback(SettingsDataUtils::SECURITY_MODE); });
    IMSA_HILOGI("register security mode observer, ret: %{public}d", ret);
}

void InputMethodSystemAbility::DataShareCallback(const std::string &key)
{
    if (key != SettingsDataUtils::SECURITY_MODE) {
        return;
    }
    if (serviceHandler_ == nullptr) {
        return;
    }
    auto task = []() {
        auto userId = OsAccountAdapter::GetMainAccountId();
        IMSA_HILOGI("DataShareCallback, %{public}d full experience change.", userId);
        ImeEnabledInfoManager::GetInstance().OnFullExperienceTableChanged(userId);
    };
    serviceHandler_->PostTask(task, "OnFullExperienceTableChanged", 0, AppExecFwk::EventQueue::Priority::IMMEDIATE);
}

void InputMethodSystemAbility::OnCurrentImeStatusChanged(
    int32_t userId, const std::string &bundleName, EnabledStatus newStatus)
{
    IMSA_HILOGI("start.");
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return;
    }
    auto imeData = session->GetRealImeData();
    if (imeData != nullptr && bundleName != imeData->ime.first) {
        IMSA_HILOGD("%{public}d,%{public}s not current ime %{public}s!", userId, bundleName.c_str(),
            imeData->ime.first.c_str());
        return;
    }
    if (newStatus == EnabledStatus::BASIC_MODE) {
        session->OnSecurityChange(static_cast<int32_t>(SecurityMode::BASIC));
    }
    if (newStatus == EnabledStatus::FULL_EXPERIENCE_MODE) {
        session->OnSecurityChange(static_cast<int32_t>(SecurityMode::FULL));
    }
    session->AddRestartIme();
}

int32_t InputMethodSystemAbility::GetSecurityMode(int32_t &security)
{
    IMSA_HILOGD("InputMethodSystemAbility start.");
    auto userId = GetCallingUserId();
    auto bundleName = FullImeInfoManager::GetInstance().Get(userId, IPCSkeleton::GetCallingTokenID());
    if (bundleName.empty()) {
        bundleName = identityChecker_->GetBundleNameByToken(IPCSkeleton::GetCallingTokenID());
        if (!ImeInfoInquirer::GetInstance().IsInputMethod(userId, bundleName)) {
            IMSA_HILOGE("[%{public}d, %{public}s] not an ime.", userId, bundleName.c_str());
            return ErrorCode::ERROR_NOT_IME;
        }
    }
    security = static_cast<int32_t>(SecurityMode::BASIC);
    EnabledStatus status = EnabledStatus::BASIC_MODE;
    auto ret = ImeEnabledInfoManager::GetInstance().GetEnabledState(userId, bundleName, status);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGW("[%{public}d, %{public}s] get enabled status failed:%{public}d,!", userId, bundleName.c_str(), ret);
    }
    if (status == EnabledStatus::FULL_EXPERIENCE_MODE) {
        security = static_cast<int32_t>(SecurityMode::FULL);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::CheckEnableAndSwitchPermission()
{
    if (identityChecker_->IsFormShell(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGD("is form shell!");
        return ErrorCode::NO_ERROR;
    }
    if (!identityChecker_->IsNativeSa(IPCSkeleton::GetCallingFullTokenID()) &&
        !identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not native sa or system app!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    if (!identityChecker_->HasPermission(IPCSkeleton::GetCallingTokenID(),
        std::string(PERMISSION_CONNECT_IME_ABILITY))) {
        IMSA_HILOGE("have not PERMISSION_CONNECT_IME_ABILITY!");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::CheckSwitchPermission(int32_t userId, const SwitchInfo &switchInfo,
    SwitchTrigger trigger)
{
    IMSA_HILOGD("trigger: %{public}d.", static_cast<int32_t>(trigger));
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (trigger == SwitchTrigger::IMSA) {
        return ErrorCode::NO_ERROR;
    }
    if (trigger == SwitchTrigger::NATIVE_SA) {
        return CheckEnableAndSwitchPermission();
    }
    if (trigger == SwitchTrigger::SYSTEM_APP) {
        if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
            IMSA_HILOGE("not system app!");
            return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
        }
        if (!identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
            IMSA_HILOGE("have not PERMISSION_CONNECT_IME_ABILITY!");
            return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
        }
        return ErrorCode::NO_ERROR;
    }
    if (trigger == SwitchTrigger::CURRENT_IME) {
        // PERMISSION_CONNECT_IME_ABILITY check temporarily reserved for application adaptation, will be deleted soon
        if (identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
            return ErrorCode::NO_ERROR;
        }
        IMSA_HILOGE("have not PERMISSION_CONNECT_IME_ABILITY!");
        auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
        std::string currentBundleName;
        if (currentImeCfg != nullptr) {
            currentBundleName = currentImeCfg->bundleName;
        }
        if (identityChecker_->IsBundleNameValid(IPCSkeleton::GetCallingTokenID(), currentBundleName) ||
            IsTmpIme(userId, tokenId)) {
            IMSA_HILOGD("current ime!");
            return ErrorCode::NO_ERROR;
        }
        IMSA_HILOGE("not current ime!");
        /* return ErrorCode::ERROR_STATUS_PERMISSION_DENIED temporarily reserved for application adaptation,
        will be replaced by ERROR_NOT_CURRENT_IME soon */
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    return ErrorCode::ERROR_BAD_PARAMETERS;
}

bool InputMethodSystemAbility::IsStartInputTypePermitted(int32_t userId)
{
    auto defaultIme = ImeInfoInquirer::GetInstance().GetDefaultImeInfo(userId);
    if (defaultIme == nullptr) {
        IMSA_HILOGE("failed to get default ime!");
        return false;
    }
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (identityChecker_->IsBundleNameValid(tokenId, defaultIme->prop.name)) {
        return true;
    }
    if (identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
        return true;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return false;
    }
    auto checkRet = identityChecker_->IsFocused(IPCSkeleton::GetCallingPid(), tokenId, userId);
    return checkRet.first && session->IsBoundToClient(GetCallingDisplayId(userId));
}

int32_t InputMethodSystemAbility::ConnectSystemCmd(const sptr<IRemoteObject> &channel, sptr<IRemoteObject> &agent)
{
    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system app!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    auto userId = GetCallingUserId();
    auto tokenId = IPCSkeleton::GetCallingTokenID();
    if (!identityChecker_->HasPermission(tokenId, std::string(PERMISSION_CONNECT_IME_ABILITY))) {
        IMSA_HILOGE("have not PERMISSION_CONNECT_IME_ABILITY!");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    std::string bundleName = identityChecker_->GetBundleNameByToken(tokenId);
    if (bundleName.empty()) {
        IMSA_HILOGE("failed to get bundle name");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    AppExecFwk::BundleInfo bundleInfo;
    if (!ImeInfoInquirer::GetInstance().GetBundleInfoByBundleName(userId, bundleName, bundleInfo)) {
        IMSA_HILOGE("failed to get bundle info");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    if (bundleInfo.signatureInfo.appIdentifier != ImeInfoInquirer::GetInstance().GetSystemPanelAppIdentifier()) {
        IMSA_HILOGE("appIdentifier mismatch: %{private}s", bundleInfo.signatureInfo.appIdentifier.c_str());
        return ErrorCode::ERROR_SYSTEM_PANEL_ERROR;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_NULL_POINTER;
    }
    return session->OnConnectSystemCmd(channel, agent);
}

void InputMethodSystemAbility::HandleWmsConnected(int32_t userId, int32_t screenId, pid_t pid)
{
    IMSA_HILOGD("in, userId: %{public}d, screenId: %{public}d", userId, screenId);
    if (!OsAccountAdapter::IsOsAccountForeground(userId)) {
        IMSA_HILOGW("userId: %{public}d not foreground", userId);
        return;
    }
    ScbInfo info = WindowMonitorsManager::GetInstance().GetForegroundUser(screenId);
    int32_t currentUserId = info.userId;
    int32_t currentpid = info.pid;
    if ((currentUserId == userId) && (currentpid == pid)) {
        IMSA_HILOGW("currentUserId: %{public}d not pid: %{public}d", currentUserId, pid);
        return;
    }
    bool isScbReboot = currentUserId == userId;
    if (!isScbReboot) {
        WindowMonitorsManager::GetInstance().UpdateForegroundUser(userId, screenId, pid);
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        UserSessionManager::GetInstance().AddUserSession(userId);
    }
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return;
    }
    session->OnScbStarted(isScbReboot);
}
// LCOV_EXCL_START
void InputMethodSystemAbility::StartNewUserIme(int32_t userId)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        UserSessionManager::GetInstance().AddUserSession(userId);
    }
    session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return;
    }
    auto imeData = session->GetRealImeData(true);
    if (imeData == nullptr && session->IsWmsReady()) {
        session->StartCurrentIme();
    }
}
// LCOV_EXCL_STOP
void InputMethodSystemAbility::HandleWmsDisconnected(int32_t userId, int32_t screenId, pid_t pid)
{
    IMSA_HILOGD("in, userId: %{public}d, screenId: %{public}d, pid: %{public}d", userId, screenId, pid);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session != nullptr) {
        session->OnScbStopped();
    }
}
// LCOV_EXCL_START
void InputMethodSystemAbility::HandleWmsStarted()
{
    // singleton, device boot, wms reboot
    IMSA_HILOGI("Wms start.");
    InitAllUsersWindowMonitors();

#ifndef SCENE_BOARD_ENABLE
    auto userSessions = UserSessionManager::GetInstance().GetUserSessions();
    for (auto userSession : userSessions) {
        auto session = userSession.second;
        if (session != nullptr) {
            session->RemoveAllCurrentClient();
        }
    }
    // if scb is not enabled, reset all input methods when wms reboot
    ResetAllImes();
#endif
}

void InputMethodSystemAbility::InitAllUsersWindowMonitors()
{
    if (!SaMgrAdapter::IsSaReady(SUBSYS_ACCOUNT_SYS_ABILITY_ID_BEGIN)) {
        IMSA_HILOGW("account sa not ready yet");
        waitAccountReadyToInit_.store(true);
        return;
    } else {
        waitAccountReadyToInit_.store(false);
    }
    if (!SaMgrAdapter::IsSaReady(WINDOW_MANAGER_SERVICE_ID)) {
        IMSA_HILOGW("window sa not ready yet");
        return;
    }
    auto accounts = OsAccountAdapter::GetForegroundOsAccountIds();
    for (auto const &account : accounts) {
        if (WindowMonitorsManager::GetInstance().IsInited(account)) {
            IMSA_HILOGD("user %{public}d has been inited", account);
            continue;
        }
        if (InitWindowMonitors(account)) {
            WindowMonitorsManager::GetInstance().SetInited(account);
        }
    }
}

bool InputMethodSystemAbility::InitWindowMonitors(int32_t userId)
{
    bool ret = InitFocusChangedMonitor(userId);
    IMSA_HILOGI("user %{public}d InitFocusChangedMonitor isSuccess: %{public}d", userId, ret);
    ret = InitDisplayGroupMonitor(userId);
    IMSA_HILOGI("user %{public}d InitDisplayGroupMonitor isSuccess: %{public}d", userId, ret);
    ret = InitWmsConnectionMonitor(userId);
    IMSA_HILOGI("user %{public}d InitWmsConnectionMonitor isSuccess: %{public}d", userId, ret);
    ret = InitWindowDisplayChangedMonitor(userId);
    IMSA_HILOGI("user %{public}d InitWindowDisplayChangedMonitor isSuccess: %{public}d", userId, ret);
    return ret;
}

void InputMethodSystemAbility::RemoveWindowMonitors(int32_t userId)
{
    IMSA_HILOGI("userId: %{public}d", userId);
    WindowMonitorsManager::GetInstance().Reset();
}

void InputMethodSystemAbility::HandleFocusChanged(bool isFocused, uint64_t displayId, int32_t pid, int32_t uid)
{
    int32_t userId = GetUserId(uid);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("[%{public}d, %{public}d] session is nullptr!", uid, userId);
        return;
    }
    isFocused ? session->OnFocused(displayId, pid, uid) : session->OnUnfocused(displayId, pid, uid);
}

void InputMethodSystemAbility::HandleMemStarted()
{
    // singleton
    IMSA_HILOGI("MemMgr start.");
    Memory::MemMgrClient::GetInstance().NotifyProcessStatus(getpid(), 1, 1, INPUT_METHOD_SYSTEM_ABILITY_ID);
    Memory::MemMgrClient::GetInstance().SetCritical(getpid(), true, INPUT_METHOD_SYSTEM_ABILITY_ID);
    if (ImeInfoInquirer::GetInstance().IsMemoryWatermarkEnabled()) {
        SystemParamAdapter::GetInstance().WatchParam(SystemParamAdapter::MEMORY_WATERMARK_KEY);
    }
    ResetAllImes();
}

void InputMethodSystemAbility::HandleOsAccountStarted()
{
    IMSA_HILOGI("account start");
    OsAccountAdapter::RegisterOsAccountStateListener();
    FullImeInfoManager::GetInstance().Init();
    if (!isAccountSaFirstStart_.load() || waitAccountReadyToInit_.load()) {
        InitAllUsersWindowMonitors();
    }
    isAccountSaFirstStart_.store(false);
    auto accounts = OsAccountAdapter::GetForegroundOsAccounts();
    for (const auto &account : accounts) {
        InitUserInfo(account.localId, account.displayId);
    }
}

void InputMethodSystemAbility::SendMessageToWorkThread(int32_t eventId)
{
    auto msg = new (std::nothrow) Message(eventId, nullptr);
    if (msg == nullptr) {
        IMSA_HILOGE("failed to create message");
        return;
    }
    auto handler = MessageHandler::Instance();
    if (handler == nullptr) {
        IMSA_HILOGE("handler is nullptr");
        delete msg;
        msg = nullptr;
        return;
    }
    handler->SendMessage(msg);
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::GetUserId(int32_t uid)
{
    IMSA_HILOGD("uid:%{public}d", uid);
    auto userId = OsAccountAdapter::GetOsAccountLocalIdFromUid(uid);
    // 0 represents user 0 in the system
    if (userId == 0) {
        IMSA_HILOGI("user 0");
        return OsAccountAdapter::GetMainAccountId();
    }
    return userId;
}

int32_t InputMethodSystemAbility::GetCallingUserId()
{
    int32_t outputUserId;
    auto callerUid = IPCSkeleton::GetCallingUid();
    auto callerUserId = OsAccountAdapter::GetOsAccountLocalIdFromUid(callerUid);
    if (callerUserId == 0) {
        outputUserId = OsAccountAdapter::GetForegroundOsAccountLocalId();
    } else {
        outputUserId = callerUserId;
    }
    IMSA_HILOGD("uid: %{public}d, userId: %{public}d", callerUid, outputUserId);
    return outputUserId;
}

int32_t InputMethodSystemAbility::GetCallingUserId(int32_t &outputUserId, int32_t inputUserId)
{
    IMSA_HILOGD("GetCallingUserId, inputUserId:%{public}d", inputUserId);
    int32_t userId = inputUserId;
    bool isExist = false;
    if (userId != -1) {
        auto errCode = AccountSA::OsAccountManager::IsOsAccountExists(userId, isExist);
        if (errCode != 0) {
            IMSA_HILOGE("IsOsAccountExists failed, errCode:%{public}d", errCode);
            return ErrorCode::ERROR_USER_NOT_EXIST;
        }
        if (!isExist) {
            return ErrorCode::ERROR_USER_NOT_EXIST;
        }
    }
    auto callerUid = IPCSkeleton::GetCallingUid();
    auto callerUserId = OsAccountAdapter::GetOsAccountLocalIdFromUid(callerUid);
    if (callerUserId == 0) {
        if (userId == -1) {
            outputUserId = OsAccountAdapter::GetForegroundOsAccountLocalId();
        } else {
            if (!OsAccountAdapter::IsOsAccountForeground(userId)) {
                IMSA_HILOGE("!OsAccountAdapter::IsOsAccountForeground(userId) failed, userId:%{public}d", userId);
                return ErrorCode::ERROR_USER_NOT_IN_FOREGROUND;
            }
            if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID()) &&
                !identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID()) &&
                !identityChecker_->IsFormShell(IPCSkeleton::GetCallingFullTokenID())) {
                IMSA_HILOGE("no system and no sa, outputUserId:%{public}d", outputUserId);
                return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
            }
            outputUserId = userId;
        }
    } else {
        if (userId != -1 && userId != callerUserId) {
            IMSA_HILOGE("cross user operation denied, caller: %{public}d, userId: %{public}d", callerUserId, userId);
            return ErrorCode::ERROR_CROSS_USER_OPERATION_DENIED;
        }
        outputUserId = callerUserId;
    }
    IMSA_HILOGD("success, outputUserId:%{public}d", outputUserId);
    return ErrorCode::NO_ERROR;
}

// LCOV_EXCL_START
uint64_t InputMethodSystemAbility::GetCallingDisplayId(int32_t userId, sptr<IRemoteObject> abilityToken)
{
    return identityChecker_->GetDisplayIdByPid(IPCSkeleton::GetCallingPid(), userId, abilityToken);
}
// LCOV_EXCL_STOP
bool InputMethodSystemAbility::IsCurrentIme(int32_t userId, uint32_t tokenId)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return false;
    }
    auto bundleName = FullImeInfoManager::GetInstance().Get(userId, tokenId);
    if (bundleName.empty()) {
        IMSA_HILOGW("user:%{public}d tokenId:%{public}d not find.", userId, tokenId);
        bundleName = identityChecker_->GetBundleNameByToken(tokenId);
    }
    auto imeData = session->GetRealImeData();
    return imeData != nullptr && bundleName == imeData->ime.first;
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::StartInputType(int32_t userId, InputType type, bool isPersistence)
{
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    ImeIdentification ime;
    int32_t ret = InputTypeManager::GetInstance().GetImeByInputType(type, ime);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGW("not find input type: %{public}d.", type);
        // add for not adapter for SECURITY_INPUT
        if (type == InputType::SECURITY_INPUT) {
            return session->StartUserSpecifiedIme();
        }
        return ret;
    }
    SwitchInfo switchInfo = { std::chrono::system_clock::now(), ime.bundleName, ime.subName };
    session->GetSwitchQueue().Push(switchInfo);
    IMSA_HILOGI("start input type: %{public}d, isPersistence: %{public}d.", type, isPersistence);
    return (type == InputType::SECURITY_INPUT) ? OnStartInputType(userId, switchInfo, false) :
        OnStartInputType(userId, switchInfo, true, isPersistence);
}

void InputMethodSystemAbility::NeedHideWhenSwitchInputType(int32_t userId, InputType type, bool &needHide)
{
    if (!needHide) {
        return;
    }
    ImeIdentification ime;
    InputTypeManager::GetInstance().GetImeByInputType(type, ime);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("UserId: %{public}d session is nullptr!", userId);
        needHide = false;
        return;
    }
    auto imeData = session->GetRealImeData(true);
    if (imeData == nullptr) {
        IMSA_HILOGI("Readyime is nullptr");
        needHide = false;
        return;
    }
    needHide = imeData->ime.first == ime.bundleName;
}

void InputMethodSystemAbility::HandleBundleScanFinished()
{
    isBundleScanFinished_.store(true);
    HandleImeCfgCapsState(OsAccountAdapter::GetMainAccountId());
}

bool InputMethodSystemAbility::ModifyImeCfgWithWrongCaps(int32_t userId)
{
    bool isCapsEnable = false;
    if (!GetDeviceFunctionKeyState(MMI::KeyEvent::CAPS_LOCK_FUNCTION_KEY, isCapsEnable)) {
        IMSA_HILOGE("Get capslock function key state failed!");
        return false;
    }
    auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentImeCfg == nullptr) {
        IMSA_HILOGE("currentImeCfg is nullptr!");
        return false;
    }
    auto info = ImeInfoInquirer::GetInstance().GetImeInfo(userId, currentImeCfg->bundleName, currentImeCfg->subName);
    if (info == nullptr) {
        IMSA_HILOGE("ime info is nullptr!");
        return false;
    }
    bool imeCfgCapsEnable = info->subProp.mode == "upper";
    if (imeCfgCapsEnable == isCapsEnable) {
        IMSA_HILOGE("current caps state is correct.");
        return true;
    }
    auto condition = isCapsEnable ? Condition::UPPER : Condition::LOWER;
    auto correctIme = ImeInfoInquirer::GetInstance().FindTargetSubtypeByCondition(info->subProps, condition);
    if (correctIme == nullptr) {
        IMSA_HILOGE("correctIme is empty!");
        return false;
    }
    std::string correctImeName = info->prop.name + "/" + info->prop.id;
    ImeEnabledInfoManager::GetInstance().SetCurrentIme(userId, correctImeName, correctIme->id, false);
    IMSA_HILOGD("Adjust imeCfg caps success! current imeName: %{public}s, subName: %{public}s",
        correctImeName.c_str(), correctIme->id.c_str());
    return true;
}

bool InputMethodSystemAbility::GetDeviceFunctionKeyState(int32_t functionKey, bool &isEnable)
{
    auto multiInputMgr = MMI::InputManager::GetInstance();
    if (multiInputMgr == nullptr) {
        IMSA_HILOGE("multiInputMgr is nullptr");
        return false;
    }
    int32_t ret = multiInputMgr->GetFunctionKeyState(functionKey, isEnable);
    IMSA_HILOGD("The function key: %{public}d, isEnable: %{public}d", functionKey, isEnable);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("multiInputMgr get function key state error: %{public}d", ret);
        return false;
    }
    return true;
}

void InputMethodSystemAbility::HandleImeCfgCapsState(int32_t userId)
{
    if (!isBundleScanFinished_.load()) {
        IMSA_HILOGE("Bundle scan is not ready.");
        return;
    }
    if (!SaMgrAdapter::IsSaReady(MULTIMODAL_INPUT_SERVICE_ID)) {
        IMSA_HILOGE("MMI service is not ready.");
        return;
    }
    if (!ModifyImeCfgWithWrongCaps(userId)) {
        IMSA_HILOGE("Check ImeCfg capslock state correct failed!");
    }
}

ErrCode InputMethodSystemAbility::GetInputMethodState(int32_t& status)
{
    auto userId = GetCallingUserId();
    auto bundleName = FullImeInfoManager::GetInstance().Get(userId, IPCSkeleton::GetCallingTokenID());
    if (bundleName.empty()) {
        bundleName = identityChecker_->GetBundleNameByToken(IPCSkeleton::GetCallingTokenID());
        if (!ImeInfoInquirer::GetInstance().IsInputMethod(userId, bundleName)) {
            IMSA_HILOGE("[%{public}d, %{public}s] not an ime.", userId, bundleName.c_str());
            return ErrorCode::ERROR_NOT_IME;
        }
    }
    EnabledStatus tmpStatus = EnabledStatus::DISABLED;
    auto ret = ImeEnabledInfoManager::GetInstance().GetEnabledState(userId, bundleName, tmpStatus);
    if (ret != ErrorCode::NO_ERROR) {
        return ret;
    }
    status = static_cast<int32_t>(tmpStatus);
    return ErrorCode::NO_ERROR;
}

ErrCode InputMethodSystemAbility::GetCursorInfo(int32_t userId, CursorInfoInner &cursorInfo)
{
    if (!identityChecker_->IsSystemApp(IPCSkeleton::GetCallingFullTokenID())) {
        IMSA_HILOGE("not system application!");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }
    int32_t outputUserId;
    int32_t result = GetCallingUserId(outputUserId, userId);
    if (result != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetCallingUserId failed, result:%{public}d", result);
        return result;
    }
    auto session = UserSessionManager::GetInstance().GetUserSession(outputUserId);
    if (session == nullptr) {
        IMSA_HILOGE("%{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    pid_t clientPid = IPCSkeleton::GetCallingPid();
    return session->GetCursorInfo(cursorInfo, clientPid);
}

int32_t InputMethodSystemAbility::SetEDCDefaultInputMethod(const std::string &edcBackupImeName)
{
    IMSA_HILOGI("SetEDCDefaultInputMethod called, backupIme: %{public}s", edcBackupImeName.c_str());

    if (edcBackupImeName.empty()) {
        IMSA_HILOGE("Invalid parameter: edcBackupImeName is empty");
        return ErrorCode::ERROR_PARAMETER_CHECK_FAILED;
    }

    if (identityChecker_ == nullptr) {
        IMSA_HILOGE("identityChecker_ is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }

    if (!identityChecker_->IsNativeSa(IPCSkeleton::GetCallingTokenID())) {
        IMSA_HILOGE("SetEDCDefaultInputMethod: caller is not native SA");
        return ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION;
    }

    int32_t userId = GetCallingUserId();
    if (userId < 0) {
        IMSA_HILOGE("GetCallingUserId failed");
        return ErrorCode::ERROR_IME_NOT_FOUND;
    }

    if (!SetEDCBackupInputMethod(userId, edcBackupImeName)) {
        IMSA_HILOGE("Failed to set EDC backup input method");
        return ErrorCode::ERROR_EX_SERVICE_SPECIFIC;
    }
    IMSA_HILOGI("EDC backup IME saved to database: %{public}s", edcBackupImeName.c_str());

    return HandleEDCInputMethodAutoSwitch(userId, edcBackupImeName);
}

int32_t InputMethodSystemAbility::HandleEDCInputMethodAutoSwitch(int32_t userId,
    const std::string &edcBackupImeName)
{
    // Get default IME dynamically instead of hardcoding
    std::string defaultImeName = ImeInfoInquirer::GetInstance().GetDefaultIme().bundleName;

    auto imeInfo = ImeInfoInquirer::GetInstance().GetImeInfo(userId, edcBackupImeName, "");
    if (imeInfo == nullptr) {
        IMSA_HILOGI("EDC backup IME %{public}s is not installed yet, skip auto-switch", edcBackupImeName.c_str());
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGI("EDC backup IME is installed: %{public}s/%{public}s",
        imeInfo->prop.name.c_str(), imeInfo->prop.id.c_str());

    auto currentIme = ImeInfoInquirer::GetInstance().GetCurrentInputMethod(userId);
    if (currentIme == nullptr || currentIme->name.empty()) {
        IMSA_HILOGE("Failed to get current input method");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    IMSA_HILOGI("Current IME: %{public}s", currentIme->name.c_str());

    if (currentIme->name == defaultImeName) {
        return SwitchToEDCBackupInputMethod(userId, edcBackupImeName, imeInfo);
    }

    IMSA_HILOGI("Current IME is not default IME (current: %{public}s), skip auto-switch", currentIme->name.c_str());
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::SwitchToEDCBackupInputMethod(int32_t userId, const std::string &edcBackupImeName,
    const std::shared_ptr<ImeInfo> &imeInfo)
{
    IMSA_HILOGI("Switching to EDC backup IME: %{public}s", edcBackupImeName.c_str());
    // imeInfo is already validated by caller, just use bundleName
    return SwitchInputMethodInner(userId, edcBackupImeName, "", SwitchTrigger::IMSA);
}

// LCOV_EXCL_STOP
ErrCode InputMethodSystemAbility::ShowCurrentInput(uint64_t displayId, uint32_t type)
{
    auto name = ImfHiSysEventUtil::GetAppName(IPCSkeleton::GetCallingTokenID());
    auto pid = IPCSkeleton::GetCallingPid();
    auto userId = GetCallingUserId();
    auto imeInfo = GetCurrentImeInfoForHiSysEvent(userId);
    auto ret = ShowCurrentInputInner(displayId);
    IMSA_HILOGD("HiSysEvent report start!");
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetPeerName(name)
                        .SetPeerPid(pid)
                        .SetPeerUserId(userId)
                        .SetClientType(static_cast<ClientType>(type))
                        .SetImeName(imeInfo.second)
                        .SetEventCode(
        static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT))
                        .SetErrCode(ret)
                        .Build();
    ImsaHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_SHOW, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", ret);
    return ret;
}

ErrCode InputMethodSystemAbility::ShowCurrentInput(uint32_t type)
{
    auto name = ImfHiSysEventUtil::GetAppName(IPCSkeleton::GetCallingTokenID());
    auto pid = IPCSkeleton::GetCallingPid();
    auto userId = GetCallingUserId();
    auto imeInfo = GetCurrentImeInfoForHiSysEvent(userId);
    auto ret = ShowCurrentInputInner();
    IMSA_HILOGD("HiSysEvent report start!");
    auto evenInfo =
        HiSysOriginalInfo::Builder()
            .SetPeerName(name)
            .SetPeerPid(pid)
            .SetPeerUserId(userId)
            .SetClientType(static_cast<ClientType>(type))
            .SetImeName(imeInfo.second)
            .SetEventCode(static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_CURRENT_INPUT))
            .SetErrCode(ret)
            .Build();
    ImsaHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_SHOW, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", ret);
    return ret;
}

ErrCode InputMethodSystemAbility::ShowInput(
    const sptr<IInputClient> &client, uint32_t windowId, uint32_t type, int32_t requestKeyboardReason)
{
    auto name = ImfHiSysEventUtil::GetAppName(IPCSkeleton::GetCallingTokenID());
    auto pid = IPCSkeleton::GetCallingPid();
    auto userId = GetCallingUserId();
    auto imeInfo = GetCurrentImeInfoForHiSysEvent(userId);
    auto ret = ShowInputInner(client, windowId, requestKeyboardReason);
    IMSA_HILOGD("HiSysEvent report start!");
    auto evenInfo = HiSysOriginalInfo::Builder()
                        .SetPeerName(name)
                        .SetPeerPid(pid)
                        .SetPeerUserId(userId)
                        .SetClientType(static_cast<ClientType>(type))
                        .SetImeName(imeInfo.second)
                        .SetEventCode(static_cast<int32_t>(IInputMethodSystemAbilityIpcCode::COMMAND_SHOW_INPUT))
                        .SetErrCode(ret)
                        .Build();
    ImsaHiSysEventReporter::GetInstance().ReportEvent(ImfEventType::CLIENT_SHOW, *evenInfo);
    IMSA_HILOGD("HiSysEvent report end, errCode: %{public}d", ret);
    return ret;
}

std::pair<int64_t, std::string> InputMethodSystemAbility::GetCurrentImeInfoForHiSysEvent(int32_t userId)
{
    std::pair<int64_t, std::string> imeInfo{ 0, "" };
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        auto currentImeCfg = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
        imeInfo.second = currentImeCfg != nullptr ? currentImeCfg->bundleName : "";
        return imeInfo;
    }
    auto imeData = session->GetRealImeData();
    if (imeData != nullptr) {
        imeInfo.first = imeData->pid;
        imeInfo.second = imeData->ime.first;
    }
    return imeInfo;
}
// LCOV_EXCL_START
int32_t InputMethodSystemAbility::GetScreenLockIme(int32_t userId, std::string &ime)
{
    auto defaultIme = ImeInfoInquirer::GetInstance().GetDefaultImeCfg();
    if (defaultIme != nullptr) {
        ime = defaultIme->imeId;
        IMSA_HILOGD("GetDefaultIme screenlocked");
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGE("GetDefaultIme is failed!");
    auto currentIme = ImeEnabledInfoManager::GetInstance().GetCurrentImeCfg(userId);
    if (currentIme != nullptr) {
        ime = currentIme->imeId;
        IMSA_HILOGD("GetCurrentIme screenlocked");
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGE("GetCurrentIme is failed!");
    if (GetAlternativeIme(userId, ime) != ErrorCode::NO_ERROR) {
        return ErrorCode::ERROR_NOT_IME;
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::GetAlternativeIme(int32_t userId, std::string &ime)
{
    InputMethodStatus status = InputMethodStatus::ENABLE;
    std::vector<Property> props;
    int32_t ret = ImeInfoInquirer::GetInstance().ListInputMethod(userId,
        static_cast<InputMethodStatus>(status), props);
    if (ret == ErrorCode::NO_ERROR && !props.empty()) {
        ime = props[0].name + "/" + props[0].id;
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGE("GetListEnableInputMethodIme is failed!");
    status = InputMethodStatus::DISABLE;
    ret = ImeInfoInquirer::GetInstance().ListInputMethod(userId,
        static_cast<InputMethodStatus>(status), props);
    if (ret != ErrorCode::NO_ERROR || props.empty()) {
        IMSA_HILOGE("GetListDisableInputMethodIme is failed!");
        return ErrorCode::ERROR_NOT_IME;
    }
    ret = EnableIme(userId, props[0].name);
    if (ret == ErrorCode::NO_ERROR) {
        ime = props[0].name + "/" + props[0].id;
        return ErrorCode::NO_ERROR;
    }
    IMSA_HILOGE("GetAlternativeIme is failed!");
    return ErrorCode::ERROR_NOT_IME;
}

ErrCode InputMethodSystemAbility::SendPrivateData(const Value &value)
{
    std::unordered_map<std::string, PrivateDataValue> privateCommand;
    privateCommand = value.valueMap;
    if (privateCommand.empty()) {
        IMSA_HILOGE("PrivateCommand is empty!");
        return ErrorCode::ERROR_PRIVATE_COMMAND_IS_EMPTY;
    }
    if (!identityChecker_->IsSpecialSaUid()) {
        IMSA_HILOGE("Uid failed, not permission!");
        return ErrorCode::ERROR_STATUS_PERMISSION_DENIED;
    }
    auto userId = OsAccountAdapter::GetMainAccountId();
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("UserId: %{public}d session is nullptr!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    if (!session->SpecialScenarioCheck()) {
        IMSA_HILOGE("Special check permission failed!");
        return ErrorCode::ERROR_SCENE_UNSUPPORTED;
    }
    auto ret = session->SpecialSendPrivateData(privateCommand);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("Special send private data failed, ret: %{public}d!", ret);
    }
    return ret;
}

InputType InputMethodSystemAbility::GetSecurityInputType(const InputClientInfo &inputClientInfo)
{
    if (inputClientInfo.config.inputAttribute.GetSecurityFlag()) {
        return InputType::SECURITY_INPUT;
    } else {
        return InputType::NONE;
    }
}
// LCOV_EXCL_STOP
int32_t InputMethodSystemAbility::StartSecurityIme(int32_t &userId, InputClientInfo &inputClientInfo)
{
    InputType type = GetSecurityInputType(inputClientInfo);
    IMSA_HILOGI("InputType:[%{public}d.", type);
    if (!InputTypeManager::GetInstance().IsStarted()) {
        IMSA_HILOGD("SecurityImeFlag, input type is not started, start.");
        // if need to switch ime, no need to hide panel first.
        NeedHideWhenSwitchInputType(userId, type, inputClientInfo.needHide);
        return StartInputType(userId, type);
    }
    if (!inputClientInfo.isNotifyInputStart) {
        IMSA_HILOGD("SecurityImeFlag, same textField, input type is started, not deal.");
        return ErrorCode::NO_ERROR;
    }
    if (!InputTypeManager::GetInstance().IsInputTypeImeStarted(type)) {
        IMSA_HILOGD("SecurityImeFlag, new textField, input type is started, but it is not target, switch.");
        NeedHideWhenSwitchInputType(userId, type, inputClientInfo.needHide);
        return StartInputType(userId, type);
    }
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnSysImeImageCreated(const Message *msg)
{
    IMSA_HILOGD("called");
    if (msg == nullptr || msg->msgContent_ == nullptr) {
        IMSA_HILOGE("Aborted! Message is nullptr!");
        return ErrorCode::ERROR_NULL_POINTER;
    }
    MessageParcel *data = msg->msgContent_;
    int32_t uid = -1;
    if (!ITypesUtil::Unmarshal(*data, uid)) {
        IMSA_HILOGE("Failed to read message parcel!");
        return ErrorCode::ERROR_BAD_PARAMETERS;
    }
    auto userId = GetUserId(uid);
    auto session = UserSessionManager::GetInstance().GetUserSession(userId);
    if (session == nullptr) {
        IMSA_HILOGE("user:%{public}d session not find!", userId);
        return ErrorCode::ERROR_IMSA_USER_SESSION_NOT_FOUND;
    }
    session->OnSysImeImageCreated();
    return ErrorCode::NO_ERROR;
}

int32_t InputMethodSystemAbility::OnMakeSysImeImage()
{
    auto sessions = UserSessionManager::GetInstance().GetUserSessions();
    for (auto const &session : sessions) {
        auto userSession = session.second;
        if (userSession == nullptr) {
            continue;
        }
        userSession->OnMakeSysImeImage();
    }
    return ErrorCode::NO_ERROR;
}

void InputMethodSystemAbility::OnSysMemChanged()
{
    bool isInLowMem = SystemParamAdapter::GetInstance().IsInLowMemWaterMark();
#ifdef IMF_RESTORE_IN_HIGH_CPU_USAGE
    bool isCpuUsageHigh = false;
    if (!isInLowMem) {
        int32_t cpuUsage = GetCpuUsage();
        if (cpuUsage > CPU_USAGE_HIGH_PERCENT) {
            isCpuUsageHigh = true;
            IMSA_HILOGI("lite device current cpu usage %{public}d is high then 70, no need startinput", cpuUsage);
        }
    }
#endif
    auto sessions = UserSessionManager::GetInstance().GetUserSessions();
    for (auto const &session : sessions) {
        auto userSession = session.second;
        if (userSession == nullptr) {
            continue;
        }
        if (isInLowMem) {
            userSession->TryDisconnectIme();
        } else {
            if (!OsAccountAdapter::IsOsAccountForeground(session.first)) {
                continue;
            }
#ifdef IMF_RESTORE_IN_HIGH_CPU_USAGE
            if (isCpuUsageHigh) {
                continue;
            }
#endif
            userSession->TryStartIme();
        }
    }
}

#ifdef IMF_RESTORE_IN_HIGH_CPU_USAGE
int32_t InputMethodSystemAbility::GetCpuUsage()
{
    int32_t cpuUsage = 0;
    auto collector = OHOS::HiviewDFX::UCollectClient::CpuCollector::Create();
    if (collector == nullptr) {
        IMSA_HILOGE("collector is nullptr");
        return cpuUsage;
    }
    auto collectResult = collector->GetSysCpuUsage();
    int32_t retCode = collectResult.retCode;
    IMSA_HILOGI("retCode of collectResult: %{public}d", retCode);
    if (retCode == OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
        cpuUsage = static_cast<int>(collectResult.data * PERCENTAGE_MULTIPLIER);
    }
    return cpuUsage;
}
#endif

bool InputMethodSystemAbility::SetEDCBackupInputMethod(int32_t userId, const std::string &backupIme)
{
    return SettingsDataUtils::GetInstance().SetEDCBackupInputMethod(userId, backupIme);
}

bool InputMethodSystemAbility::GetEDCBackupInputMethod(int32_t userId, std::string &backupIme)
{
    return SettingsDataUtils::GetInstance().GetEDCBackupInputMethod(userId, backupIme);
}

void InputMethodSystemAbility::HandleEDCInputMethodInstall(int32_t userId, const std::string &installedBundleName)
{
    IMSA_HILOGD("HandleEDCInputMethodInstall called, bundleName: %{public}s", installedBundleName.c_str());

    std::string defaultImeName = ImeInfoInquirer::GetInstance().GetDefaultIme().bundleName;
    if (defaultImeName.empty()) {
        IMSA_HILOGE("Failed to get default IME");
        return;
    }

    std::string edcBackupImeName;
    if (!GetEDCBackupInputMethod(userId, edcBackupImeName)) {
        IMSA_HILOGD("EDC backup IME not configured, skip EDC install handling");
        return;
    }

    if (installedBundleName != edcBackupImeName) {
        IMSA_HILOGD("Installed IME is not EDC backup IME, skip handling");
        return;
    }

    auto currentIme = ImeInfoInquirer::GetInstance().GetCurrentInputMethod(userId);
    if (currentIme == nullptr || currentIme->name.empty()) {
        IMSA_HILOGW("Failed to get current input method");
        return;
    }

    if (currentIme->name == defaultImeName) {
        IMSA_HILOGI("Switching from default IME to EDC backup IME on install");
        if (SwitchInputMethodInner(userId, edcBackupImeName, "", SwitchTrigger::IMSA) != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("Failed to switch to EDC backup IME");
        }
    }
}

void InputMethodSystemAbility::HandleEDCInputMethodRemove(int32_t userId, const std::string &removedBundleName)
{
    IMSA_HILOGD("HandleEDCInputMethodRemove called, bundleName: %{public}s", removedBundleName.c_str());

    std::string defaultImeName = ImeInfoInquirer::GetInstance().GetDefaultIme().bundleName;
    if (defaultImeName.empty()) {
        IMSA_HILOGE("Failed to get default IME");
        return;
    }

    std::string edcBackupImeName;
    if (!GetEDCBackupInputMethod(userId, edcBackupImeName)) {
        IMSA_HILOGD("EDC backup IME not configured, skip EDC remove handling");
        return;
    }

    auto currentIme = ImeInfoInquirer::GetInstance().GetCurrentInputMethod(userId);
    if (currentIme == nullptr || currentIme->name.empty()) {
        IMSA_HILOGW("Failed to get current input method");
        return;
    }

    // Case 1: Uninstalled IME is EDC backup IME B, and current IME is also B
    if (removedBundleName == edcBackupImeName && currentIme->name == edcBackupImeName) {
        IMSA_HILOGI("EDC backup IME removed, switching to default IME");
        if (SwitchInputMethodInner(userId, defaultImeName, "", SwitchTrigger::IMSA) != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("Failed to switch to default IME");
        }
        return;
    }

    // Case 2: Uninstalled IME is current IME, and is not B
    if (removedBundleName == currentIme->name && removedBundleName != edcBackupImeName) {
        IMSA_HILOGI("Current IME removed, trying to switch to EDC backup IME first");
        auto info = ImeInfoInquirer::GetInstance().GetImeInfo(userId, edcBackupImeName, "");
        std::string targetIme = (info != nullptr) ? edcBackupImeName : defaultImeName;
        IMSA_HILOGI("Switching to %{public}s", targetIme.c_str());
        if (SwitchInputMethodInner(userId, targetIme, "", SwitchTrigger::IMSA) != ErrorCode::NO_ERROR) {
            IMSA_HILOGE("Failed to switch IME after removal");
        }
    }
}

} // namespace MiscServices
} // namespace OHOS