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

#include "napi_errors.h"
#include "napi_utils.h"
#include "power_common.h"
#include "power_log.h"
#include "power_mgr_client.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <uv.h>
#include "app_manager_utils.h"
#ifdef POWER_API_METRICS_ENABLE
#include "histogram_plugin_macros.h"
#endif

#define SET_REBOOT _IOW(BOOT_DETECTOR_IOCTL_BASE, 109, int)

namespace OHOS {
namespace PowerMgr {
namespace {
constexpr uint32_t REBOOT_SHUTDOWN_MAX_ARGC = 1;
constexpr uint32_t WAKEUP_MAX_ARGC = 1;
constexpr uint32_t SET_MODE_CALLBACK_MAX_ARGC = 2;
constexpr uint32_t SET_MODE_PROMISE_MAX_ARGC = 1;
constexpr uint32_t SUSPEND_MAX_ARGC = 1;
constexpr uint32_t SET_SCREEN_OFFTIME_ARGC = 1;
constexpr uint32_t HIBERNATE_ARGC = 1;
constexpr uint32_t REFRESH_ACTIVITY_ARGC = 1;
constexpr uint32_t POWERRKEY_FILTERING_STRATEGY_ARGC = 1;
constexpr uint32_t SHUTDOWN_CALLBACK_ARGC = 1;
constexpr int32_t INDEX_0 = 0;
constexpr int32_t INDEX_1 = 1;
constexpr int32_t RESTORE_DEFAULT_SCREENOFF_TIME = -1;
constexpr int32_t SHUTDOWN_CALLBACK_TIMEOUT = 5000;
static PowerMgrClient& g_powerMgrClient = PowerMgrClient::GetInstance();
thread_local sptr<PowerShutdownCallback> g_powerShutdownCallback = new (std::nothrow) PowerShutdownCallback();
#ifdef POWER_API_METRICS_ENABLE
constexpr int32_t HISTOGRAM_API_CALL_COUNT = 1;
#endif
} // namespace

PowerShutdownCallback::~PowerShutdownCallback()
{
    ReleaseCallback();
}

void PowerShutdownCallback::ReleaseCallback()
{
    std::lock_guard lock(callbackMutex_);
    if (callbackRef_ != nullptr) {
        napi_delete_reference(env_, callbackRef_);
    }
    callbackRef_ = nullptr;
    env_ = nullptr;
}

void PowerShutdownCallback::CreateCallback(napi_env env, napi_value jsCallback)
{
    std::lock_guard lock(callbackMutex_);
    if (napi_ok != napi_create_reference(env, jsCallback, SHUTDOWN_CALLBACK_ARGC, &callbackRef_)) {
        POWER_HILOGW(FEATURE_SHUTDOWN, "Failed to create a JS callback reference");
        callbackRef_ = nullptr;
    }
    env_ = env;
}

void PowerShutdownCallback::OnAsyncShutdownOrReboot(bool isReboot)
{
    isReboot_ = isReboot;
    RETURN_IF(env_ == nullptr);
    uv_work_t* work = new (std::nothrow) uv_work_t;
    RETURN_IF(work == nullptr);
    work->data = reinterpret_cast<void*>(this);
    std::shared_ptr<SyncContext> syncContext = std::make_shared<SyncContext>();
    auto uvcallback = [work, syncContext]() mutable {
        PowerShutdownCallback* callback = reinterpret_cast<PowerShutdownCallback*>(work->data);
        if (callback != nullptr) {
            callback->OnShutdownOrReboot();
        }
        delete work;
        work = nullptr;
        syncContext->NotifyOne();
    };
    if (napi_send_event(env_, uvcallback, napi_eprio_low, __func__) != napi_status::napi_ok) {
        delete work;
        work = nullptr;
        POWER_HILOGW(FEATURE_SHUTDOWN, "uv_queue_work is failed");
        return;
    }
    if (syncContext->WaitFor(SHUTDOWN_CALLBACK_TIMEOUT)) {
        POWER_HILOGI(FEATURE_SHUTDOWN, "OnShutdownOrReboot excuted end");
    } else {
        POWER_HILOGE(FEATURE_SHUTDOWN, "OnShutdownOrReboot excuted timeout");
    }
}

void PowerShutdownCallback::OnShutdownOrReboot()
{
    POWER_HILOGI(FEATURE_SHUTDOWN, "OnShutdownOrReboot, isReboot: %{public}d", static_cast<int32_t>(isReboot_));
    std::lock_guard lock(callbackMutex_);
    RETURN_IF_WITH_LOG(callbackRef_ == nullptr || env_ == nullptr, "js callback ref or env is nullptr");
    
    napi_handle_scope scope = nullptr;
    napi_open_handle_scope(env_, &scope);
    if (scope == nullptr) {
        POWER_HILOGW(FEATURE_SHUTDOWN, "scope is nullptr");
        return;
    }

    napi_value isRebootValue = nullptr;
    if (napi_ok != napi_get_boolean(env_, isReboot_, &isRebootValue)) {
        POWER_HILOGW(FEATURE_SHUTDOWN, "napi_get_boolean callback failed");
        napi_close_handle_scope(env_, scope);
        return;
    }

    napi_value callback = nullptr;
    napi_status status = napi_get_reference_value(env_, callbackRef_, &callback);
    if (status != napi_ok) {
        POWER_HILOGW(FEATURE_SHUTDOWN, "napi_get_reference_value callback failed. status = %{public}d", status);
        napi_close_handle_scope(env_, scope);
        return;
    }

    napi_value callResult = nullptr;
    status = napi_call_function(env_, nullptr, callback, SHUTDOWN_CALLBACK_ARGC, &isRebootValue, &callResult);
    if (status != napi_ok) {
        POWER_HILOGW(FEATURE_SHUTDOWN, "napi_call_function callback failed, status = %{public}d", status);
    }
    napi_close_handle_scope(env_, scope);
}

napi_value PowerNapi::Shutdown(napi_env env, napi_callback_info info)
{
    return RebootOrShutdown(env, info, false);
}

napi_value PowerNapi::Reboot(napi_env env, napi_callback_info info)
{
    return RebootOrShutdown(env, info, true);
}

napi_value PowerNapi::IsActive(napi_env env, napi_callback_info info)
{
    bool isScreen = g_powerMgrClient.IsScreenOn();
    napi_value napiValue;
    NAPI_CALL(env, napi_get_boolean(env, isScreen, &napiValue));
    return napiValue;
}

napi_value PowerNapi::Wakeup(napi_env env, napi_callback_info info)
{
    size_t argc = WAKEUP_MAX_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != WAKEUP_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_string)) {
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
    POWER_HILOGD(FEATURE_WAKEUP, "Wakeup type: APPLICATION, reason: %{public}s", detail.c_str());
    int32_t apiVersion = AppManagerUtils::GetApiTargetVersion();
    PowerErrors code = g_powerMgrClient.WakeupDevice(
        WakeupDeviceType::WAKEUP_DEVICE_APPLICATION, detail, std::to_string(apiVersion));
    if (code != PowerErrors::ERR_OK) {
        error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::Suspend(napi_env env, napi_callback_info info)
{
    size_t argc = SUSPEND_MAX_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != SUSPEND_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_boolean)) {
        if (!NapiUtils::CheckValueType(env, argv[INDEX_0], napi_undefined)) {
            std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
            if (!detail.empty()) {
                return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
            }
        }
    }

    bool isForce = false;
    napi_get_value_bool(env, argv[0], &isForce);

    PowerErrors code;
    int32_t apiVersion = AppManagerUtils::GetApiTargetVersion();
    if (isForce) {
        code = g_powerMgrClient.ForceSuspendDevice(std::to_string(apiVersion));
    } else {
        code = g_powerMgrClient.SuspendDevice(
            SuspendDeviceType::SUSPEND_DEVICE_REASON_APPLICATION, false, std::to_string(apiVersion));
    }
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_WAKEUP, "Suspend Device fail, isForce:%{public}d", isForce);
        return error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::Hibernate(napi_env env, napi_callback_info info)
{
    size_t argc = HIBERNATE_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != HIBERNATE_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_boolean)) {
        if (!NapiUtils::CheckValueType(env, argv[INDEX_0], napi_undefined)) {
            std::string detail = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
            if (!detail.empty()) {
                return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
            }
        }
    }

    bool clearMemory = false;
    napi_get_value_bool(env, argv[0], &clearMemory);

    int32_t apiVersion = AppManagerUtils::GetApiTargetVersion();
    PowerErrors code = g_powerMgrClient.Hibernate(clearMemory, "", std::to_string(apiVersion));
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_WAKEUP, "Hibernate failed.");
        error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::SetPowerMode(napi_env env, napi_callback_info info)
{
    size_t argc = SET_MODE_CALLBACK_MAX_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != SET_MODE_CALLBACK_MAX_ARGC && argc != SET_MODE_PROMISE_MAX_ARGC) {
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::unique_ptr<AsyncCallbackInfo> asyncInfo = std::make_unique<AsyncCallbackInfo>();
    RETURN_IF_WITH_RET(asyncInfo == nullptr, nullptr);
    // callback
    if (argc == SET_MODE_CALLBACK_MAX_ARGC) {
        POWER_HILOGD(FEATURE_POWER_MODE, "Call setPowerMode callback");
        return SetPowerModeCallback(env, argv, asyncInfo);
    }

    // promise
    POWER_HILOGD(FEATURE_POWER_MODE, "Call setPowerMode promise");
    return SetPowerModePromise(env, argv, asyncInfo);
}

