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

#include <cinttypes>

#include "ability_manager_client.h"
#include "accesstoken_kit.h"
#include "app_mgr_adapter.h"
#include "display_adapter.h"
#include "global.h"
#include "ime_info_inquirer.h"
#include "ipc_skeleton.h"
#include "tokenid_kit.h"
#include "window_adapter.h"
namespace OHOS {
namespace MiscServices {
using namespace Rosen;
using namespace Security::AccessToken;
using namespace OHOS::AAFwk;

std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocused(int64_t callingPid, uint32_t callingTokenId,
    int32_t userId, uint32_t windowId, const sptr<IRemoteObject> &abilityToken)
{
#ifdef SCENE_BOARD_ENABLE
    std::vector<FocusChangeInfo> focusWindowInfos;
    WindowAdapter::GetAllFocusWindowInfos(focusWindowInfos, userId);
    if (focusWindowInfos.empty()) {
        IMSA_HILOGF("focus window infos is empty!");
    }
    auto retInfo = IsFocusedUIAbility(callingPid, windowId, focusWindowInfos, userId);
    if (retInfo.first) {
        IMSA_HILOGD("%{public}" PRId64 "/%{public}d is focused uiAbility!", callingPid, windowId);
        return retInfo;
    }
    if (ImeInfoInquirer::GetInstance().IsInputMethodExtension(callingPid)) {
        return retInfo;
    }
    retInfo = IsFocusedUIExtension(callingTokenId, abilityToken, focusWindowInfos, userId);
    if (!retInfo.first) {
        IMSA_HILOGE("%{public}" PRId64 "/%{public}d/%{public}d is not focused!", callingPid, windowId, callingTokenId);
    }
    return retInfo;
#else
    return IsFocusedScbNotEnable(callingPid, callingTokenId, windowId, abilityToken, userId);
#endif
}
// LCOV_EXCL_START
bool IdentityCheckerImpl::IsFocusedUIExtension(uint32_t callingTokenId, int32_t userId)
{
    std::vector<FocusChangeInfo> focusWindowInfos;
    WindowAdapter::GetAllFocusWindowInfos(focusWindowInfos, userId);
    auto checkRet = IsFocusedUIExtension(callingTokenId, nullptr, focusWindowInfos, userId);
    return checkRet.first;
}
// LCOV_EXCL_STOP
std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIAbility(
    int64_t callingPid, uint32_t windowId, const std::vector<FocusChangeInfo> &focusWindowInfos, int32_t userId)
{
    std::pair<bool, FocusedInfo> retInfo{ false, {} };
    if (windowId != ImfCommonConst::INVALID_WINDOW_ID) {
        auto displayId = WindowAdapter::GetDisplayIdByWindowId(windowId, userId);
        retInfo = IsFocusedUIAbility(callingPid, displayId, focusWindowInfos, userId);
        if (retInfo.first) {
            return retInfo;
        }
    }
    return IsFocusedUIAbility(callingPid, focusWindowInfos);
}

std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIAbility(int64_t callingPid, uint64_t displayId,
    const std::vector<FocusChangeInfo> &focusWindowInfos, int32_t 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);
        return { false, {} };
    }
    auto iter = std::find_if(
        focusWindowInfos.begin(), focusWindowInfos.end(), [callingPid, displayGroupId](const auto focusWindowInfo) {
            return focusWindowInfo.pid_ == callingPid && focusWindowInfo.displayGroupId_ == displayGroupId;
        });
    if (iter == focusWindowInfos.end()) {
        return { false, {} };
    }
    std::pair<bool, FocusedInfo> rectInfo = GenerateFocusCheckRet(*iter, focusWindowInfos);
    if (rectInfo.second.displayId == 0 && ImeInfoInquirer::GetInstance().IsSupperFold()) {
        auto callingDisplayId = WindowAdapter::GetDisplayIdByWindowId(rectInfo.second.windowId, userId);
        rectInfo.second.displayId = callingDisplayId;
        rectInfo.second.keyboardDisplayId = callingDisplayId;
        IMSA_HILOGD("IsSupperFold, displayId:%{public}" PRIu64 "", callingDisplayId);
    }
    return rectInfo;
}
// LCOV_EXCL_START
std::pair<bool, FocusedInfo> IdentityCheckerImpl::GenerateFocusCheckRet(
    const FocusChangeInfo &focusWindowInfo, const std::vector<Rosen::FocusChangeInfo> &focusWindowInfos)
{
    FocusedInfo focusedInfo;
    focusedInfo.displayId = focusWindowInfo.realDisplayId_;
    focusedInfo.windowId = static_cast<uint32_t>(focusWindowInfo.windowId_);
    focusedInfo.displayGroupId = focusWindowInfo.displayGroupId_;
    focusedInfo.keyboardDisplayId = focusWindowInfo.realDisplayId_;
    focusedInfo.keyboardWindowId = static_cast<uint32_t>(focusWindowInfo.windowId_);
    focusedInfo.keyboardDisplayGroupId = focusWindowInfo.displayGroupId_;
    auto [isRestrictedMainDisplay, bundleName] =
        DisplayAdapter::GetRestrictedMainDisplayInfo(focusWindowInfo.realDisplayId_);
    if (!isRestrictedMainDisplay) {
        return { true, focusedInfo };
    }
    IMSA_HILOGI("restricted main display, bundleName:%{public}s!", bundleName.c_str());
    auto iter = std::find_if(focusWindowInfos.begin(), focusWindowInfos.end(),
        [displayId = ImfCommonConst::DEFAULT_DISPLAY_ID, displayGroupId = ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID](
            const auto focusWindowInfo) {
            return focusWindowInfo.realDisplayId_ == displayId && focusWindowInfo.displayGroupId_ == displayGroupId;
        });
    if (iter == focusWindowInfos.end()) {
        IMSA_HILOGE("main display has no focus!");
        return { false, {} };
    }
    focusedInfo.keyboardDisplayId = ImfCommonConst::DEFAULT_DISPLAY_ID;
    focusedInfo.keyboardDisplayGroupId = ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID;
    focusedInfo.keyboardWindowId = static_cast<uint32_t>(iter->windowId_);
    if (bundleName.empty()) {
        IMSA_HILOGW("bundleName is empty!");
        return { true, focusedInfo };
    }
    return { true, focusedInfo };
}
// LCOV_EXCL_STOP
std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIAbility(
    int64_t callingPid, const std::vector<FocusChangeInfo> &focusWindowInfos)
{
    auto iter = std::find_if(focusWindowInfos.begin(), focusWindowInfos.end(),
        [callingPid](const auto focusWindowInfo) { return focusWindowInfo.pid_ == callingPid; });
    if (iter == focusWindowInfos.end()) {
        return { false, {} };
    }
    return GenerateFocusCheckRet(*iter, focusWindowInfos);
}
// LCOV_EXCL_START
bool IdentityCheckerImpl::IsSystemApp(uint64_t fullTokenId)
{
    return TokenIdKit::IsSystemAppByFullTokenID(fullTokenId);
}

