/*
 * Copyright (c) 2022-2024 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 "privacy_manager_service.h"

#include <cinttypes>
#include <cstring>

#include "access_token.h"
#include "accesstoken_log.h"
#include "active_status_callback_manager.h"
#include "ipc_skeleton.h"
#ifdef COMMON_EVENT_SERVICE_ENABLE
#include "privacy_common_event_subscriber.h"
#endif //COMMON_EVENT_SERVICE_ENABLE
#include "constant_common.h"
#include "constant.h"
#include "ipc_skeleton.h"
#include "permission_record_manager.h"
#ifdef SECURITY_COMPONENT_ENHANCE_ENABLE
#include "privacy_sec_comp_enhance_agent.h"
#endif
#include "screenlock_manager_loader.h"
#include "system_ability_definition.h"
#include "string_ex.h"

namespace OHOS {
namespace Security {
namespace AccessToken {
namespace {
static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {
    LOG_CORE, SECURITY_DOMAIN_PRIVACY, "PrivacyManagerService"
};
}

const bool REGISTER_RESULT =
    SystemAbility::MakeAndRegisterAbility(DelayedSingleton<PrivacyManagerService>::GetInstance().get());

PrivacyManagerService::PrivacyManagerService()
    : SystemAbility(SA_ID_PRIVACY_MANAGER_SERVICE, true), state_(ServiceRunningState::STATE_NOT_START)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "PrivacyManagerService()");
}

PrivacyManagerService::~PrivacyManagerService()
{
    ACCESSTOKEN_LOG_INFO(LABEL, "~PrivacyManagerService()");
#ifdef COMMON_EVENT_SERVICE_ENABLE
    PrivacyCommonEventSubscriber::UnRegisterEvent();
#endif //COMMON_EVENT_SERVICE_ENABLE
}

void PrivacyManagerService::OnStart()
{
    if (state_ == ServiceRunningState::STATE_RUNNING) {
        ACCESSTOKEN_LOG_INFO(LABEL, "PrivacyManagerService has already started!");
        return;
    }
    ACCESSTOKEN_LOG_INFO(LABEL, "PrivacyManagerService is starting");
    if (!Initialize()) {
        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to initialize");
        return;
    }

    AddSystemAbilityListener(COMMON_EVENT_SERVICE_ID);
    AddSystemAbilityListener(SCREENLOCK_SERVICE_ID);

    state_ = ServiceRunningState::STATE_RUNNING;
    bool ret = Publish(DelayedSingleton<PrivacyManagerService>::GetInstance().get());
    if (!ret) {
        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to publish service!");
        return;
    }
    ACCESSTOKEN_LOG_INFO(LABEL, "Congratulations, PrivacyManagerService start successfully!");
}

void PrivacyManagerService::OnStop()
{
    ACCESSTOKEN_LOG_INFO(LABEL, "Stop service");
    state_ = ServiceRunningState::STATE_NOT_START;
}

int32_t PrivacyManagerService::AddPermissionUsedRecord(const AddPermParamInfoParcel& infoParcel,
    bool asyncMode)
{
    ACCESSTOKEN_LOG_DEBUG(LABEL, "id: %{public}d, perm: %{public}s, succCnt: %{public}d,"
        " failCnt: %{public}d, type: %{public}d", infoParcel.info.tokenId, infoParcel.info.permissionName.c_str(),
        infoParcel.info.successCount, infoParcel.info.failCount, infoParcel.info.type);
    AddPermParamInfo info = infoParcel.info;
    return PermissionRecordManager::GetInstance().AddPermissionUsedRecord(info);
}

int32_t PrivacyManagerService::StartUsingPermission(
    AccessTokenID tokenId, int32_t pid, const std::string& permissionName)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}u, pid: %{public}d, perm: %{public}s",
        tokenId, pid, permissionName.c_str());
    return PermissionRecordManager::GetInstance().StartUsingPermission(tokenId, pid, permissionName);
}

int32_t PrivacyManagerService::StartUsingPermission(
    AccessTokenID tokenId, int32_t pid, const std::string& permissionName, const sptr<IRemoteObject>& callback)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}u, pid: %{public}d, perm: %{public}s",
        tokenId, pid, permissionName.c_str());
    return PermissionRecordManager::GetInstance().StartUsingPermission(tokenId, pid, permissionName, callback);
}

int32_t PrivacyManagerService::StopUsingPermission(
    AccessTokenID tokenId, int32_t pid, const std::string& permissionName)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}u, pid: %{public}d, perm: %{public}s",
        tokenId, pid, permissionName.c_str());
    return PermissionRecordManager::GetInstance().StopUsingPermission(tokenId, pid, permissionName);
}

int32_t PrivacyManagerService::RemovePermissionUsedRecords(AccessTokenID tokenId, const std::string& deviceID)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}u", tokenId);
    PermissionRecordManager::GetInstance().RemovePermissionUsedRecords(tokenId, deviceID);
    return Constant::SUCCESS;
}

int32_t PrivacyManagerService::GetPermissionUsedRecords(
    const PermissionUsedRequestParcel& request, PermissionUsedResultParcel& result)
{
    std::string permissionList;
    for (const auto& perm : request.request.permissionList) {
        permissionList.append(perm);
        permissionList.append(" ");
    }
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}d, timestamp: [%{public}" PRId64 "-%{public}" PRId64
        "], flag: %{public}d, perm: %{public}s", request.request.tokenId, request.request.beginTimeMillis,
        request.request.endTimeMillis, request.request.flag, permissionList.c_str());

    PermissionUsedResult permissionRecord;
    int32_t ret =  PermissionRecordManager::GetInstance().GetPermissionUsedRecords(request.request, permissionRecord);
    result.result = permissionRecord;
    return ret;
}

int32_t PrivacyManagerService::GetPermissionUsedRecords(
    const PermissionUsedRequestParcel& request, const sptr<OnPermissionUsedRecordCallback>& callback)
{
    ACCESSTOKEN_LOG_DEBUG(LABEL, "id: %{public}d", request.request.tokenId);
    return PermissionRecordManager::GetInstance().GetPermissionUsedRecordsAsync(request.request, callback);
}

int32_t PrivacyManagerService::RegisterPermActiveStatusCallback(
    std::vector<std::string>& permList, const sptr<IRemoteObject>& callback)
{
    return PermissionRecordManager::GetInstance().RegisterPermActiveStatusCallback(
        IPCSkeleton::GetCallingTokenID(), permList, callback);
}

#ifdef SECURITY_COMPONENT_ENHANCE_ENABLE
int32_t PrivacyManagerService::RegisterSecCompEnhance(const SecCompEnhanceDataParcel& enhanceParcel)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "Pid: %{public}d", enhanceParcel.enhanceData.pid);
    return PrivacySecCompEnhanceAgent::GetInstance().RegisterSecCompEnhance(enhanceParcel.enhanceData);
}

int32_t PrivacyManagerService::UpdateSecCompEnhance(int32_t pid, uint32_t seqNum)
{
    return PrivacySecCompEnhanceAgent::GetInstance().UpdateSecCompEnhance(pid, seqNum);
}

int32_t PrivacyManagerService::GetSecCompEnhance(int32_t pid, SecCompEnhanceDataParcel& enhanceParcel)
{
    SecCompEnhanceData enhanceData;
    int32_t res = PrivacySecCompEnhanceAgent::GetInstance().GetSecCompEnhance(pid, enhanceData);
    if (res != RET_SUCCESS) {
        ACCESSTOKEN_LOG_WARN(LABEL, "Pid: %{public}d get enhance failed ", pid);
        return res;
    }

    enhanceParcel.enhanceData = enhanceData;
    return RET_SUCCESS;
}

int32_t PrivacyManagerService::GetSpecialSecCompEnhance(const std::string& bundleName,
    std::vector<SecCompEnhanceDataParcel>& enhanceParcelList)
{
    std::vector<SecCompEnhanceData> enhanceList;
    PrivacySecCompEnhanceAgent::GetInstance().GetSpecialSecCompEnhance(bundleName, enhanceList);
    for (const auto& enhance : enhanceList) {
        SecCompEnhanceDataParcel parcel;
        parcel.enhanceData = enhance;
        enhanceParcelList.emplace_back(parcel);
    }
    return RET_SUCCESS;
}
#endif

int32_t PrivacyManagerService::ResponseDumpCommand(int32_t fd, const std::vector<std::u16string>& args)
{
    if (args.size() < 2) { // 2 :need two args 0:command 1:tokenId
        return ERR_INVALID_VALUE;
    }
    long long tokenId = atoll(static_cast<const char*>(Str16ToStr8(args.at(1)).c_str()));
    PermissionUsedRequest request;
    if (tokenId <= 0) {
        return ERR_INVALID_VALUE;
    }
    request.tokenId = static_cast<uint32_t>(tokenId);
    request.flag = FLAG_PERMISSION_USAGE_SUMMARY;
    PermissionUsedResult result;
    if (PermissionRecordManager::GetInstance().GetPermissionUsedRecords(request, result) != 0) {
        return ERR_INVALID_VALUE;
    }
    std::string infos;
    if (result.bundleRecords.empty() || result.bundleRecords[0].permissionRecords.empty()) {
        dprintf(fd, "No Record \n");
        return ERR_OK;
    }
    for (size_t index = 0; index < result.bundleRecords[0].permissionRecords.size(); index++) {
        infos.append(R"(  "permissionRecord": [)");
        infos.append("\n");
        infos.append(R"(    "bundleName": )" + result.bundleRecords[0].bundleName + ",\n");
        infos.append(R"(    "isRemote": )" + std::to_string(result.bundleRecords[0].isRemote) + ",\n");
        infos.append(R"(    "permissionName": ")" + result.bundleRecords[0].permissionRecords[index].permissionName +
                    R"(")" + ",\n");
        time_t lastAccessTime = static_cast<time_t>(result.bundleRecords[0].permissionRecords[index].lastAccessTime);
        infos.append(R"(    "lastAccessTime": )" + std::to_string(lastAccessTime) + ",\n");
        infos.append(R"(    "lastAccessDuration": )" +
                    std::to_string(result.bundleRecords[0].permissionRecords[index].lastAccessDuration) + ",\n");
        infos.append(R"(    "accessCount": ")" +
                    std::to_string(result.bundleRecords[0].permissionRecords[index].accessCount) + R"(")" + ",\n");
        infos.append("  ]");
        infos.append("\n");
    }
    dprintf(fd, "%s\n", infos.c_str());
    return ERR_OK;
}

int32_t PrivacyManagerService::Dump(int32_t fd, const std::vector<std::u16string>& args)
{
    if (fd < 0) {
        ACCESSTOKEN_LOG_ERROR(LABEL, "Dump fd invalid value");
        return ERR_INVALID_VALUE;
    }
    int32_t ret = ERR_OK;
    dprintf(fd, "Privacy Dump:\n");
    if (args.size() == 0) {
        dprintf(fd, "please use hidumper -s said -a '-h' command help\n");
        return ret;
    }
    std::string arg0 = Str16ToStr8(args.at(0));
    if (arg0.compare("-h") == 0) {
        dprintf(fd, "Usage:\n");
        dprintf(fd, "       -h: command help\n");
        dprintf(fd, "       -t <TOKEN_ID>: according to specific token id dump permission used records\n");
    } else if (arg0.compare("-t") == 0) {
        ret = PrivacyManagerService::ResponseDumpCommand(fd, args);
    } else {
        dprintf(fd, "please use hidumper -s said -a '-h' command help\n");
        ret = ERR_INVALID_VALUE;
    }
    return ret;
}

int32_t PrivacyManagerService::UnRegisterPermActiveStatusCallback(const sptr<IRemoteObject>& callback)
{
    return PermissionRecordManager::GetInstance().UnRegisterPermActiveStatusCallback(callback);
}

bool PrivacyManagerService::IsAllowedUsingPermission(AccessTokenID tokenId, const std::string& permissionName)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}d, perm: %{public}s", tokenId, permissionName.c_str());
    return PermissionRecordManager::GetInstance().IsAllowedUsingPermission(tokenId, permissionName);
}

int32_t PrivacyManagerService::SetMutePolicy(uint32_t policyType, uint32_t callerType, bool isMute)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "PolicyType: %{public}d, callerType: %{public}d, isMute: %{public}d",
        policyType, callerType, isMute);
    return PermissionRecordManager::GetInstance().SetMutePolicy(
        static_cast<PolicyType>(policyType), static_cast<CallerType>(callerType), isMute);
}

int32_t PrivacyManagerService::SetHapWithFGReminder(uint32_t tokenId, bool isAllowed)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "id: %{public}d, isAllowed: %{public}d", tokenId, isAllowed);
    return PermissionRecordManager::GetInstance().SetHapWithFGReminder(tokenId, isAllowed);
}

int32_t PrivacyManagerService::GetPermissionUsedTypeInfos(const AccessTokenID tokenId,
    const std::string& permissionName, std::vector<PermissionUsedTypeInfoParcel>& resultsParcel)
{
    ACCESSTOKEN_LOG_DEBUG(LABEL, "id: %{public}d, perm: %{public}s", tokenId, permissionName.c_str());

    std::vector<PermissionUsedTypeInfo> results;
    int32_t res = PermissionRecordManager::GetInstance().GetPermissionUsedTypeInfos(tokenId, permissionName, results);
    if (res != RET_SUCCESS) {
        return res;
    }

    for (const auto& result : results) {
        PermissionUsedTypeInfoParcel parcel;
        parcel.info = result;
        resultsParcel.emplace_back(parcel);
    }

    return RET_SUCCESS;
}

void PrivacyManagerService::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId)
{
    ACCESSTOKEN_LOG_INFO(LABEL, "saId is %{public}d", systemAbilityId);
#ifdef COMMON_EVENT_SERVICE_ENABLE
    if (systemAbilityId == COMMON_EVENT_SERVICE_ID) {
        PrivacyCommonEventSubscriber::RegisterEvent();
        return;
    }
#endif //COMMON_EVENT_SERVICE_ENABLE

    if (systemAbilityId == SCREENLOCK_SERVICE_ID) {
        LibraryLoader loader(SCREENLOCK_MANAGER_LIBPATH);
        ScreenLockManagerAccessLoaderInterface* screenlockManagerLoader =
            loader.GetObject<ScreenLockManagerAccessLoaderInterface>();
        if (screenlockManagerLoader != nullptr) {
            int32_t lockScreenStatus = LockScreenStatusChangeType::PERM_ACTIVE_IN_UNLOCKED;
            if (screenlockManagerLoader->IsScreenLocked()) {
                lockScreenStatus = LockScreenStatusChangeType::PERM_ACTIVE_IN_LOCKED;
            }

            PermissionRecordManager::GetInstance().SetLockScreenStatus(lockScreenStatus);
        }
        return;
    }
}

bool PrivacyManagerService::Initialize()
{
    PermissionRecordManager::GetInstance().Init();
#ifdef EVENTHANDLER_ENABLE
    eventRunner_ = AppExecFwk::EventRunner::Create(true, AppExecFwk::ThreadMode::FFRT);
    if (!eventRunner_) {
        ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to create eventRunner.");
        return false;
    }
    eventHandler_ = std::make_shared<AccessEventHandler>(eventRunner_);
    ActiveStatusCallbackManager::GetInstance().InitEventHandler(eventHandler_);
#endif
    return true;
}
} // namespace AccessToken
} // namespace Security
} // namespace OHOS