napi_value PowerNapi::GetPowerMode(napi_env env, napi_callback_info info)
{
    PowerMode mode = g_powerMgrClient.GetDeviceMode();
    napi_value napiValue;
    NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(mode), &napiValue));
    return napiValue;
}

napi_value PowerNapi::GetPowerConfig(napi_env env, napi_callback_info info)
{
    constexpr size_t maxArgc = 1;
    size_t argc = maxArgc;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != maxArgc || !NapiUtils::CheckValueType(env, argv[0], napi_string)) {
        POWER_HILOGW(COMP_FWK, "GetPowerConfig invalid param");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::string sceneName = NapiUtils::GetStringFromNapi(env, argv[0]);
    std::string configVal;
    PowerErrors code = g_powerMgrClient.GetPowerConfig(sceneName, configVal);
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(COMP_FWK, "GetPowerConfig failed, code: %{public}d", static_cast<int32_t>(code));
        return error.ThrowError(env, code);
    }

    napi_value napiValue;
    NAPI_CALL(env, napi_create_string_utf8(env, configVal.c_str(), NAPI_AUTO_LENGTH, &napiValue));
    return napiValue;
}

napi_value PowerNapi::SetPowerConfig(napi_env env, napi_callback_info info)
{
    constexpr size_t maxArgc = 2;
    size_t argc = maxArgc;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != maxArgc || !NapiUtils::CheckValueType(env, argv[0], napi_string) ||
        !NapiUtils::CheckValueType(env, argv[1], napi_string)) {
        POWER_HILOGW(COMP_FWK, "SetPowerConfig invalid param");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::string sceneName = NapiUtils::GetStringFromNapi(env, argv[0]);
    std::string configVal = NapiUtils::GetStringFromNapi(env, argv[1]);
    PowerErrors code = g_powerMgrClient.SetPowerConfig(sceneName, configVal);
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(COMP_FWK, "SetPowerConfig failed, code: %{public}d", static_cast<int32_t>(code));
        return error.ThrowError(env, code);
    }
    return nullptr;
}