bool IdentityCheckerImpl::IsBundleNameValid(uint32_t tokenId, const std::string &validBundleName)
{
    std::string bundleName = GetBundleNameByToken(tokenId);
    if (bundleName.empty()) {
        return false;
    }
    if (bundleName != validBundleName) {
        IMSA_HILOGE("bundleName is invalid, caller: %{public}s, current: %{public}s", bundleName.c_str(),
            validBundleName.c_str());
        return false;
    }
    IMSA_HILOGD("checked successfully.");
    return true;
}
// LCOV_EXCL_STOP
bool IdentityCheckerImpl::HasPermission(uint32_t tokenId, const std::string &permission)
{
    if (AccessTokenKit::VerifyAccessToken(tokenId, permission) != PERMISSION_GRANTED) {
        IMSA_HILOGE("Permission [%{public}s] not granted!", permission.c_str());
        return false;
    }
    IMSA_HILOGD("verify AccessToken success.");
    return true;
}

std::pair<bool, FocusedInfo> IdentityCheckerImpl::CheckBroker(AccessTokenID tokenId, int32_t userId)
{
    if (!IsBrokerInner(tokenId)) {
        return { false, {} };
    }
    FocusChangeInfo focusInfo;
    WindowAdapter::GetFocusInfo(focusInfo, userId);
    return GenerateFocusCheckRet(focusInfo, { focusInfo });
}