static void SetFrameworkBootStage(bool isReboot)
{
    int fd = open("/dev/bbox", O_WRONLY);
    if (fd < 0) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "open /dev/bbox failed!");
        return;
    }

    fdsan_exchange_owner_tag(fd, 0, DOMAIN_FEATURE_SHUTDOWN);
    POWER_HILOGI(FEATURE_SHUTDOWN, "Set shutdown fw start timeout.");

    int rebootFlag = isReboot ? 1 : 0;
    int ret = ioctl(fd, SET_REBOOT, &rebootFlag);
    if (ret < 0) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "set reboot flag failed!");
        fdsan_close_with_tag(fd, DOMAIN_FEATURE_SHUTDOWN);
        return;
    }

    int stage = SHUT_STAGE_FRAMEWORK_START;
    ret = ioctl(fd, SET_SHUT_STAGE, &stage);
    if (ret < 0) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "set shut stage failed!");
    }

    POWER_HILOGI(FEATURE_SHUTDOWN, "Set shutdown timeout mechanism started.");
    fdsan_close_with_tag(fd, DOMAIN_FEATURE_SHUTDOWN);

    return;
}

napi_value PowerNapi::RebootOrShutdown(napi_env env, napi_callback_info info, bool isReboot)
{
    size_t argc = REBOOT_SHUTDOWN_MAX_ARGC;
    napi_value argv[argc];
    SetFrameworkBootStage(isReboot);
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != REBOOT_SHUTDOWN_MAX_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_string)) {
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::string reason = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
    POWER_HILOGD(FEATURE_SHUTDOWN, "reboot: %{public}d, reason: %{public}s", isReboot, reason.c_str());

    PowerErrors code;
    if (isReboot) {
        code = g_powerMgrClient.RebootDevice(reason);
    } else {
        code = g_powerMgrClient.ShutDownDevice(reason);
    }
    if (code != PowerErrors::ERR_OK && code != PowerErrors::ERR_SKIP_FUNCTION_CALL) {
        error.ThrowError(env, code);
    }

    return nullptr;
}

napi_value PowerNapi::SetPowerModeCallback(
    napi_env& env, napi_value argv[], std::unique_ptr<AsyncCallbackInfo>& asyncInfo)
{
    bool isNum = NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number);
    bool isFunc = NapiUtils::CheckValueType(env, argv[INDEX_1], napi_function);
    if (!isNum || !isFunc) {
        POWER_HILOGW(FEATURE_POWER_MODE, "isNum: %{public}d, isFunc: %{public}d", isNum, isFunc);
        return asyncInfo->GetError().ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    asyncInfo->GetData().SetMode(env, argv[INDEX_0]);
    asyncInfo->CreateCallback(env, argv[INDEX_1]);

    AsyncWork(
        env, asyncInfo, "SetPowerModeCallback",
        [](napi_env env, void* data) {
            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
            RETURN_IF(asyncInfo == nullptr);
            PowerErrors error = g_powerMgrClient.SetDeviceMode(asyncInfo->GetData().GetMode());
            asyncInfo->GetError().Error(error);
        },
        [](napi_env env, napi_status status, void* data) {
            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
            RETURN_IF(asyncInfo == nullptr);
            asyncInfo->CallFunction(env, nullptr);
            asyncInfo->Release(env);
            delete asyncInfo;
        });
    return nullptr;
}

napi_value PowerNapi::SetPowerModePromise(
    napi_env& env, napi_value argv[], std::unique_ptr<AsyncCallbackInfo>& asyncInfo)
{
    bool isNum = NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number);
    if (!isNum) {
        POWER_HILOGW(FEATURE_POWER_MODE, "isNum: %{public}d", isNum);
        return asyncInfo->GetError().ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }
    napi_value promise;
    asyncInfo->CreatePromise(env, promise);
    RETURN_IF_WITH_RET(promise == nullptr, nullptr);
    asyncInfo->GetData().SetMode(env, argv[INDEX_0]);

    AsyncWork(
        env, asyncInfo, "SetPowerModePromise",
        [](napi_env env, void* data) {
            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
            RETURN_IF(asyncInfo == nullptr);
            PowerErrors error = g_powerMgrClient.SetDeviceMode(asyncInfo->GetData().GetMode());
            asyncInfo->GetError().Error(error);
        },
        [](napi_env env, napi_status status, void* data) {
            AsyncCallbackInfo* asyncInfo = reinterpret_cast<AsyncCallbackInfo*>(data);
            RETURN_IF(asyncInfo == nullptr);
            if (asyncInfo->GetError().IsError()) {
                napi_reject_deferred(env, asyncInfo->GetDeferred(), asyncInfo->GetError().GetNapiError(env));
            } else {
                napi_value undefined;
                napi_get_undefined(env, &undefined);
                napi_resolve_deferred(env, asyncInfo->GetDeferred(), undefined);
            }
            asyncInfo->Release(env);
            delete asyncInfo;
        });
    return promise;
}