bool IdentityCheckerImpl::IsBroker(AccessTokenID tokenId)
{
    return IsBrokerInner(tokenId);
}

bool IdentityCheckerImpl::IsBrokerInner(AccessTokenID tokenId)
{
    if (!IsNativeSa(tokenId)) {
        return false;
    }
    NativeTokenInfo nativeTokenInfoRes;
    AccessTokenKit::GetNativeTokenInfo(tokenId, nativeTokenInfoRes);
    return nativeTokenInfoRes.processName == "broker";
}

bool IdentityCheckerImpl::IsNativeSa(AccessTokenID tokenId)
{
    return AccessTokenKit::GetTokenTypeFlag(tokenId) == TypeATokenTypeEnum::TOKEN_NATIVE;
}
// LCOV_EXCL_START
bool IdentityCheckerImpl::IsFormShell(AccessTokenID tokenId)
{
    return AccessTokenKit::GetTokenTypeFlag(tokenId) == TypeATokenTypeEnum::TOKEN_SHELL;
}
// LCOV_EXCL_STOP
uint32_t IdentityCheckerImpl::GetUIExtensionWindowId(sptr<IRemoteObject> abilityToken)
{
    if (abilityToken == nullptr) {
        IMSA_HILOGD("abilityToken is nullptr!");
        return INVALID_WINDOW_ID;
    }
    AAFwk::UIExtensionSessionInfo info;
    auto ret = AAFwk::AbilityManagerClient::GetInstance()->GetUIExtensionSessionInfo(abilityToken, info);
    if (ret != ERR_OK) {
        IMSA_HILOGD("failed to GetUIExtensionSessionInfo, ret: %{public}d!", ret);
        return INVALID_WINDOW_ID;
    }
    return info.hostWindowId;
}