void PowerNapi::AsyncWork(napi_env& env, std::unique_ptr<AsyncCallbackInfo>& asyncInfo, const std::string& resourceName,
    napi_async_execute_callback execute, napi_async_complete_callback complete)
{
    napi_value resource = nullptr;
    napi_create_string_utf8(env, resourceName.c_str(), NAPI_AUTO_LENGTH, &resource);
    napi_create_async_work(env, nullptr, resource, execute, complete,
        reinterpret_cast<void*>(asyncInfo.get()), &(asyncInfo->GetAsyncWork()));
    NAPI_CALL_RETURN_VOID(env, napi_queue_async_work_with_qos(env, asyncInfo->GetAsyncWork(), napi_qos_utility));
    asyncInfo.release();
}

napi_value PowerNapi::SetScreenOffTime(napi_env env, napi_callback_info info)
{
    size_t argc = SET_SCREEN_OFFTIME_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != SET_SCREEN_OFFTIME_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number)) {
        POWER_HILOGE(FEATURE_WAKEUP, "check value type failed.");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    int64_t timeout;
    if (napi_ok != napi_get_value_int64(env, argv[INDEX_0], &timeout)) {
        POWER_HILOGE(FEATURE_WAKEUP, "napi get int64 value failed.");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    if (timeout == 0 || (timeout < 0 && timeout != RESTORE_DEFAULT_SCREENOFF_TIME)) {
        POWER_HILOGE(FEATURE_WAKEUP, "timeout is not right.");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    PowerErrors code;
    int32_t apiVersion = AppManagerUtils::GetApiTargetVersion();
    if (timeout == RESTORE_DEFAULT_SCREENOFF_TIME) {
        code = g_powerMgrClient.RestoreScreenOffTime(std::to_string(apiVersion));
    } else {
        code = g_powerMgrClient.OverrideScreenOffTime(timeout, std::to_string(apiVersion));
    }
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_WAKEUP, "SetScreenOffTime failed.");
        return error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::IsStandby(napi_env env, napi_callback_info info)
{
#ifdef POWER_API_METRICS_ENABLE
    HISTOGRAM_BOOLEAN("BasicServicesKit.PowerManager.isStandby.Boolean", HISTOGRAM_API_CALL_COUNT);
#endif

    bool isStandby = false;
    PowerErrors code = g_powerMgrClient.IsStandby(isStandby);
    if (code == PowerErrors::ERR_OK) {
        napi_value napiValue;
        NAPI_CALL(env, napi_get_boolean(env, isStandby, &napiValue));
        return napiValue;
    }
    NapiErrors error;
    return error.ThrowError(env, code);
}

napi_value PowerNapi::RefreshActivity(napi_env env, napi_callback_info info)
{
    size_t argc = REFRESH_ACTIVITY_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != REFRESH_ACTIVITY_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_string)) {
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    std::string reason = NapiUtils::GetStringFromNapi(env, argv[INDEX_0]);
    PowerErrors code = g_powerMgrClient.RefreshActivity(UserActivityType::USER_ACTIVITY_TYPE_APPLICATION, reason);
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_ACTIVITY, "RefreshActivity failed. code:%{public}d", static_cast<int32_t>(code));
        return error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::SetPowerKeyFilteringStrategy(napi_env env, napi_callback_info info)
{
    size_t argc = POWERRKEY_FILTERING_STRATEGY_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors error;
    if (argc != POWERRKEY_FILTERING_STRATEGY_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_number)) {
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    int32_t strategy = 0;
    if (napi_ok != napi_get_value_int32(env, argv[INDEX_0], &strategy)) {
        POWER_HILOGE(FEATURE_INPUT, "napi get int32 value failed.");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    if (strategy < 0 || strategy >= static_cast<int32_t>(PowerKeyFilteringStrategy::STRATEGY_MAX)) {
        POWER_HILOGE(FEATURE_INPUT, "strategy is not right.");
        return error.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    PowerErrors code = g_powerMgrClient.SetPowerKeyFilteringStrategy(static_cast<PowerKeyFilteringStrategy>(strategy));
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_INPUT, "SetPowerKeyFilteringStrategy failed. code:%{public}d", static_cast<int32_t>(code));
        return error.ThrowError(env, code);
    }
    return nullptr;
}

napi_value PowerNapi::RegisterShutdownCallback(napi_env env, napi_callback_info info)
{
#ifdef POWER_API_METRICS_ENABLE
    HISTOGRAM_BOOLEAN("BasicServicesKit.PowerManager.registerShutdownCallback.Boolean", HISTOGRAM_API_CALL_COUNT);
#endif
    size_t argc = SHUTDOWN_CALLBACK_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    NapiErrors errors;
    if (argc != SHUTDOWN_CALLBACK_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_function)) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "RegisterShutdownCallback ERR_PARAM_INVALID");
        return errors.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    napi_value result;
    napi_get_undefined(env, &result);

    if (g_powerShutdownCallback == nullptr) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "g_powerShutdownCallback null");
        return nullptr;
    }
    g_powerShutdownCallback->ReleaseCallback();
    g_powerShutdownCallback->CreateCallback(env, argv[INDEX_0]);
    PowerErrors code = g_powerMgrClient.RegisterAsyncShutdownCallback(g_powerShutdownCallback,
        ShutdownPriority::DEFAULT);
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "RegisterShutdownCallback failed. code:%{public}d", static_cast<int32_t>(code));
        return errors.ThrowError(env, code);
    }

    return result;
}