std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIExtension(uint32_t callingTokenId,
    const sptr<IRemoteObject> &abilityToken, const std::vector<FocusChangeInfo> &focusWindowInfos, int32_t userId)
{
    uint32_t windowId = GetUIExtensionWindowId(abilityToken);
    if (windowId != ImfCommonConst::INVALID_WINDOW_ID) {
        auto displayIdByWindow = WindowAdapter::GetDisplayIdByWindowId(windowId, userId);
        IMSA_HILOGD("windowId is: %{public}d, displayId is %{public}" PRIu64 "", windowId, displayIdByWindow);
        if (displayIdByWindow != DEFAULT_DISPLAY_ID) {
            return IsFocusedUIExtension(windowId, displayIdByWindow, focusWindowInfos, userId);
        }
    }

    std::pair<bool, FocusedInfo> retInfo{ false, {} };
    constexpr size_t MAX_FOCUS_UI_EXTENSION_CHECK_LOOP = 3;
    auto focusSize = focusWindowInfos.size();
    for (size_t i = 0; i < focusSize && i < MAX_FOCUS_UI_EXTENSION_CHECK_LOOP; i++) {
        retInfo = IsFocusedUIExtension(focusWindowInfos[i].realDisplayId_, callingTokenId, focusWindowInfos, userId);
        if (retInfo.first) {
            IMSA_HILOGD(
                "loop %{public}zu, focus ui extension in %{public}" PRIu64 ".", i, focusWindowInfos[i].realDisplayId_);
            return retInfo;
        }
    }
    return retInfo;
}
// LCOV_EXCL_START
std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIExtension(
    uint32_t windowId, uint64_t displayId, const std::vector<FocusChangeInfo> &focusWindowInfos, int32_t 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);
        return { false, {} };
    }
    auto iter = std::find_if(
        focusWindowInfos.begin(), focusWindowInfos.end(), [displayGroupId, windowId](const auto focusWindowInfo) {
            return focusWindowInfo.displayGroupId_ == displayGroupId
                   && windowId == static_cast<uint32_t>(focusWindowInfo.windowId_);
        });
    if (iter == focusWindowInfos.end()) {
        return { false, {} };
    }
    auto retInfo = GenerateFocusCheckRet(*iter, focusWindowInfos);
    if (retInfo.second.displayId == 0 && ImeInfoInquirer::GetInstance().IsSupperFold()) {
        auto callingDisplayId = WindowAdapter::GetDisplayIdByWindowId(retInfo.second.windowId, userId);
        retInfo.second.displayId = callingDisplayId;
        retInfo.second.keyboardDisplayId = callingDisplayId;
        IMSA_HILOGD("IsSupperFold, displayId:%{public}" PRIu64 "", callingDisplayId);
    }
    retInfo.second.uiExtensionHostPid = iter->pid_;
    return retInfo;
}
// LCOV_EXCL_STOP
std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedUIExtension(
    uint64_t displayId, uint32_t callingTokenId, const std::vector<FocusChangeInfo> &focusWindowInfos, int32_t userId)
{
    std::pair<bool, FocusedInfo> retInfo{ false, {} };
    auto client = AbilityManagerClient::GetInstance();
    if (client == nullptr) {
        IMSA_HILOGE("AbilityManagerClient is nullptr!");
        return retInfo;
    }
    bool isFocused = false;
    auto ret = client->CheckUIExtensionIsFocused(callingTokenId, isFocused, displayId);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to CheckUIExtensionIsFocused, ret: %{public}d!", ret);
        return retInfo;
    }
    IMSA_HILOGD("tokenId: %{public}d, isFocused: %{public}d", callingTokenId, isFocused);
    if (!isFocused) {
        return retInfo;
    }
    uint64_t displayGroupId = ImfCommonConst::DEFAULT_DISPLAY_GROUP_ID;
    ret = WindowAdapter::GetInstance().GetDisplayGroupIdWithRetry(displayId, userId, displayGroupId);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("GetDisplayGroupIdWithRetry failed, ret: %{public}d", ret);
        return retInfo;
    }
    auto iter = std::find_if(focusWindowInfos.begin(), focusWindowInfos.end(),
        [displayGroupId](const auto &focusWindowInfo) { return focusWindowInfo.displayGroupId_ == displayGroupId; });
    if (iter == focusWindowInfos.end()) {
        return retInfo;
    }
    retInfo = GenerateFocusCheckRet(*iter, focusWindowInfos);
    retInfo.second.uiExtensionHostPid = iter->pid_;
    return retInfo;
}

std::string IdentityCheckerImpl::GetBundleNameByToken(uint32_t tokenId)
{
    auto tokenType = AccessTokenKit::GetTokenTypeFlag(tokenId);
    if (tokenType != TOKEN_HAP) {
        IMSA_HILOGE("invalid token!");
        return "";
    }
    HapTokenInfo info;
    int ret = AccessTokenKit::GetHapTokenInfo(tokenId, info);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to get hap info, ret: %{public}d!", ret);
        return "";
    }
    return info.bundleName;
}

uint64_t IdentityCheckerImpl::GetDisplayIdByWindowId(int32_t callingWindowId, int32_t userId)
{
    return WindowAdapter::GetDisplayIdByWindowId(callingWindowId, userId);
}
// LCOV_EXCL_START
uint64_t IdentityCheckerImpl::GetDisplayIdByPid(int64_t callingPid, int32_t userId, sptr<IRemoteObject> abilityToken)
{
    uint64_t displayId = 0;
    bool ret = WindowAdapter::GetDisplayId(callingPid, displayId, userId);
    if (ret || abilityToken == nullptr) {
        return displayId;
    }
    AAFwk::UIExtensionSessionInfo info;
    AAFwk::AbilityManagerClient::GetInstance()->GetUIExtensionSessionInfo(abilityToken, info);
    auto windowId = info.hostWindowId;
    displayId = WindowAdapter::GetDisplayIdByWindowId(windowId, userId);
    IMSA_HILOGD("GetDisplayIdByPid displayId: %{public}" PRIu64 "", displayId);
    return displayId;
}

bool IdentityCheckerImpl::IsValidVirtualIme(int32_t callingUid)
{
    return ImeInfoInquirer::GetInstance().IsProxyIme(callingUid);
}

bool IdentityCheckerImpl::IsSpecialSaUid()
{
    auto callingUid = IPCSkeleton::GetCallingUid();
    return ImeInfoInquirer::GetInstance().IsSpecialSaUid(callingUid);
}

bool IdentityCheckerImpl::IsUIExtension(int64_t pid)
{
    return ImeInfoInquirer::GetInstance().IsUIExtension(pid);
}

std::pair<bool, FocusedInfo> IdentityCheckerImpl::IsFocusedScbNotEnable(int64_t callingPid, uint32_t callingTokenId,
    uint32_t windowId, const sptr<IRemoteObject> &abilityToken, int32_t userId)
{
    std::pair<bool, FocusedInfo> retInfo{ false, {} };
    retInfo.second.windowId = windowId;
    uint64_t displayId;
    if (abilityToken != nullptr) {
        displayId = WindowAdapter::GetDisplayIdByToken(abilityToken, userId);
        IMSA_HILOGD("abilityToken not nullptr, displayId: %{public}" PRIu64 "", displayId);
    } else {
        displayId = WindowAdapter::GetDisplayIdByPid(callingPid, userId);
    }
    FocusChangeInfo focusInfo;
    WindowAdapter::GetFocusInfo(focusInfo, displayId);
    auto focusedPid = focusInfo.pid_;
    if (callingPid == focusedPid) {
        IMSA_HILOGD("focused app, pid: %{public}" PRId64 ", display: %{public}" PRIu64 ",windowId: %{public}d",
            callingPid, displayId, focusInfo.windowId_);
        retInfo.first = true;
        return retInfo;
    }
    if (ImeInfoInquirer::GetInstance().IsInputMethodExtension(callingPid)) {
        return retInfo;
    }
    bool isFocused = IsFocusedUIExtension(callingTokenId, abilityToken, userId);
    if (!isFocused) {
        IMSA_HILOGE("not focused, focusedPid: %{public}d, callerPid: %{public}" PRId64 ", callerToken: "
                    "%{public}d",
            focusedPid, callingPid, callingTokenId);
        return retInfo;
    }
    retInfo.first = true;
    return retInfo;
}

bool IdentityCheckerImpl::IsFocusedUIExtension(
    uint32_t callingTokenId, sptr<IRemoteObject> abilityToken, int32_t userId)
{
    uint32_t windowId = GetUIExtensionWindowId(abilityToken);
    if (windowId != INVALID_WINDOW_ID) {
        auto displayIdByWindow = WindowAdapter::GetDisplayIdByWindowId(windowId, userId);
        IMSA_HILOGD("windowId is: %{public}d, displayId is %{public}" PRIu64 "", windowId, displayIdByWindow);
        if (displayIdByWindow != DEFAULT_DISPLAY_ID) {
            FocusChangeInfo focusInfo;
            WindowAdapter::GetFocusInfo(focusInfo, userId, displayIdByWindow);
            return windowId == static_cast<uint32_t>(focusInfo.windowId_);
        }
    }

    bool isFocused = false;
    auto client = AbilityManagerClient::GetInstance();
    if (client == nullptr) {
        IMSA_HILOGE("AbilityManagerClient is nullptr!");
        return false;
    }
    auto ret = client->CheckUIExtensionIsFocused(callingTokenId, isFocused);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to CheckUIExtensionIsFocused, ret: %{public}d!", ret);
        return false;
    }
    IMSA_HILOGD("tokenId: %{public}d, isFocused: %{public}d", callingTokenId, isFocused);
    return isFocused;
}
// LCOV_EXCL_STOP
} // namespace MiscServices
} // namespace OHOS