napi_value PowerNapi::UnRegisterShutdownCallback(napi_env env, napi_callback_info info)
{
#ifdef POWER_API_METRICS_ENABLE
    HISTOGRAM_BOOLEAN("BasicServicesKit.PowerManager.unregisterShutdownCallback.Boolean", HISTOGRAM_API_CALL_COUNT);
#endif

    size_t argc = SHUTDOWN_CALLBACK_ARGC;
    napi_value argv[argc];
    NapiUtils::GetCallbackInfo(env, info, argc, argv);

    if (g_powerShutdownCallback == nullptr) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "g_powerShutdownCallback null");
        return nullptr;
    }
    NapiErrors errors;
    g_powerShutdownCallback->ReleaseCallback();
    PowerErrors code = g_powerMgrClient.UnRegisterAsyncShutdownCallback(g_powerShutdownCallback);
    if (code != PowerErrors::ERR_OK) {
        POWER_HILOGE(FEATURE_SHUTDOWN,
            "UnRegisterShutdownCallback failed. code:%{public}d", static_cast<int32_t>(code));
        return errors.ThrowError(env, code);
    }

    RETURN_IF_WITH_RET(argc == INDEX_0, nullptr);
    if (argc == SHUTDOWN_CALLBACK_ARGC && NapiUtils::CheckValueType(env, argv[INDEX_0], napi_undefined)) {
        POWER_HILOGI(FEATURE_SHUTDOWN, "UnRegisterShutdownCallback end");
        return nullptr;
    }
    if (argc != SHUTDOWN_CALLBACK_ARGC || !NapiUtils::CheckValueType(env, argv[INDEX_0], napi_function)) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "UnRegisterShutdownCallback ERR_PARAM_INVALID");
        return errors.ThrowError(env, PowerErrors::ERR_PARAM_INVALID);
    }

    napi_value handler = nullptr;
    napi_ref handlerRef = nullptr;
    napi_create_reference(env, argv[INDEX_0], SHUTDOWN_CALLBACK_ARGC, &handlerRef);
    napi_get_reference_value(env, handlerRef, &handler);
    napi_delete_reference(env, handlerRef);

    napi_value result = nullptr;
    if (handler == nullptr) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "Handler is nullptr");
        return result;
    }

    napi_get_undefined(env, &result);
    napi_status status = napi_call_function(env, nullptr, handler, INDEX_0, nullptr, &result);
    if (status != napi_ok) {
        POWER_HILOGE(FEATURE_SHUTDOWN, "status=%{public}d", status);
        return result;
    }
    return result;
}
} // namespace PowerMgr
} // namespace OHOS